-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An Alternative to Monad Transformers -- -- This package introduces datatypes for typeclass-constrained effects, -- as an alternative to monad-transformer based (datatype-constrained) -- approach of multi-layered effects. For more information, see the -- original paper at -- http://okmij.org/ftp/Haskell/extensible/exteff.pdf. Any help is -- appreciated! @package extensible-effects @version 1.6.0 -- | 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)
--
module Control.Eff
-- | Basic datatype returned by all computations with extensible effects.
-- The type r is the type of effects that can be handled, and
-- a is the type of value that is returned.
data Eff r a
-- | A VE is either a value, or an effect of type Union
-- r producing another VE. The result is that a VE can
-- produce an arbitrarily long chain of Union r effects,
-- terminated with a pure value.
data VE w r
Val :: w -> VE w r
E :: !(Union r (VE w r)) -> VE w r
-- | The Member t r specifies whether t is present
-- anywhere in the sum type r, where t is some
-- effectful type, e.g. Lift IO,
-- State Int`.
class Member t r
-- | 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 --class Member t r => SetMember set (t :: * -> *) r | r set -> t -- | Where r is t1 :> t2 ... :> tn, -- Union r v can be constructed with a value of type -- ti v. Ideally, we should be be able to add the constraint -- Member t r. data Union r v -- | A sum data type, for composing effects data (:>) (a :: * -> *) b -- | Construct a Union. inj :: (Functor t, Typeable1 t, Member t r) => t v -> Union r v -- | Try extracting the contents of a Union as a given type. prj :: (Typeable1 t, Member t r) => Union r v -> Maybe (t v) -- | Extract the contents of a Union as a given type. If the Union isn't of -- that type, a runtime error occurs. prjForce :: (Typeable1 t, Member t r) => Union r v -> (t v -> a) -> a -- | 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. decomp :: Typeable1 t => Union (t :> r) v -> Either (Union r v) (t v) -- | Given a method of turning requests into results, we produce an -- effectful computation. send :: (forall w. (a -> VE w r) -> Union r (VE w r)) -> Eff r a -- | Tell an effectful computation that you're ready to start running -- effects and return a value. admin :: Eff r w -> VE w r -- | Get the result from a pure computation. run :: Eff () w -> w -- | 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. interpose :: (Typeable1 t, Functor t, Member t r) => Union r v -> (v -> Eff r a) -> (t v -> Eff r a) -> Eff r a -- | Given a request, either handle it or relay it. handleRelay :: Typeable1 t => Union (t :> r) v -> (v -> Eff r a) -> (t v -> Eff r a) -> Eff r a -- | Juggle types for a Union. Use cautiously. unsafeReUnion :: Union r w -> Union t w instance Typeable2 VE instance Typeable2 Eff instance Monad (Eff r) instance Applicative (Eff r) instance Functor (Eff r) -- | Nondeterministic choice effect module Control.Eff.Choose -- | Nondeterministic choice data Choose v Choose :: [a] -> (a -> v) -> Choose v -- | choose lst non-deterministically chooses one value from the lst choose -- [] thus corresponds to failure choose :: Member Choose r => [a] -> Eff r a -- | Run a nondeterministic effect, returning all values. runChoice :: Eff (Choose :> r) a -> Eff r [a] -- | MonadPlus-like operators are expressible via choose mzero' :: Member Choose r => Eff r a -- | MonadPlus-like operators are expressible via choose mplus' :: Member Choose r => Eff r a -> Eff r a -> Eff r a instance Typeable1 Choose instance Functor Choose -- | Coroutines implemented with extensible effects module Control.Eff.Coroutine -- | The yield request: reporting a value of type e and suspending the -- coroutine. For readability, a coroutine accepts a unit to produce its -- value. data Yield a v -- | Yield a value of type a and suspend the coroutine. yield :: (Typeable a, Member (Yield a) r) => a -> Eff r () -- | Launch a thread and report its status. runC :: Typeable a => Eff (Yield a :> r) w -> Eff r (Y r a w) -- | Status of a thread: done or reporting the value of the type a (For -- simplicity, a co-routine reports a value but accepts unit) -- -- Type parameter r is the effect we're yielding from. -- -- Type parameter a is the type that is yielded. -- -- Type parameter w is the type of the value returned from the -- coroutine when it has completed. data Y r a w Y :: a -> (() -> Eff r (Y r a w)) -> Y r a w Done :: w -> Y r a w instance Typeable2 Yield instance Functor (Yield a) -- | Lifting primitive Monad types to effectful computations. We only allow -- a single Lifted Monad because Monads aren't commutative (e.g. Maybe -- (IO a) is functionally distinct from IO (Maybe a)). module Control.Eff.Lift -- | Lift a Monad m to an effect. data Lift m v -- | Lift a Monad to an Effect. We only allow a single lifted Monad. lift :: (Typeable1 m, SetMember Lift (Lift m) r) => m a -> Eff r a -- | The handler of Lift requests. It is a terminal handler: use -- runLift in place of run. runLift :: (Monad m, Typeable1 m) => Eff (Lift m :> ()) w -> m w instance [incoherent] (MonadBase b m, Typeable1 m, Member (* -> *) * (Lift m) r) => MonadBase b (Eff r) instance [incoherent] (Typeable1 m, MonadIO m, Member (* -> *) * (Lift m) r) => MonadIO (Eff r) instance [incoherent] Functor (Lift m) instance [incoherent] Member (* -> *) * (Lift m) r => SetMember ((* -> *) -> * -> *) * Lift (Lift m) r instance [incoherent] SetMember ((* -> *) -> * -> *) * Lift (Lift m) ((:>) * (Lift m) ()) instance [incoherent] Typeable1 m => Typeable1 (Lift m) -- | Exception-producing and exception-handling effects module Control.Eff.Exception -- | These are exceptions of the type e. This is akin to the error monad. newtype Exc e v Exc :: e -> Exc e v type Fail = Exc () -- | Throw an exception in an effectful computation. throwExc :: (Typeable e, Member (Exc e) r) => e -> Eff r a -- | Makes an effect fail, preventing future effects from happening. die :: Member Fail r => Eff r a -- | Run a computation that might produce an exception. runExc :: Typeable e => Eff (Exc e :> r) a -> Eff r (Either e a) -- | Runs a failable effect, such that failed computation return -- Nothing, and Just the return value on success. runFail :: Eff (Fail :> r) a -> Eff r (Maybe a) -- | Run a computation that might produce exceptions, and give it a way to -- deal with the exceptions that come up. catchExc :: (Typeable e, Member (Exc e) r) => Eff r a -> (e -> Eff r a) -> Eff r a -- | Add a default value (i.e. failure handler) to a fallible computation. -- This hides the fact that a failure happened. onFail :: Eff (Fail :> r) a -> Eff r a -> Eff r a -- | Run a computation until it produces an exception, and convert and -- throw that exception in a new context. rethrowExc :: (Typeable e, Typeable e', Member (Exc e') r) => (e -> e') -> Eff (Exc e :> r) a -> Eff r a -- | Treat Lefts as exceptions and Rights as return values. liftEither :: (Typeable e, Member (Exc e) r) => Either e a -> Eff r a -- | liftEither in a lifted Monad liftEitherM :: (Typeable1 m, Typeable e, Member (Exc e) r, Member (Lift m) r) => m (Either e a) -> Eff r a -- | Lift a maybe into the Fail effect, causing failure if it's -- Nothing. liftMaybe :: Member Fail r => Maybe a -> Eff r a -- | Ignores a failure event. Since the event can fail, you cannot inspect -- its return type, because it has none on failure. To inspect it, use -- runFail. ignoreFail :: Eff (Fail :> r) a -> Eff r () instance Typeable2 Exc instance Functor (Exc e) -- | An example of non-trivial interaction of effects, handling of two -- effects together Non-determinism with control (cut) For the -- explanation of cut, see Section 5 of Hinze ICFP 2000 paper. Hinze -- suggests expressing cut in terms of cutfalse: -- --
-- = return () `mplus` cutfalse -- where -- cutfalse :: m a ---- -- satisfies the following laws: -- --
-- cutfalse >>= k = cutfalse (F1) -- cutfalse | m = cutfalse (F2) ---- -- (note: m `mplus` cutfalse is different from -- cutfalse `mplus` m) In other words, cutfalse is the -- left zero of both bind and mplus. -- -- Hinze also introduces the operation call :: m a -> m -- a that delimits the effect of cut: call m -- executes m. If the cut is invoked in m, it discards only the choices -- made since m was called. Hinze postulates the axioms of call: -- --
-- call false = false (C1) -- call (return a | m) = return a | call m (C2) -- call (m | cutfalse) = call m (C3) -- call (lift m >>= k) = lift m >>= (call . k) (C4) ---- -- call m behaves like m except any cut inside -- m has only a local effect, he says. -- -- Hinze noted a problem with the "mechanical" derivation of backtracing -- monad transformer with cut: no axiom specifying the interaction of -- call with bind; no way to simplify nested invocations of call. -- -- We use exceptions for cutfalse Therefore, the law cutfalse -- >>= k = cutfalse is satisfied automatically since all -- exceptions have the above property. module Control.Eff.Cut data CutFalse -- | The interpreter -- it is like reify . reflect with a twist Compare -- this implementation with the huge implementation of call in Hinze 2000 -- (Figure 9) Each clause corresponds to the axiom of call or cutfalse. -- All axioms are covered. The code clearly expresses the intuition that -- call watches the choice points of its argument computation. When it -- encounteres a cutfalse request, it discards the remaining -- choicepoints. It completely handles CutFalse effects but not -- non-determinism. call :: Member Choose r => Eff (Exc CutFalse :> r) a -> Eff r a cutfalse :: Member (Exc CutFalse) r => Eff r a instance Typeable CutFalse -- | Create unique Enumerable values. module Control.Eff.Fresh -- | Create unique Enumerable values. data Fresh i v -- | Produce a value that has not been previously produced. fresh :: (Typeable i, Enum i, Member (Fresh i) r) => Eff r i -- | Run an effect requiring unique values. runFresh :: (Typeable i, Enum i) => Eff (Fresh i :> r) w -> i -> Eff r w instance Typeable2 Fresh instance Functor (Fresh i) -- | Lazy read-only state module Control.Eff.Reader.Lazy -- | The request for a value of type e from the current environment. This -- environment is analogous to a parameter of type e. data Reader e v -- | Get the current value from a Reader. ask :: (Typeable e, Member (Reader e) r) => Eff r e -- | Locally rebind the value in the dynamic environment. This function -- both requests and admins Reader requests. local :: (Typeable e, Member (Reader e) r) => (e -> e) -> Eff r a -> Eff r a -- | Request the environment value using a transformation function. reader :: (Typeable e, Member (Reader e) r) => (e -> a) -> Eff r a -- | The handler of Reader requests. The return type shows that all Reader -- requests are fully handled. runReader :: Typeable e => Eff (Reader e :> r) w -> e -> Eff r w instance Typeable2 Reader instance Functor (Reader e) -- | Strict read-only state module Control.Eff.Reader.Strict -- | The request for a value of type e from the current environment. This -- environment is analogous to a parameter of type e. data Reader e v -- | Get the current value from a Reader. ask :: (Typeable e, Member (Reader e) r) => Eff r e -- | Locally rebind the value in the dynamic environment. This function -- both requests and admins Reader requests. local :: (Typeable e, Member (Reader e) r) => (e -> e) -> Eff r a -> Eff r a -- | Request the environment value using a transformation function. reader :: (Typeable e, Member (Reader e) r) => (e -> a) -> Eff r a -- | The handler of Reader requests. The return type shows that all Reader -- requests are fully handled. runReader :: Typeable e => Eff (Reader e :> r) w -> e -> Eff r w instance Typeable2 Reader instance Functor (Reader e) -- | Lazy state effect module Control.Eff.State.Lazy -- | Strict state effect data State s w -- | Return the current value of the state. get :: (Typeable e, Member (State e) r) => Eff r e -- | Write a new value of the state. put :: (Typeable e, Member (State e) r) => e -> Eff r () -- | Transform the state with a function. modify :: (Typeable s, Member (State s) r) => (s -> s) -> Eff r () -- | Run a State effect. runState :: Typeable s => s -> Eff (State s :> r) w -> Eff r (s, w) -- | Run a State effect, discarding the final state. evalState :: Typeable s => s -> Eff (State s :> r) w -> Eff r w -- | Run a State effect and return the final state. execState :: Typeable s => s -> Eff (State s :> r) w -> Eff r s instance Typeable2 State instance Functor (State s) -- | Strict state effect -- -- Example: implementing Fresh -- --
-- runFresh' :: (Typeable i, Enum i, Num i) => Eff (Fresh i :> r) w -> i -> Eff r w -- runFresh' m s = fst <$> runState s (loop $ admin m) -- where -- loop (Val x) = return x -- loop (E u) = case decomp u of -- Right (Fresh k) -> do -- n <- get -- put (n + 1) -- loop (k n) -- Left u' -> send (\k -> unsafeReUnion $ k <$> u') >>= loop --module Control.Eff.State.Strict -- | Strict state effect data State s w -- | Return the current value of the state. get :: (Typeable e, Member (State e) r) => Eff r e -- | Write a new value of the state. put :: (Typeable e, Member (State e) r) => e -> Eff r () -- | Transform the state with a function. modify :: (Typeable s, Member (State s) r) => (s -> s) -> Eff r () -- | Run a State effect. runState :: Typeable s => s -> Eff (State s :> r) w -> Eff r (s, w) -- | Run a State effect, discarding the final state. evalState :: Typeable s => s -> Eff (State s :> r) w -> Eff r w -- | Run a State effect and return the final state. execState :: Typeable s => s -> Eff (State s :> r) w -> Eff r s instance Typeable2 State instance Functor (State s) -- | Lazy write-only state. module Control.Eff.Writer.Lazy -- | The request to remember a value of type w in the current environment data Writer w v -- | Write a new value. tell :: (Typeable w, Member (Writer w) r) => w -> Eff r () -- | Transform the state being produced. censor :: (Typeable w, Member (Writer w) r) => (w -> w) -> Eff r a -> Eff r a -- | Handle Writer requests, using a user-provided function to accumulate -- values. runWriter :: Typeable w => (w -> b -> b) -> b -> Eff (Writer w :> r) a -> Eff r (b, a) -- | Handle Writer requests by taking the first value provided. runFirstWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a) -- | Handle Writer requests by overwriting previous values. runLastWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a) -- | Handle Writer requests, using a Monoid instance to accumulate values. runMonoidWriter :: (Monoid w, Typeable w) => Eff (Writer w :> r) a -> Eff r (w, a) instance Typeable2 Writer instance Functor (Writer w) -- | Strict write-only state. module Control.Eff.Writer.Strict -- | The request to remember a value of type w in the current environment data Writer w v -- | Write a new value. tell :: (Typeable w, Member (Writer w) r) => w -> Eff r () -- | Transform the state being produced. censor :: (Typeable w, Member (Writer w) r) => (w -> w) -> Eff r a -> Eff r a -- | Handle Writer requests, using a user-provided function to accumulate -- values. runWriter :: Typeable w => (w -> b -> b) -> b -> Eff (Writer w :> r) a -> Eff r (b, a) -- | Handle Writer requests by taking the first value provided. runFirstWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a) -- | Handle Writer requests by overwriting previous values. runLastWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a) -- | Handle Writer requests, using a Monoid instance to accumulate values. runMonoidWriter :: (Monoid w, Typeable w) => Eff (Writer w :> r) a -> Eff r (w, a) instance Typeable2 Writer instance Functor (Writer w) -- | A Trace effect for debugging module Control.Eff.Trace -- | Trace effect for debugging data Trace v -- | Print a string as a trace. trace :: Member Trace r => String -> Eff r () -- | Run a computation producing Traces. runTrace :: Eff (Trace :> ()) w -> IO w instance Typeable1 Trace instance Functor Trace