Re: [Haskell-cafe] Yet Another Forkable Class
Hi. On 23 August 2013 13:29, Nicolas Trangez wrote: > Did anyone ever consider using type-level literals (strings) to 'name' > effects (or transformer layers when using monad stacks)? > Edwin Brady had this in his effects library in Idris. http://www.idris-lang.org/documentation/effects/ Ozgur. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Yet Another Forkable Class
On Fri, 2013-08-23 at 08:06 +, o...@okmij.org wrote: > > It will > > arbitrarily pick the first match in the former and fail to compile > in > > the latter case. > Of course we can have duplicate layers. In that case, the dynamically > closest > handler wins -- which sounds about right (think of reset in delimited > control). Did anyone ever consider using type-level literals (strings) to 'name' effects (or transformer layers when using monad stacks)? A stupid example (OTOH) could be updateStats :: (Member (State "min" Int) r, Member (State "max" Int) r) => Int -> Eff r () updateStats i = do min <- askMin max <- askMax when (i < min) $ putMin i when (i > max) $ putMax i where askMin :: Member (State "min" Int) r => Eff r Int askMin = ask putMax :: Member (State "max" Int) r => Int -> Eff r () putMax = put -- askMax, putMin accordingly Using constraint synonyms/ConstraintKinds (e.g. type StateMax r = Member (State "max" Int) r) might reduce some notation overhead. Just a thought. Nicolas ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Yet Another Forkable Class
I must stress that OpenUnion1.hs described (briefly) in the paper is only one implementation of open unions, out of many possible. For example, I have two more implementations. A year-old version of the code implemented open unions *WITHOUT* overlapping instances or Typeable. http://okmij.org/ftp/Haskell/extensible/TList.hs The implementation in the paper is essentially the one described in the full HList paper, Appendix C. The one difference is that the HList version precluded duplicate summands. Adding the duplication check to OpenUnion1 takes three lines of code. I didn't add them because it didn't seem necessary, or even desired. I should further stress, OverlappingInstances are enabled only within one module, OpenUnion1.hs. The latter is an internal, closed module, not meant to be modified by a user. No user program needs to declare OverlappingInstances in its LANGUAGES pragma. Second, OverlappingInstances are used only within the closed type class Member. This type class is not intended to be user-extensible; the programmer need not and should not define any more instances for it. The type class is meant to be closed. So Member emulates closed type families implemented in the recent version of GHC. With the closed type families, no overlapping instances are needed. > Simply the fact that the Member class needs -XOverlappingInstances > means that we cannot have duplicate or polymorphic effects. It will > arbitrarily pick the first match in the former and fail to compile in > the latter case. Of course we can have duplicate layers. In that case, the dynamically closest handler wins -- which sounds about right (think of reset in delimited control). The file Eff.hs even has a test case for that, tdup. BTW, I'm not sure of the word 'pick' -- the Member class is a purely compile-time constraint. It doesn't do any picking -- it doesn't do anything at all at run-time. > For example we should be able to project the open sum equivalent of > Either String String into the second String but we cannot with the > implementation in the paper. You inject a String or a String, and you will certainly project a String (the one your have injected). What is the problem then? You can always project what you have injected. Member merely keeps track of what types could possibly be injected/projected. So, String + String indeed should be String. By polymorphic effects you must mean first-class polymorphism (because the already implemented Reader effect is polymorphic in the environment). First of all, there are workarounds. Second, I'm not sure what would be a good example of polymorphic effect (aside from ST-monad-like). > To be honest I'm not so sure about these "effects"... Haskell Symposium will have a panel on effect libraries in Haskell. It seems plausible that effects, one way or the other, will end ip in Haskell. Come to Haskell Symposium, tell us your doubts and concerns. We want to hear them. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Yet Another Forkable Class
For the open union used in extensible effects, apart from using the Typeable mechanism, is there a more protected way to implement the open sum type? I managed to modified the Member class given in the paper, but ended up having to use the vague OverlappingInstance. That's not quite what I hope. I'm not even sure whether the instance `Member t (t :> r)` is more specific than `Member t (t' :> r)`. -- suhorng {-# LANGUAGE KindSignatures, TypeOperators, GADTs, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, OverlappingInstances #-} -- FlexibleContexts is for Show instance of Union import Data.Functor import Control.Applicative -- for several functor instances -- open union infixr 2 :> data (a :: * -> *) :> b data Union r v where Elsewhere :: Functor t' => Union r v -> Union (t' :> r) v Here :: Functor t => t v -> Union (t :> r) v class Member t r where inj :: Functor t => t v -> Union r v prj :: Functor t => Union r v -> Maybe (t v) instance Member t (t :> r) where inj tv = Here tv prj (Here tv) = Just tv prj (Elsewhere _) = Nothing -- Note: overlapped by letting t' = t instance (Functor t', Member t r) => Member t (t' :> r) where inj tv = Elsewhere (inj tv) prj (Here _) = Nothing prj (Elsewhere u) = prj u decomp :: Functor t => Union (t :> r) v -> Either (Union r v) (t v) decomp (Here tv) = Right tv decomp (Elsewhere u) = Left u -- Auxiliary definitions for tests data Void newtype Func a = Func a instance Show (Union Void a) where show _ = undefined instance (Show (t v), Show (Union r v)) => Show (Union (t :> r) v) where show (Here tv) = "Here " ++ show tv show (Elsewhere u) = "Elsewhere " ++ show u instance Functor Func where fmap f (Func x) = Func (f x) instance Show a => Show (Func a) where show (Func a) = show a type Stk = Maybe :> Either Char :> Func :> Void type Stk' = Either Char :> Func :> Void -- used in `deTrue`, `deFalse` unTrue :: Union Stk Bool unTrue = inj (Func True) unFalse :: Union Stk Bool unFalse = inj (Just False) -- `Func` is repeated un5 :: Union (Maybe :> Func :> Either Char :> Func :> Void) Int un5 = inj (Func 5) maybe2 :: Maybe (Func Int) maybe2 = prj un5 maybeTrue :: Maybe (Func Bool) maybeTrue = prj unTrue maybeFalse1 :: Maybe (Func Bool) maybeFalse1 = prj unFalse maybeFalse2 :: Maybe (Maybe Bool) maybeFalse2 = prj unFalse deTrue :: Either (Union Stk' Bool) (Maybe Bool) deTrue = decomp unTrue deFalse :: Either (Union Stk' Bool) (Maybe Bool) deFalse = decomp unFalse 2013/8/22 Alberto G. Corona > The paper is very interesting: > > http://www.cs.indiana.edu/~sabry/papers/exteff.pdf > > It seems that the approach is mature enough and it is better in every way > than monad transformers, while at the same time the syntax may become > almost identical to MTL for many uses. > > I only expect to see the library in Hackage with all the blessings, and > with all the instances of the MTL classes in order to make the transition > form monad transformers to ExtEff as transparent as possible > > > 2013/8/22 > > >> Perhaps effect libraries (there are several to choose from) could be a >> better answer to Fork effects than monad transformers. One lesson from >> the recent research in effects is that we should start thinking what >> effect we want to achieve rather than which monad transformer to >> use. Using ReaderT or StateT or something else is an implementation >> detail. Once we know what effect to achieve we can write a handler, or >> interpreter, to implement the desired operation on the World, obeying >> the desired equations. And we are done. >> >> For example, with ExtEff library with which I'm more familiar, the >> Fork effect would take as an argument a computation that cannot throw >> any requests. That means that the parent has to provide interpreters >> for all child effects. It becomes trivially to implement: >> >> > Another example would be a child that should not be able to throw >> errors as >> > opposed to the parent thread. >> It is possible to specify which errors will be allowed for the child >> thread (the ones that the parent will be willing to reflect and >> interpret). The rest of errors will be statically prohibited then. >> >> > instance (Protocol p) => Forkable (WebSockets p) (ReaderT (Sink p) IO) >> where >> > fork (ReaderT f) = liftIO . forkIO . f =<< getSink >> >> This is a good illustration of too much implementation detail. Why do we >> need to know of (Sink p) as a Reader layer? Would it be clearer to >> define an Effect of sending to the socket? Computation's type will >> make it patent the computation is sending to the socket. >> The parent thread, before forking, has to provide a handler for that >> effect (and the handler will probably need a socket). >> >> Defining a new class for each effect is possible but not needed at >> all. With monad transformers, a class per effect is meant to hide the >> ordering of transformer layers in a monad transformer st
Re: [Haskell-cafe] Yet Another Forkable Class
To be honest I'm not so sure about these "effects"... Simply the fact that the Member class needs -XOverlappingInstances means that we cannot have duplicate or polymorphic effects. It will arbitrarily pick the first match in the former and fail to compile in the latter case. Furthermore I don't really understand the way open sums are implemented. These unions should be disjoint, but the way they're implemented in the paper they try to be "true" unions which cannot be done as that would need type equality (-XOverlappingInstances is a hack around this) A correct disjoint open sum would behave well with duplicate and polymorphic types in the type list. For example we should be able to project the open sum equivalent of Either String String into the second String but we cannot with the implementation in the paper. This means we need to ~index~ the type list instead of picking the result type and "trying for equality" with each entry. Something like this: http://lpaste.net/92069 Of course this is very inconvenient and simply replaces the monad transformers' lifts with a static index into the "effect" list. In general I think there is no convenient way of stacking effects that is also type safe. At some point we have to disambiguate which effect we are trying to use one way or the other. The implementation in the paper simply picks a heuristic and chooses the first effect that seems to match and discards the others. On 22 August 2013 12:15, Alberto G. Corona wrote: > The paper is very interesting: > > http://www.cs.indiana.edu/~sabry/papers/exteff.pdf > > It seems that the approach is mature enough and it is better in every way > than monad transformers, while at the same time the syntax may become > almost identical to MTL for many uses. > > I only expect to see the library in Hackage with all the blessings, and > with all the instances of the MTL classes in order to make the transition > form monad transformers to ExtEff as transparent as possible > > > 2013/8/22 > > >> Perhaps effect libraries (there are several to choose from) could be a >> better answer to Fork effects than monad transformers. One lesson from >> the recent research in effects is that we should start thinking what >> effect we want to achieve rather than which monad transformer to >> use. Using ReaderT or StateT or something else is an implementation >> detail. Once we know what effect to achieve we can write a handler, or >> interpreter, to implement the desired operation on the World, obeying >> the desired equations. And we are done. >> >> For example, with ExtEff library with which I'm more familiar, the >> Fork effect would take as an argument a computation that cannot throw >> any requests. That means that the parent has to provide interpreters >> for all child effects. It becomes trivially to implement: >> >> > Another example would be a child that should not be able to throw >> errors as >> > opposed to the parent thread. >> It is possible to specify which errors will be allowed for the child >> thread (the ones that the parent will be willing to reflect and >> interpret). The rest of errors will be statically prohibited then. >> >> > instance (Protocol p) => Forkable (WebSockets p) (ReaderT (Sink p) IO) >> where >> > fork (ReaderT f) = liftIO . forkIO . f =<< getSink >> >> This is a good illustration of too much implementation detail. Why do we >> need to know of (Sink p) as a Reader layer? Would it be clearer to >> define an Effect of sending to the socket? Computation's type will >> make it patent the computation is sending to the socket. >> The parent thread, before forking, has to provide a handler for that >> effect (and the handler will probably need a socket). >> >> Defining a new class for each effect is possible but not needed at >> all. With monad transformers, a class per effect is meant to hide the >> ordering of transformer layers in a monad transformer stack. Effect >> libraries abstract over the implementation details out of the >> box. Crutches -- extra classes -- are unnecessary. We can start by >> writing handlers on a case-by-case basis. Generalization, if any, >> we'll be easier to see. From my experience, generalizing from concrete >> cases is easier than trying to write a (too) general code at the >> outset. Way too often, as I read and saw, code that is meant to be >> reusable ends up hardly usable. >> >> >> >> >> ___ >> Haskell-Cafe mailing list >> Haskell-Cafe@haskell.org >> http://www.haskell.org/mailman/listinfo/haskell-cafe >> > > > > -- > Alberto. > > ___ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://www.haskell.org/mailman/listinfo/haskell-cafe > > ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Yet Another Forkable Class
The paper is very interesting: http://www.cs.indiana.edu/~sabry/papers/exteff.pdf It seems that the approach is mature enough and it is better in every way than monad transformers, while at the same time the syntax may become almost identical to MTL for many uses. I only expect to see the library in Hackage with all the blessings, and with all the instances of the MTL classes in order to make the transition form monad transformers to ExtEff as transparent as possible 2013/8/22 > > Perhaps effect libraries (there are several to choose from) could be a > better answer to Fork effects than monad transformers. One lesson from > the recent research in effects is that we should start thinking what > effect we want to achieve rather than which monad transformer to > use. Using ReaderT or StateT or something else is an implementation > detail. Once we know what effect to achieve we can write a handler, or > interpreter, to implement the desired operation on the World, obeying > the desired equations. And we are done. > > For example, with ExtEff library with which I'm more familiar, the > Fork effect would take as an argument a computation that cannot throw > any requests. That means that the parent has to provide interpreters > for all child effects. It becomes trivially to implement: > > > Another example would be a child that should not be able to throw errors > as > > opposed to the parent thread. > It is possible to specify which errors will be allowed for the child > thread (the ones that the parent will be willing to reflect and > interpret). The rest of errors will be statically prohibited then. > > > instance (Protocol p) => Forkable (WebSockets p) (ReaderT (Sink p) IO) > where > > fork (ReaderT f) = liftIO . forkIO . f =<< getSink > > This is a good illustration of too much implementation detail. Why do we > need to know of (Sink p) as a Reader layer? Would it be clearer to > define an Effect of sending to the socket? Computation's type will > make it patent the computation is sending to the socket. > The parent thread, before forking, has to provide a handler for that > effect (and the handler will probably need a socket). > > Defining a new class for each effect is possible but not needed at > all. With monad transformers, a class per effect is meant to hide the > ordering of transformer layers in a monad transformer stack. Effect > libraries abstract over the implementation details out of the > box. Crutches -- extra classes -- are unnecessary. We can start by > writing handlers on a case-by-case basis. Generalization, if any, > we'll be easier to see. From my experience, generalizing from concrete > cases is easier than trying to write a (too) general code at the > outset. Way too often, as I read and saw, code that is meant to be > reusable ends up hardly usable. > > > > > ___ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://www.haskell.org/mailman/listinfo/haskell-cafe > -- Alberto. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Yet Another Forkable Class
Perhaps effect libraries (there are several to choose from) could be a better answer to Fork effects than monad transformers. One lesson from the recent research in effects is that we should start thinking what effect we want to achieve rather than which monad transformer to use. Using ReaderT or StateT or something else is an implementation detail. Once we know what effect to achieve we can write a handler, or interpreter, to implement the desired operation on the World, obeying the desired equations. And we are done. For example, with ExtEff library with which I'm more familiar, the Fork effect would take as an argument a computation that cannot throw any requests. That means that the parent has to provide interpreters for all child effects. It becomes trivially to implement: > Another example would be a child that should not be able to throw errors as > opposed to the parent thread. It is possible to specify which errors will be allowed for the child thread (the ones that the parent will be willing to reflect and interpret). The rest of errors will be statically prohibited then. > instance (Protocol p) => Forkable (WebSockets p) (ReaderT (Sink p) IO) where > fork (ReaderT f) = liftIO . forkIO . f =<< getSink This is a good illustration of too much implementation detail. Why do we need to know of (Sink p) as a Reader layer? Would it be clearer to define an Effect of sending to the socket? Computation's type will make it patent the computation is sending to the socket. The parent thread, before forking, has to provide a handler for that effect (and the handler will probably need a socket). Defining a new class for each effect is possible but not needed at all. With monad transformers, a class per effect is meant to hide the ordering of transformer layers in a monad transformer stack. Effect libraries abstract over the implementation details out of the box. Crutches -- extra classes -- are unnecessary. We can start by writing handlers on a case-by-case basis. Generalization, if any, we'll be easier to see. From my experience, generalizing from concrete cases is easier than trying to write a (too) general code at the outset. Way too often, as I read and saw, code that is meant to be reusable ends up hardly usable. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Yet Another Forkable Class
TLDR: New forkable monad/transformer suggestion http://pastebin.com/QNUVL12v(hpaste is down) Hi, There are a dozen packages on hackage defining a class for monads that can be forked, however none of these are modular enough to be useful in my opinion. In particular the following are not addressed: 1. Cases when the child thread's monad is different from the parent's 2. Monad transformers (this is somewhat addressed with Control.Monad.Trans.Control) I will try to demonstrate both issues with an example. 1. WebSockets WebSockets is a monad that cannot itself be forked. This is because at any given time there should only be a single thread listening on a websocket. However there is a reasonable monad that can be forked off, namely one that can send to the websocket - one that has access to the Sink. So first off a "Forkable" class should not look like this: class (MonadIO m, MonadIO n) => Forkable m where fork :: m () -> m ThreadId But rather like this: class Forkable m n where fork :: n () -> m ThreadId For our example the instance would be instance (Protocol p) => Forkable (WebSockets p) (ReaderT (Sink p) IO) where fork (ReaderT f) = liftIO . forkIO . f =<< getSink Another example would be a child that should not be able to throw errors as opposed to the parent thread. 2. ReaderT Continuing from the previous example to demonstrate the need to distinguish forkable transformers. Say we have some shared state S that both parent and child should have access to: type Parent p = ReaderT (TVar S) (WebSockets p) type Child p = ReaderT (TVar S) (ReaderT (Sink p) IO) The "forkability" of Child from Parent should be implied, however with Forkable we have to write a separate instance. So what I suggest is a second class: class ForkableT t where forkT :: (Forkable m n) => t n () -> t m ThreadId And then: instance ForkableT (ReaderT r) where forkT (ReaderT f) = ReaderT $ fork . f We can also introduce a default for Forkable that uses a ForkableT instance: class (MonadIO m, MonadIO n) => Forkable m n where fork :: n () -> m ThreadId default fork :: ForkableT t => t n () -> t m ThreadId fork = forkT instance (Forkable m n) => Forkable (ReaderT r m) (ReaderT r n) This means Child is automatically Forkable from Parent, no need to write a specific case for our specific monads (and if we newtype it we can use -XGeneralizedNewtypeDeriving) Note how MonadTransControl already solves the specific problem of lifting a forking operation into ReaderT. However consider ResourceT from Control.Monad.Resource: it is basically a ReaderT, however in order to safely deallocate resources when sharing reference counting is needed. This means a simple lift would not suffice. We can nevertheless provide a default ForkableT based on MonadTransControl: class ForkableT t where forkT :: (Forkable m n) => t n () -> t m ThreadId default forkT :: (MonadTransControl t, Forkable m n) => t n () -> t m ThreadId forkT t = liftWith $ \run -> fork $ run t >> return () Actually resourcet's reference counting resourceForkIO also nicely demonstrates the first problem: type Parent p = ResourceT (WebSockets p) type Child p = ResourceT (ReaderT (Sink p) IO) Note how we cannot use resourceForkIO without touching the underlying monads. What do you think? Is there already an established way of modular forking? I wouldn't like to litter hackage with another unusable Forkable class:) ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe