Copyright | (c) 2021 Xy Ren |
---|---|
License | BSD3 |
Maintainer | xy.r@outlook.com |
Stability | unstable |
Portability | non-portable (GHC only) |
Safe Haskell | None |
Language | Haskell2010 |
This module contains most functions for interacting with the effect system. Most of the times you won't need to import this directly; the module Cleff reexports the majority of the functionalities. If you want operations more flexible than these, see Cleff.Internal.Env.
This is an internal module and its API may change even between minor versions. Therefore you should be extra careful if you're to depend on this module.
Synopsis
- alter :: forall es es'. (Env es' -> Env es) -> Eff es ~> Eff es'
- adjust :: forall es es'. (Stack es' -> Stack es) -> Eff es ~> Eff es'
- send :: e :> es => e (Eff es) ~> Eff es
- sendVia :: e :> es' => (Eff es ~> Eff es') -> e (Eff es) ~> Eff es'
- raise :: forall e es. Eff es ~> Eff (e ': es)
- raiseN :: forall es' es. KnownList es' => Eff es ~> Eff (es' ++ es)
- inject :: forall es' es. Subset es' es => Eff es' ~> Eff es
- subsume :: forall e es. e :> es => Eff (e ': es) ~> Eff es
- subsumeN :: forall es' es. Subset es' es => Eff (es' ++ es) ~> Eff es
- raiseUnder :: forall e' e es. Eff (e ': es) ~> Eff (e ': (e' ': es))
- raiseNUnder :: forall es' e es. KnownList es' => Eff (e ': es) ~> Eff (e ': (es' ++ es))
- raiseUnderN :: forall e es' es. KnownList es' => Eff (es' ++ es) ~> Eff (es' ++ (e ': es))
- raiseNUnderN :: forall es'' es' es. (KnownList es', KnownList es'') => Eff (es' ++ es) ~> Eff (es' ++ (es'' ++ es))
- type Handler e es = forall esSend. Handling esSend e es => e (Eff esSend) ~> Eff es
- interpret :: forall e es. Handler e es -> Eff (e ': es) ~> Eff es
- reinterpret :: forall e' e es. Handler e (e' ': es) -> Eff (e ': es) ~> Eff (e' ': es)
- reinterpret2 :: forall e' e'' e es. Handler e (e' ': (e'' ': es)) -> Eff (e ': es) ~> Eff (e' ': (e'' ': es))
- reinterpret3 :: forall e' e'' e''' e es. Handler e (e' ': (e'' ': (e''' ': es))) -> Eff (e ': es) ~> Eff (e' ': (e'' ': (e''' ': es)))
- reinterpretN :: forall es' e es. KnownList es' => Handler e (es' ++ es) -> Eff (e ': es) ~> Eff (es' ++ es)
- interpose :: forall e es. e :> es => Handler e es -> Eff es ~> Eff es
- impose :: forall e' e es. e :> es => Handler e (e' ': es) -> Eff es ~> Eff (e' ': es)
- imposeN :: forall es' e es. (KnownList es', e :> es) => Handler e (es' ++ es) -> Eff es ~> Eff (es' ++ es)
- type Translator e e' = forall esSend. e (Eff esSend) ~> e' (Eff esSend)
- transform :: forall e e' es. e' :> es => Translator e e' -> Eff (e ': es) ~> Eff es
- translate :: forall e e' es. Translator e e' -> Eff (e ': es) ~> Eff (e' ': es)
- class Handling esSend e es | esSend -> e es
- esSend :: Handling esSend e es => Env esSend
- toEff :: Handling esSend e es => Eff esSend ~> Eff es
- toEffWith :: forall esSend e es. Handling esSend e es => Handler e es -> Eff esSend ~> Eff es
- withFromEff :: Handling esSend e es => ((Eff es ~> Eff esSend) -> Eff esSend a) -> Eff es a
General transformation
adjust :: forall es es'. (Stack es' -> Stack es) -> Eff es ~> Eff es' Source #
A specialized version of alter
that only adjusts the effect stack.
Performing operations
send :: e :> es => e (Eff es) ~> Eff es Source #
Perform an effect operation, i.e. a value of an effect type e ::
. This requires Effect
e
to be in the
effect stack.
Trivial handling
raise :: forall e es. Eff es ~> Eff (e ': es) Source #
Lift a computation into a bigger effect stack with one more effect. For a more general version see raiseN
.
raiseN :: forall es' es. KnownList es' => Eff es ~> Eff (es' ++ es) Source #
Lift a computation into a bigger effect stack with arbitrarily more effects. This function requires
TypeApplications
.
inject :: forall es' es. Subset es' es => Eff es' ~> Eff es Source #
Lift a computation with a fixed, known effect stack into some superset of the stack.
subsume :: forall e es. e :> es => Eff (e ': es) ~> Eff es Source #
Eliminate a duplicate effect from the top of the effect stack. For a more general version see subsumeN
.
subsumeN :: forall es' es. Subset es' es => Eff (es' ++ es) ~> Eff es Source #
Eliminate several duplicate effects from the top of the effect stack. This function requires TypeApplications
.
raiseUnder :: forall e' e es. Eff (e ': es) ~> Eff (e ': (e' ': es)) Source #
Like raise
, but adds the new effect under the top effect. This is useful for transforming an interpreter
e'
into a reinterpreter :>
es => Eff
(e : es) ~>
Eff
es
:Eff
(e : es) ~>
Eff
(e' : es)
myInterpreter :: Bar:>
es =>Eff
(Foo : es)~>
Eff
es myInterpreter = ... myReinterpreter ::Eff
(Foo : es)~>
Eff
(Bar : es) myReinterpreter = myInterpreter.
raiseUnder
In other words,
reinterpret
h ==interpret
h .raiseUnder
However, note that this function is suited for transforming an existing interpreter into a reinterpreter; if you
want to define a reinterpreter from scratch, you should still prefer reinterpret
, which is both easier to use and
more efficient.
Since: 0.2.0.0
raiseNUnder :: forall es' e es. KnownList es' => Eff (e ': es) ~> Eff (e ': (es' ++ es)) Source #
Like raiseUnder
, but allows introducing multiple effects. This function requires TypeApplications
.
Since: 0.2.0.0
raiseUnderN :: forall e es' es. KnownList es' => Eff (es' ++ es) ~> Eff (es' ++ (e ': es)) Source #
Like raiseUnder
, but allows introducing the effect under multiple effects. This function requires
TypeApplications
.
Since: 0.2.0.0
raiseNUnderN :: forall es'' es' es. (KnownList es', KnownList es'') => Eff (es' ++ es) ~> Eff (es' ++ (es'' ++ es)) Source #
A generalization of both raiseUnderN
and raiseNUnder
, allowing introducing multiple effects under multiple
effects. This function requires TypeApplications
and is subject to serious type ambiguity; you most likely will
need to supply all three type variables explicitly.
Since: 0.2.0.0
Interpreting effects
type Handler e es = forall esSend. Handling esSend e es => e (Eff esSend) ~> Eff es Source #
The type of an effect handler, which is a function that transforms an effect e
from an arbitrary effect stack
into computations in the effect stack es
.
interpret :: forall e es. Handler e es -> Eff (e ': es) ~> Eff es Source #
Interpret an effect e
in terms of effects in the effect stack es
with an effect handler.
reinterpret :: forall e' e es. Handler e (e' ': es) -> Eff (e ': es) ~> Eff (e' ': es) Source #
Like interpret
, but adds a new effect e'
to the stack that can be used in the handler.
reinterpret2 :: forall e' e'' e es. Handler e (e' ': (e'' ': es)) -> Eff (e ': es) ~> Eff (e' ': (e'' ': es)) Source #
Like reinterpret
, but adds two new effects.
reinterpret3 :: forall e' e'' e''' e es. Handler e (e' ': (e'' ': (e''' ': es))) -> Eff (e ': es) ~> Eff (e' ': (e'' ': (e''' ': es))) Source #
Like reinterpret
, but adds three new effects.
reinterpretN :: forall es' e es. KnownList es' => Handler e (es' ++ es) -> Eff (e ': es) ~> Eff (es' ++ es) Source #
Like reinterpret
, but adds arbitrarily many new effects. This function requires TypeApplications
.
interpose :: forall e es. e :> es => Handler e es -> Eff es ~> Eff es Source #
Respond to an effect, but does not eliminate it from the stack. This means you can re-send the operations in the effect handler; it is often useful when you need to "intercept" operations so you can add extra behaviors like logging.
impose :: forall e' e es. e :> es => Handler e (e' ': es) -> Eff es ~> Eff (e' ': es) Source #
Like interpose
, but allows to introduce one new effect to use in the handler.
imposeN :: forall es' e es. (KnownList es', e :> es) => Handler e (es' ++ es) -> Eff es ~> Eff (es' ++ es) Source #
Like impose
, but allows introducing arbitrarily many effects. This requires TypeApplications
.
Translating effects
type Translator e e' = forall esSend. e (Eff esSend) ~> e' (Eff esSend) Source #
The type of a simple transformation function from effect e
to e'
.
Combinators for interpreting higher effects
class Handling esSend e es | esSend -> e es Source #
The typeclass that denotes a handler scope, handling effect e
sent from the effect stack esSend
in the
effect stack es
.
You should not define instances for this typeclass whatsoever.
toEff :: Handling esSend e es => Eff esSend ~> Eff es Source #
Run a computation in the current effect stack; this is useful for interpreting higher-order effects. For example,
if you want to interpret a bracketing effects in terms of IO
:
data Resource m a where Bracket :: m a -> (a -> m ()) -> (a -> m b) -> Resource m b
You will not be able to simply write this for the effect:
runBracket :: IOE:>
es =>Eff
(Resource : es) a ->Eff
es a runBracket =interpret
\case Bracket alloc dealloc use -> UnliftIO.bracket
alloc dealloc use
This is because effects are sended from all kinds of stacks that has Resource
in it, so effect handlers received
the effect as Resource esSend a
, where esSend
is an arbitrary stack with Resource
, instead of
Resource es a
. This means alloc
, dealloc
and use
are of type
, while Eff
esSend abracket
can
only take and return
. So we need to use Eff
es atoEff
, which converts an
into
an Eff
esSend a
:Eff
es a
runBracket :: IOE:>
es =>Eff
(Resource : es) a ->Eff
es a runBracket =interpret
\case Bracket alloc dealloc use -> UnliftIO.bracket
(toEff
alloc) (toEff
. dealloc) (toEff
. use)
toEffWith :: forall esSend e es. Handling esSend e es => Handler e es -> Eff esSend ~> Eff es Source #
Run a computation in the current effect stack, just like toEff
, but takes a Handler
of the current effect
being interpreted, so that inside the computation being ran, the effect is interpreted differently. This is useful
for interpreting effects with local contexts, like Local
:
runReader :: r ->Eff
(Reader
r : es)~>
Eff
es runReader x =interpret
(handle x) where handle :: r ->Handler
(Reader
r) es handle r = \caseAsk
->pure
rLocal
f m ->toEffWith
(handle $ f r) m