HORIZON HASKELLDocslts/ghc-9.14.x45dd5372026-10-11Search names, modules, packages, or :: a typeCtrl K

GHC 9.14.1 · lts/ghc-9.14.x · 45dd537 · 2026-10-11

Moduleextensible-effects-5.0.0.1Haskell2010

Control.Eff.Extend

This module exports functions, types, and typeclasses necessary for implementing a custom effect and/or effect handler.

  • 8 types
  • 4 classes
  • 22 values

The effect monad

3 declarations
datadata Eff (r :: [Type -> Type]) a
#

The monad that all effects in this library are based on.

An effectful computation is a value of type `Eff r a`. In this signature, r is a type-level list of effects that are being requested and need to be handled inside an effectful computation. a is the computation's result similar to other monads.

A computation's result can be retrieved via the run function. However, all effects used in the computation need to be handled by the use of the effects' run* functions before unwrapping the final result. For additional details, see the documentation of the effects you are using.

Constructors

Instances35MonadBase, MonadBaseControl, Monad, Functor, Applicative, Alternative, …
  • Alternative f => Handle NDet r a (Eff r' (f w))Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet

    Given a callback and NDet requests respond to them. Note that this makes explicit that we rely on f to have enough room to store all possibilities.

  • Alternative f => Handle NDet r a ([Eff r a] -> Eff r' (f w))Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet

    More performant handler; uses reified job queue

  • (Monad b, MonadBase b m, Lifted m r) => MonadBase b (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • MonadBase m m => MonadBaseControl m (Eff '[Lift m])Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (Exc e ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.Exception
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (Fresh ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.Fresh
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (NDet ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (State s ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.State.Lazy
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (OnDemandState s ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.State.OnDemand
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (State s ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.State.Strict
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (Writer w ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.Writer.Lazy
  • (MonadBase m m, LiftedBase m r) => MonadBaseControl m (Eff (Writer w ': r))Defined in extensible-effects-5.0.0.1 · Control.Eff.Writer.Strict
  • (MonadBase m m, LiftedBase m s) => MonadBaseControl m (Eff (Reader e ': s))Defined in extensible-effects-5.0.0.1 · Control.Eff.Reader.Lazy
  • (MonadBase m m, LiftedBase m s) => MonadBaseControl m (Eff (Reader e ': s))Defined in extensible-effects-5.0.0.1 · Control.Eff.Reader.Strict
  • Monad (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Functor (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Applicative (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Member NDet r => Alternative (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet · orphan
  • Member NDet r => MonadPlus (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet · orphan

    Mapping of NDet requests to MonadPlus. We obey the following laws (taken from the Backtr and @LogicT papers):

    mzero >>= f = mzero                               -- (L1)
    mzero `mplus` m = m                               -- (L2)
    m `mplus` mzero = m                               -- (L3)
    m `mplus` (n `mplus` o) = (m `mplus` n) `mplus` o -- (L4)
    (m `mplus` n) >>= k = (m >>= k) `mplus` (n >>= k) -- (L5)
    • L1 is the left-zero law for mzero

    • L2, L3, L4 are the Monoid laws

    NOTE that we do not obey the right-zero law for mzero. Specifically, we do not obey:

    m >> mzero  = mzero
  • (MonadIO m, Lifted m r) => MonadIO (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Member NDet r => MSplit (Eff r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet · orphan

    We implement LogicT, the non-determinism reflection, of which soft-cut is one instance. See the LogicT paper for an explanation.

  • Relay (Eff r w) rDefined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Handle (Program f) r a (Intrprtr f r' -> Eff r' a)Defined in extensible-effects-5.0.0.1 · Control.Eff.Operational

    Given a continuation and a program, interpret it Usually, we have r ~ [Program f : r']

  • Handle (Yield a b) (Yield a b ': r) w (Eff r (Y r b a))Defined in extensible-effects-5.0.0.1 · Control.Eff.Coroutine

    Given a continuation and a request, respond to it

  • type StM (Eff '[Lift m]) a = aDefined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • type StM (Eff (Exc e ': r)) a = StM (Eff r) (Either e a)Defined in extensible-effects-5.0.0.1 · Control.Eff.Exception
  • type StM (Eff (Fresh ': r)) a = StM (Eff r) (a, Int)Defined in extensible-effects-5.0.0.1 · Control.Eff.Fresh
  • type StM (Eff (NDet ': r)) a = StM (Eff r) [a]Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet
  • type StM (Eff (Reader e ': s)) a = StM (Eff s) aDefined in extensible-effects-5.0.0.1 · Control.Eff.Reader.Lazy
  • type StM (Eff (Reader e ': s)) a = StM (Eff s) aDefined in extensible-effects-5.0.0.1 · Control.Eff.Reader.Strict
  • type StM (Eff (State s ': r)) a = StM (Eff r) (a, s)Defined in extensible-effects-5.0.0.1 · Control.Eff.State.Lazy
  • type StM (Eff (OnDemandState s ': r)) a = StM (Eff r) (a, s)Defined in extensible-effects-5.0.0.1 · Control.Eff.State.OnDemand
  • type StM (Eff (State s ': r)) a = StM (Eff r) (a, s)Defined in extensible-effects-5.0.0.1 · Control.Eff.State.Strict
  • type StM (Eff (Writer w ': r)) a = StM (Eff r) (a, [w])Defined in extensible-effects-5.0.0.1 · Control.Eff.Writer.Lazy
  • type StM (Eff (Writer w ': r)) a = StM (Eff r) (a, [w])Defined in extensible-effects-5.0.0.1 · Control.Eff.Writer.Strict
valuerun :: Eff '[] w -> w
#

Get the result from a pure computation

A pure computation has type Eff '[] a. The empty effect-list indicates that no further effects need to be handled.

valueeff :: (a -> b) -> (forall v. Arrs r v a -> Union r v -> b) -> Eff r a -> b
#

Case analysis for Eff datatype. If the value is Val a apply the first function to a; if it is E u q, apply the second function.

Lifting operations

8 declarations
newtypenewtype Lift (m :: k -> Type) (a :: k)
#

Lifting: emulating monad transformers

Constructors

Instances3MonadBaseControl, Handle, StM
  • MonadBase m m => MonadBaseControl m (Eff '[Lift m])Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Monad m => Handle (Lift m) r a (m k)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal

    Handle lifted requests by running them sequentially

  • type StM (Eff '[Lift m]) a = aDefined in extensible-effects-5.0.0.1 · Control.Eff.Internal
typetype Lifted (m :: Type -> Type) (r :: [Type -> Type]) = SetMember Lift (Lift m) r
#

A convenient alias to SetMember Lift (Lift m) r, which allows us to assert that the lifted type occurs ony once in the effect list.

valuelift :: Lifted m r => m a -> Eff r a
#

embed an operation of type `m a` into the Eff monad when Lift m is in a part of the effect-list.

valuerunLift :: Monad m => Eff '[Lift m] w -> m w
#

The handler of Lift requests. It is meant to be terminal: we only allow a single Lifted Monad. Note, too, how this is different from other handlers.

valuecatchesDynE :: Lifted IO r => Eff r a -> [HandlerDynE r a] -> Eff r a
#

Catch multiple dynamic exceptions. The implementation follows that in Control.Exception almost exactly. Not yet tested. Could this be useful for control with cut?

Open Unions

10 declarations
datadata Union (r :: [Type -> Type]) (v :: k) where
#

The data constructors of Union are not exported

Strong Sum (Existential with the evidence) is an open union t is can be a GADT and hence not necessarily a Functor. Int is the index of t in the list r; that is, the index of t in the universe r

classclass FindElem t r => Member (t :: Type -> Type) (r :: [Type -> Type]) where
#

Typeclass that asserts that effect t is contained inside the effect-list r.

The FindElem typeclass is an implementation detail and not required for using the effect list or implementing custom effects.

Instances2Member
  • FindElem t r => Member t rDefined in extensible-effects-5.0.0.1 · Data.OpenUnion
  • t ~ s => Member t '[s]Defined in extensible-effects-5.0.0.1 · Data.OpenUnion

    Explicit type-level equality condition is a dirty hack to eliminate the type annotation in the trivial case, such as run (runReader () get).

    There is no ambiguity when finding instances for Member t (a ': b ': r), which the second instance is selected.

    The only case we have to concerned about is Member t '[s]. But, in this case, values of definition is the same (if present), and the first one is chosen according to GHC User Manual, since the latter one is incoherent. This is the optimal choice.

patternpattern U0' :: Member t r => t v -> Union r v
#

Pattern synonym to project the union onto the effect t.

valuedecomp :: Union (t ': r) v -> Either (Union r v) (t v)
#

Orthogonal decomposition of the union: head and the rest.

patternpattern U0 :: t v -> Union (t ': r) v
#

Some helpful pattern synonyms. U0 : the first element of the union

patternpattern U1 :: Union r v -> Union (t ': r) v
#

U1 : everything excluding the first element of the union.

classclass Member t r => SetMember (tag :: k -> Type -> Type) (t :: Type -> Type) (r :: [Type -> Type]) | tag r -> t
#

This class is used for emulating monad transformers

Instances1SetMember
  • (EQU t1 t2 p, MemberU' p tag t1 (t2 ': r)) => SetMember tag t1 (t2 ': r)Defined in extensible-effects-5.0.0.1 · Data.OpenUnion

Helper functions that are used for implementing effect-handlers

6 declarations
classclass Handle (t :: Type -> Type) (r :: [Type -> Type]) a k where
#

Respond to requests of type t. The handlers themselves are expressed in open-recursion style.

Methods

  • handle :: (Eff r a -> k) -> Arrs r v a -> t v -> k
  • handle_relay :: (r ~ (t ': r'), Relay k r') => (a -> k) -> (Eff r a -> k) -> Eff r a -> k

    A convenient pattern: given a request (in an open union), either handle it (using default Handler) or relay it.

    Handle implies that all requests of type t are dealt with, i.e., k (the response type) doesn't have t as part of its effect list. The Relay k r constraint ensures that k is an effectful computation (with effectlist r).

    Note that we can only handle the leftmost effect type (a consequence of the Data.OpenUnion implementation.

  • respond_relay :: (Member t r, Relay k r) => (a -> k) -> (Eff r a -> k) -> Eff r a -> k

    Intercept the request and possibly respond to it, but leave it unhandled. The Relay k r constraint ensures that k is an effectful computation (with effectlist r). As such, the effect type t will show up in the response type k. There are two natural / commmon options for k: the implicit effect domain (i.e., Eff r (f a)), or the explicit effect domain (i.e., s1 -> s2 -> ... -> sn -> Eff r (f a s1 s2 ... sn)).

    There are three different ways in which we may want to alter behaviour:

    1. Before: This work should be done before respond_relay is called.

    2. During: This work should be done by altering the handler being passed to respond_relay. This allows us to modify the requests "in flight".

    3. After: This work should be done be altering the ret being passed to respond_relay. This allows us to overwrite changes or discard them altogether. If this seems magical, note that we have the flexibility of altering the target domain k. Specifically, the explicit domain representation gives us access to the "effect" realm allowing us to manipulate it directly.

Instances15Handle, …
  • Handle Trace r a (IO k)Defined in extensible-effects-5.0.0.1 · Control.Eff.Trace

    Given a callback and request, respond to it

  • Handle Fresh r a (Int -> k)Defined in extensible-effects-5.0.0.1 · Control.Eff.Fresh

    Given a continuation and requests, respond to them

  • Alternative f => Handle NDet r a (Eff r' (f w))Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet

    Given a callback and NDet requests respond to them. Note that this makes explicit that we rely on f to have enough room to store all possibilities.

  • Alternative f => Handle NDet r a ([Eff r a] -> Eff r' (f w))Defined in extensible-effects-5.0.0.1 · Control.Eff.Logic.NDet

    More performant handler; uses reified job queue

  • Handle (State s) r a (s -> k)Defined in extensible-effects-5.0.0.1 · Control.Eff.State.Lazy

    Handle 'State s' requests

  • Handle (OnDemandState s) r a (s -> k)Defined in extensible-effects-5.0.0.1 · Control.Eff.State.OnDemand

    Given a continuation, respond to requests

  • Handle (State s) r a (s -> k)Defined in extensible-effects-5.0.0.1 · Control.Eff.State.Strict

    Handle 'State s' requests

  • Monad m => Handle (Writer w) r a (b -> (w -> b -> b) -> m (a, b))Defined in extensible-effects-5.0.0.1 · Control.Eff.Writer.Lazy

    Given a value to write, and a callback (which includes empty and append), respond to requests.

  • Monad m => Handle (Writer w) r a (b -> (w -> b -> b) -> m (a, b))Defined in extensible-effects-5.0.0.1 · Control.Eff.Writer.Strict

    Given a value to write, and a callback (which includes empty and append), respond to requests.

  • Monad m => Handle (Exc e) r a (m (Either e a))Defined in extensible-effects-5.0.0.1 · Control.Eff.Exception

    Given a callback, and an Exc request, respond to it.

  • Monad m => Handle (Lift m) r a (m k)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal

    Handle lifted requests by running them sequentially

  • Handle (Program f) r a (Intrprtr f r' -> Eff r' a)Defined in extensible-effects-5.0.0.1 · Control.Eff.Operational

    Given a continuation and a program, interpret it Usually, we have r ~ [Program f : r']

  • Handle (Reader e) r a (e -> k)Defined in extensible-effects-5.0.0.1 · Control.Eff.Reader.Lazy

    Given a value to read, and a callback, how to respond to requests.

  • Handle (Reader e) r a (e -> k)Defined in extensible-effects-5.0.0.1 · Control.Eff.Reader.Strict

    Given a value to read, and a callback, how to respond to requests.

  • Handle (Yield a b) (Yield a b ': r) w (Eff r (Y r b a))Defined in extensible-effects-5.0.0.1 · Control.Eff.Coroutine

    Given a continuation and a request, respond to it

classclass Relay k (r :: [Type -> Type]) where
#

Abstract the recursive relay pattern, i.e., "somebody else's problem".

Methods

Instances2Relay
  • Relay (Eff r w) rDefined in extensible-effects-5.0.0.1 · Control.Eff.Internal
  • Relay k r => Relay (s -> k) rDefined in extensible-effects-5.0.0.1 · Control.Eff.Internal
valuehandle_relay'
  1. :: (r ~ (t ': r'), Relay k r')
  2. => (forall v. (Eff r a -> k) -> Arrs r v a -> t v -> k)

    handler

  3. -> (a -> k)

    return

  4. -> (Eff r a -> k)

    untied recursive knot

  5. -> Eff r a
  6. -> k
#

A less commonly needed variant with an explicit handler (instead of Handle t r a k constraint).

valuerespond_relay'
  1. :: (Member t r, Relay k r)
  2. => (forall v. (Eff r a -> k) -> Arrs r v a -> t v -> k)

    handler

  3. -> (a -> k)

    return

  4. -> (Eff r a -> k)

    recursive knot

  5. -> Eff r a
  6. -> k
#

Variant with an explicit handler (instead of Handle t r a k constraint).

valueraise :: Eff r a -> Eff (e ': r) a
#

Embeds a less-constrained Eff into a more-constrained one. Analogous to MTL's lift.

valuesend :: Member t r => t v -> Eff r v
#

Send a request and wait for a reply (resulting in an effectful computation).

Arrow types and compositions

12 declarations
typetype Arr (r :: [Type -> Type]) a b = a -> Eff r b
#

Effectful arrow type: a function from a to b that also does effects denoted by r

newtypenewtype Arrs (r :: [Type -> Type]) a b
#

An effectful function from a to b that is a composition of one or more effectful functions. The paremeter r describes the overall effect.

The composition members are accumulated in a type-aligned queue. Using a newtype here enables us to define C.Category and A.Arrow instances.

Instances2Category, Arrow
  • Category (Arrs r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal

    Arrs can be composed and have a natural identity.

  • Arrow (Arrs r)Defined in extensible-effects-5.0.0.1 · Control.Eff.Internal

    As the name suggests, Arrs also has an Arrow instance.

valuesingleK :: Arr r a b -> Arrs r a b
#

convert single effectful arrow into composable type. i.e., convert Arr to Arrs

valueqApp :: Arrs r b w -> Arr r b w
#

Application to the `generalized effectful function' Arrs r b w, i.e., convert Arrs to Arr

valuearr :: (a -> b) -> Arrs r a b
#

Lift a function to an arrow

valueqComp :: Arrs r a b -> (Eff r b -> k) -> a -> k
#

Compose effectful arrows (and possibly change the effect!)

valueqComps :: Arrs r a b -> (Eff r b -> Eff r' c) -> Arrs r' a c
#

Compose effectful arrows (and possibly change the effect!)