Safe Haskell | Safe |
---|---|
Language | Haskell2010 |
Original work available at http://okmij.org/ftp/Haskell/extensible/Eff.hs. This module implements extensible effects as an alternative to monad transformers, as described in http://okmij.org/ftp/Haskell/extensible/exteff.pdf.
Extensible Effects are implemented as typeclass constraints on an Eff[ect] datatype. A contrived example is:
{-# LANGUAGE FlexibleContexts #-} import Control.Eff import Control.Eff.Lift import Control.Eff.State import Control.Monad (void) import Data.Typeable -- Write the elements of a list of numbers, in order. writeAll :: (Typeable a, Member (Writer a) e) => [a] -> Eff e () writeAll = mapM_ putWriter -- Add a list of numbers to the current state. sumAll :: (Typeable a, Num a, Member (State a) e) => [a] -> Eff e () sumAll = mapM_ (onState . (+)) -- Write a list of numbers and add them to the current state. writeAndAdd :: (Member (Writer Integer) e, Member (State Integer) e) => [Integer] -> Eff e () writeAndAdd l = do writeAll l sumAll l -- Sum a list of numbers. sumEff :: (Num a, Typeable a) => [a] -> a sumEff l = let (s, ()) = run $ runState 0 $ sumAll l in s -- Safely get the last element of a list. -- Nothing for empty lists; Just the last element otherwise. lastEff :: Typeable a => [a] -> Maybe a lastEff l = let (a, ()) = run $ runWriter $ writeAll l in a -- Get the last element and sum of a list lastAndSum :: (Typeable a, Num a) => [a] -> (Maybe a, a) lastAndSum l = let (lst, (total, ())) = run $ runWriter $ runState 0 $ writeAndAdd l in (lst, total)
- type Eff r = Free (Union r)
- class (Member' t r ~ True) => Member t r
- class Member t r => SetMember set t r | r set -> t
- data Union r v
- data a :> b
- inj :: (Functor t, Typeable t, Member t r) => t v -> Union r v
- prj :: (Typeable t, Member t r) => Union r v -> Maybe (t v)
- prjForce :: (Typeable t, Member t r) => Union r v -> (t v -> a) -> a
- decomp :: Typeable t => Union (t :> r) v -> Either (Union r v) (t v)
- send :: Union r a -> Eff r a
- run :: Eff Void w -> w
- interpose :: (Typeable t, Functor t, Member t r) => Union r v -> (v -> Eff r a) -> (t v -> Eff r a) -> Eff r a
- handleRelay :: Typeable t => Union (t :> r) v -> (v -> Eff r a) -> (t v -> Eff r a) -> Eff r a
- unsafeReUnion :: Union r w -> Union t w
Documentation
type Eff r = Free (Union r) Source
Basic type returned by all computations with extensible effects. The
type is a type synonym where the type Eff
rr
is the type of effects that can
be handled, and the missing type a
(from the type application) is the type
of value that is returned.
Expressed another way: an Eff
can either be a value (i.e., Pure
case), or
an effect of type
producing another Union
rEff
(i.e., Impure
case). The result is that an Eff
can produce an arbitrarily long chain of
effects, terminated with a pure value.Union
r
As is made explicit below, the Eff
type is simply the Free monad resulting from the
functor.Union
r
typeEff
r a =Free
(Union
r) a
class Member t r => SetMember set t r | r set -> t Source
SetMember
is similar to Member
, but it allows types to belong to a
"set". For every set, only one member can be in r
at any given time.
This allows us to specify exclusivity and uniqueness among arbitrary effects:
-- Terminal effects (effects which must be run last) data Terminal -- Make Lifts part of the Terminal effects set. -- The fundep assures that there can only be one Terminal effect for any r. instance Member (Lift m) r => SetMember Terminal (Lift m) r -- Only allow a single unique Lift effect, by making a "Lift" set. instance Member (Lift m) r => SetMember Lift (Lift m) r
MemberU k set t r => SetMember (k -> * -> *) set t r |
Parameter r
is phantom: it just tells what could be in the union.
Where r
is t1 :> t2 ... :> tn
,
can be constructed with a
value of type Union
r vti v
.
Ideally, we should be able to add the constraint
.Member
t r
NOTE: exposing the constructor below allows users to bypass the type
system. See unsafeReUnion
for example.
A sum data type, for composing effects
prj :: (Typeable t, Member t r) => Union r v -> Maybe (t v) Source
Try extracting the contents of a Union as a given type.
prjForce :: (Typeable t, Member t r) => Union r v -> (t v -> a) -> a Source
Extract the contents of a Union as a given type. If the Union isn't of that type, a runtime error occurs.
decomp :: Typeable t => Union (t :> r) v -> Either (Union r v) (t v) Source
Try extracting the contents of a Union as a given type. If we can't, return a reduced Union that excludes the type we just checked.
send :: Union r a -> Eff r a Source
Given a method of turning requests into results, we produce an effectful computation.
interpose :: (Typeable t, Functor t, Member t r) => Union r v -> (v -> Eff r a) -> (t v -> Eff r a) -> Eff r a Source
Given a request, either handle it or relay it. Both the handler and the relay can produce the same type of request that was handled.
:: Typeable t | |
=> Union (t :> r) v | Request |
-> (v -> Eff r a) | Relay the request |
-> (t v -> Eff r a) | Handle the request of type t |
-> Eff r a |
Given a request, either handle it or relay it.
unsafeReUnion :: Union r w -> Union t w Source
Juggle types for a Union. Use cautiously.