extensible-effects- An Alternative to Monad Transformers

Safe HaskellSafe
  • Cpp
  • UndecidableInstances
  • ScopedTypeVariables
  • TypeSynonymInstances
  • FlexibleContexts
  • FlexibleInstances
  • ConstrainedClassMethods
  • MultiParamTypeClasses
  • ExistentialQuantification
  • TupleSections
  • RankNTypes
  • TypeOperators
  • ExplicitNamespaces
  • ExplicitForAll



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 can be found under Control.Eff.Example. To run the effects, consult the tests.



type Eff r = Free (Union r) Source #

Basic type returned by all computations with extensible effects. The Eff r type is a type synonym where the type r 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 Union r producing another Eff (i.e., Impure case). The result is that an Eff can produce an arbitrarily long chain of Union r effects, terminated with a pure value.

As is made explicit below, the Eff type is simply the Free monad resulting from the Union r functor.

type Eff r a = Free (Union r) a

type Member = MemberImpl OU2 Source #

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 Source # 

data Union r v Source #

Parameter r is phantom: it just tells what could be in the union. Where r is t1 :> t2 ... :> tn, Union r v can be constructed with a value of type ti 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.


Functor (Union r) Source # 


fmap :: (a -> b) -> Union r a -> Union r b #

(<$) :: a -> Union r b -> Union r a #

data a :> b infixr 1 Source #

A sum data type, for composing effects


MemberU' k (EQU (* -> *) t1 t2) tag t1 ((:>) t2 r) => MemberUImpl k OU2 tag t1 ((:>) t2 r) 

inj :: (Functor t, Typeable t, Member t r) => t v -> Union r v Source #

Construct a Union.

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.

run :: Eff Void w -> w Source #

Get the result from a pure 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.

handleRelay Source #


:: Typeable t 
=> Union (t :> r) v


-> (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.