heftia-0.7.0.0: higher-order algebraic effects done right
Copyright(c) 2024-2025 Sayo contributors
LicenseMPL-2.0 (see the LICENSE file)
Maintainerymdfield@outlook.jp
Safe HaskellNone
LanguageGHC2021

Control.Monad.Hefty

Description

Heftia is an extensible effects library that generalizes "Algebraic Effects and Handlers" to higher-order effects, providing users with maximum flexibility and delivering standard and reasonable speed. In its generalization, the focus is on ensuring predictable results based on simple, consistent semantics, while preserving soundness.

Basic Usage

The following is an example of defining, using, and interpreting the first-order effect Log for logging and the higher-order effect Span for representing named spans in a program.

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TemplateHaskell #-}

import Control.Monad.Hefty
import Prelude hiding (log, span)

data Log :: Effect where
    Log :: String -> Log f ()
makeEffectF ''Log

data Span :: Effect where
    Span :: String -> f a -> Span f a
makeEffectH ''Span

runLog :: (Emb IO :> es) => Eff (Log ': es) ~> Eff es
runLog = interpret \(Log msg) -> liftIO $ putStrLn $ "[LOG] " <> msg

runSpan :: (Emb IO :> es) => Eff (Span ': es) ~> Eff es
runSpan = interpret \(Span name m) -> do
    liftIO $ putStrLn $ "[Start span '" <> name <> "']"
    r <- m
    liftIO $ putStrLn $ "[End span '" <> name <> "']"
    pure r

prog :: IO ()
prog = runEff . runLog . runSpan $ do
    span "example program" do
        log "foo"

        span "greeting" do
            log "hello"
            log "world"

        log "bar"

>>> prog
[Start span 'example program']
[LOG] foo
[Start span 'greeting']
[LOG] hello
[LOG] world
[End span 'greeting']
[LOG] bar
[End span 'example program']

Algebraic Handler

An interpreter function that realizes features related to the continuation in algebraic effects.

It is a function that takes two arguments: an effectful operation and a continuation, which is the continuation of the computation from that operation, and returns the computation up to the end of the computation being interpreted.

By ignoring the continuation argument, it allows for global escapes like the Throw effect.

runThrow :: (FOEs es) => Eff (Throw e ': es) a -> Eff es (Either e a)
runThrow = interpretBy (pure . Right) handleThrow

handleThrow :: Applicative g => AlgHandler (Throw e) f g (Either e a)
handleThrow (Throw e) _ = pure $ Left e

Here, handleThrow is the algebraic handler for the Throw effect.

By calling the continuation argument multiple times, it allows for non-deterministic computations like the Data.Effect.NonDet effect.

runNonDet
    :: (Alternative f)
    => Eff (Choose ': Empty ': es) a
    -> Eff es (f a)
runNonDet =
    interpretsBy
        (pure . pure)
        $ (\Choose k -> liftA2 (<|>) (k False) (k True))
            !: (\Empty _ -> pure empty)
            !: nil

The function passed as the second argument to interpretBy/interpretsBy is the algebraic handler.

Additionally, what is passed as the first argument to interpretBy/interpretsBy is called a value handler. This extends the continuation in the computation being interpreted.

We shall call the state of computation that emerges through algebraic interpretation and behaves according to continuation-based semantics a "algebraic state".

Naming Rules for Interpretation Functions

  • Functions may additionally have With or By at the end of their names.

    • These provide functionality equivalent to "Algebraic Effects and Handlers," meaning they offer access to delimited continuations during interpretation.
    • Functions in the By family take two arguments: a value handler and a algebraic effect handler. They are the most generalized form.
    • Functions in the With family omit the value handler and take only the effect interpreter as an argument.
    • The difference between interpretBy ret f m and interpretWith f m >>= ret is that, during interpretation, the delimited continuation passed as the second argument k to f in the former extends up to when ret finishes, whereas in the latter, it only goes until m finishes (just before ret), so ret is not included in k.
    • Functions without With or By cannot manipulate continuations; therefore, you cannot maintain internal state or perform behaviors like global escapes or non-deterministic computations during interpretation.

Semantics of effects

Consider the following example.

data SomeEff :: Effect where
    SomeAction :: SomeEff m a
makeEffectF ''SomeEff

-- | Throws an exception when 'SomeAction' is encountered
runSomeEff :: (Throw String :> es) => Eff (SomeEff ': es) ~> Eff es
runSomeEff = interpret \SomeAction -> throw "not caught"

-- | Catches the exception if 'someAction' results in one
action :: (SomeEff :> es, Catch String :> es, Throw String :> es) => Eff es String
action = someAction `catch` \(_ :: String) -> pure "caught"

prog1 :: IO ()
prog1 = runPure . runThrow . runCatch . runSomeEff $ action

>>> prog1
Right "caught"

prog2 :: IO ()
prog2 = runPure . runThrow . runSomeEff . runCatch $ action

>>> prog2
Left "not caught"

When applying runCatch after runSomeEff in prog1, the exception is caught, but in the reverse order, it is not caught. We will now explain this behavior to understand it.

In Heftia, the behavior of higher-order effects is based on reduction semantics—that is, term rewriting semantics similar to those in "Algebraic Effects and Handlers." By properly understanding and becoming familiar with this semantics, users can quickly and easily predict execution results.

Let's revisit the definition of runCatch:

runCatch :: (Throw e `In` es, FOEs es) => Eff (Catch e ': es) ~> Eff es
runCatch = interpret handleCatch

handleCatch :: (Throw e `In` es, FOEs es) => Catch e ~~> Eff es
handleCatch (Catch action hdl) = action & interposeWith \(Throw e) _ -> hdl e

When runCatch encounters code like ... (action `catch` hdl) ... in the program, it rewrites that part to ... (interposeWith (\(Throw e) _ -> hdl e) action) .... In general, functions like interpret and interpose behave this way—they recursively rewrite the target higher-order effects according to the given handler. Rewriting proceeds from the deepest scope toward the outer scopes.

The same applies to first-order effects. Handling an effect means rewriting the effects that appear in the program.

With this in mind, let's follow the rewriting step by step.

Looking at prog1. First, when runSomeEff is applied to action:

    runSomeEff action
 =  interpret (\SomeAction -> throw "not caught") $ someAction `catch` \(_ :: String) -> pure "caught"
==> throw "not caught" `catch` \(_ :: String) -> pure "caught"

The program is rewritten into a program like the above.

Next, when runCatch is applied to this, it evaluates to:

    runCatch $ throw "not caught" `catch` \(_ :: String) -> pure "caught"
==> interposeWith (\(Throw e) _ -> pure "caught") $ throw "not caught"
==> pure "caught"

In this way, the exception is caught.

On the other hand, in prog2, when runCatch is applied to action:

    runCatch action
 =  runCatch $ someAction `catch` \(_ :: String) -> pure "caught"
==> interposeWith (\(Throw e) _ -> pure "caught") $ someAction

At this point, since there is no throw in the computation that is the target of interposeWith (only someAction appears, which is not throw!), interposeWith does nothing because there is no throw to rewrite:

==> someAction

Therefore, when runSomeEff is applied:

    runSomeEff someAction
==> throw "not caught"

Thus, the exception remains as is.

In other words, in prog2, at the point of runCatch, it is impossible for runCatch to know that someAction will later be rewritten into throw. Interpreters decide what to do based only on the current state of the program's rewriting. They do not change the result based on any other information.

This is all there is to the reduction semantics of algebraic effects.

Independence from IO Semantics

As seen in the initial example with logs and spans, IO operations are embedded as effects. Not limited to IO, any monad can be embedded as an effect.

Embedded IO can be viewed as instruction scripts, and to avoid confusion when using Heftia, it should be regarded as such. Rather than thinking "Haskell represents side effects via a type-level tag called IO", it's better to think:

  • Haskell is a purely functional language where you cannot write anything other than pure functions.
  • IO is just an opaque algebraic data type whose definition you cannot see, but no different from others.
  • The runtime system treats the value main as a sequence of instructions to be executed on the CPU.
  • Programming with side effects in Haskell is meta-programming where you write a pure function program that outputs IO-typed instruction scripts.

In fact, the semantics of effects in Heftia are completely isolated from the level of IO. Considerations at the IO level, such as "asynchronous exceptions might be thrown", "what is the current state of exception masking", or "this state/environment value is local and not shared between threads", have no influence on effect interpretation and need not be considered. IO is just a data type representing programs with side effects, and we are merely meta-programming it. The consistent semantics of algebraic effects prevent leaks of abstraction from the IO level.

This is a significant difference from IO-fused effect system libraries like effectful and cleff.

Interpreting Multiple Effects Simultaneously

For example, consider a situation where you want to use multiple Catch effects simultaneously. The following is a case where both String and Int appear as exception types:

prog :: Eff '[Catch String, Catch Int, Throw String, Throw Int] ()

In this case, you may get stuck trying to use runCatch. This is because runCatch has the following type signature:

runCatch :: (Throw e `In` es, FOEs es) => Eff (Catch e ': es) ~> Eff es

You cannot write runCatch . runCatch. It requires the higher-order effects to be exhausted after interpretation:

    runCatch . runCatch $ prog
               ^^^^^^^^

  • No instance for ‘Data.Effect.FirstOrder (Catch Int)’
      arising from a use of ‘runCatch’
  • In the second argument of ‘(.)’, namely ‘runCatch’
    In the first argument of ‘($)’, namely ‘runCatch . runCatch’
    In the expression: runCatch . runCatch $ prog

In situations like this, where you want to perform algebraic interpretation on multiple higher-order effects simultaneously, you generally cannot reduce the higher-order effect list step by step or via multi-staging. Instead, you need to interpret all of them at once simultaneously.

This is possible by interprets family and pattern matching on the open union using the !: operator.

prog' :: Eff '[Throw String, Throw Int] ()
prog' = interprets (handleCatch !: handleCatch !: nil) prog
Synopsis

Basics

data Freer (f :: Type -> Type) a Source #

Constructors

Val a

A pure value.

Op

An effectful operation.

Fields

  • (f x)
     
  • (FTCQueue (Freer f) x a)

    the continuation of the operation.

Instances

Instances details
Free Monad Freer Source # 
Instance details

Defined in Control.Monad.Hefty.Types

Methods

liftFree :: f a -> Freer f a #

runFree :: Monad g => (forall x. f x -> g x) -> Freer f a -> g a #

retract :: Monad f => Freer f a -> f a #

hoist :: (forall x. f x -> g x) -> Freer f a -> Freer g a #

Applicative (Freer f) Source # 
Instance details

Defined in Control.Monad.Hefty.Types

Methods

pure :: a -> Freer f a #

(<*>) :: Freer f (a -> b) -> Freer f a -> Freer f b #

liftA2 :: (a -> b -> c) -> Freer f a -> Freer f b -> Freer f c #

(*>) :: Freer f a -> Freer f b -> Freer f b #

(<*) :: Freer f a -> Freer f b -> Freer f a #

Functor (Freer f) Source # 
Instance details

Defined in Control.Monad.Hefty.Types

Methods

fmap :: (a -> b) -> Freer f a -> Freer f b #

(<$) :: a -> Freer f b -> Freer f a #

Monad (Freer f) Source # 
Instance details

Defined in Control.Monad.Hefty.Types

Methods

(>>=) :: Freer f a -> (a -> Freer f b) -> Freer f b #

(>>) :: Freer f a -> Freer f b -> Freer f b #

return :: a -> Freer f a #

type ($) (f :: Type -> Type) a = f a infixr 3 #

Type-level infix applcation for functors.

type ($$) (h :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) = h f infixr 4 #

Type-level infix applcation for higher-order functors.

type AlgHandler (e :: Effect) (m :: Type -> Type) (n :: Type -> Type) ans = forall x. e m x -> (x -> n ans) -> n ans Source #

type (~>) (f :: Type -> Type) (g :: Type -> Type) = forall x. f x -> g x infixr 2 #

A natural transformation.

type (~~>) (e :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) = e f ~> f infix 2 #

class FOEs (es :: [Effect]) #

The list es consists only of first-order effects.

Instances

Instances details
FOEs ('[] :: [Effect]) 
Instance details

Defined in Data.Effect.OpenUnion

(FirstOrder e, FOEs es) => FOEs (e ': es) 
Instance details

Defined in Data.Effect.OpenUnion

class FormOf e ~ 'Polynomial => PolyHFunctor (e :: Effect) #

A higher-order polynomial functor.

Instances

Instances details
PolyHFunctor Halt 
Instance details

Defined in Data.Effect.Concurrent.Parallel

PolyHFunctor Parallel 
Instance details

Defined in Data.Effect.Concurrent.Parallel

PolyHFunctor Race 
Instance details

Defined in Data.Effect.Concurrent.Parallel

PolyHFunctor CyclicTimer 
Instance details

Defined in Data.Effect.Concurrent.Timer

PolyHFunctor Timer 
Instance details

Defined in Data.Effect.Concurrent.Timer

PolyHFunctor Choose 
Instance details

Defined in Data.Effect

PolyHFunctor ChooseH 
Instance details

Defined in Data.Effect

PolyHFunctor Empty 
Instance details

Defined in Data.Effect

PolyHFunctor Fail 
Instance details

Defined in Data.Effect

PolyHFunctor Fix 
Instance details

Defined in Data.Effect

PolyHFunctor Nop 
Instance details

Defined in Data.Effect

PolyHFunctor (Accum w) 
Instance details

Defined in Data.Effect.Accum

PolyHFunctor (For t) 
Instance details

Defined in Data.Effect.Concurrent.Parallel

PolyHFunctor (Fresh i) 
Instance details

Defined in Data.Effect.Fresh

PolyHFunctor (Input i) 
Instance details

Defined in Data.Effect.Input

PolyHFunctor (Log msg) 
Instance details

Defined in Data.Effect.Log

PolyHFunctor (Output o) 
Instance details

Defined in Data.Effect.Output

PolyHFunctor (Select r) 
Instance details

Defined in Data.Effect.Select

PolyHFunctor (Ask r) 
Instance details

Defined in Data.Effect

PolyHFunctor (CC ref) 
Instance details

Defined in Data.Effect

PolyHFunctor (Catch e) 
Instance details

Defined in Data.Effect

PolyHFunctor (Emb e) 
Instance details

Defined in Data.Effect

PolyHFunctor (Local r) 
Instance details

Defined in Data.Effect

PolyHFunctor (State s) 
Instance details

Defined in Data.Effect

PolyHFunctor (Tell w) 
Instance details

Defined in Data.Effect

PolyHFunctor (Throw e) 
Instance details

Defined in Data.Effect

PolyHFunctor (WriterH w) 
Instance details

Defined in Data.Effect

PolyHFunctor (Yield a b) 
Instance details

Defined in Data.Effect.Coroutine

PolyHFunctor (KVStore k v) 
Instance details

Defined in Data.Effect.KVStore

PolyHFunctor (Shift ans ref) 
Instance details

Defined in Data.Effect.Shift

class PolyHFunctors (es :: [Effect]) #

The list es consists only of polynomial effects.

Instances

Instances details
PolyHFunctors ('[] :: [Effect]) 
Instance details

Defined in Data.Effect.OpenUnion

(PolyHFunctor e, PolyHFunctors es) => PolyHFunctors (e ': es) 
Instance details

Defined in Data.Effect.OpenUnion

type (:>) (e :: Effect) (es :: [Effect]) = MemberBy LabelResolver (Discriminator LabelResolver e) e es infix 4 #

type In (e :: Effect) (es :: [Effect]) = MemberBy IdentityResolver (IdentityDiscriminator e) e es infix 4 #

type Has (key :: k) (e :: Effect) (es :: [Effect]) = MemberBy KeyResolver (KeyDiscriminator key) (e # key) es #

type family (es :: [Effect]) ++ (es' :: [Effect]) :: [Effect] where ... infixr 5 #

Equations

('[] :: [Effect]) ++ es = es 
(e ': es) ++ es' = e ': (es ++ es') 

(!:) :: forall (f :: Type -> Type) a r (es :: [Effect]). Elem e order => (e f a -> r) -> (Union es f a -> r) -> Union (e ': es) f a -> r infixr 5 #

(!++) :: forall (es :: [Effect]) (es' :: [Effect]) (f :: Type -> Type) a r. KnownLength es => (Union es f a -> r) -> (Union es' f a -> r) -> Union (es ++ es') f a -> r infixr 5 #

nil :: forall (f :: Type -> Type) a r. Union ('[] :: [Effect]) f a -> r #

perform :: forall e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (e :> es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

perform' :: forall {k} (key :: k) e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Has key e es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

perform'' :: forall {k} (tag :: k) e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). ((e # tag) :> es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

send :: forall e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (In e es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

sendAt :: forall (i :: Nat) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownIndex i es, Free c ff) => At i es (Eff ff es) a -> Eff ff es a #

sendFor :: forall e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownOrder e, Free c ff) => Membership e es -> e (Eff ff es) a -> Eff ff es a #

emb :: forall f (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Emb f :> es, Free c ff) => f a -> Eff ff es a #

Interpreting effects

Running Eff

runEff :: forall (m :: Type -> Type). Monad m => Eff '[Emb m] ~> m Source #

Lowers the computation into a monad m by treating the effect as a monad.

runPure :: Eff ('[] :: [Effect]) a -> a Source #

Extracts the value from a computation that contains only pure values without any effect.

Standard functions

interpret :: forall (e :: Effect) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownOrder e, Free c ff) => (e ~~> Eff ff es) -> Eff ff (e ': es) a -> Eff ff es a #

interprets :: forall (es :: [Effect]) (r :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownLength es, Free c ff) => (Union es ~~> Eff ff r) -> Eff ff (es ++ r) a -> Eff ff r a #

interpretWith :: forall (e :: Effect) (es :: [Effect]) a. (KnownOrder e, FOEs es) => AlgHandler e (Eff (e ': es)) (Eff es) a -> Eff (e ': es) a -> Eff es a Source #

Interprets the effect e at the head of the list using the provided algebraic handler.

interpretBy :: forall (e :: Effect) (es :: [Effect]) ans a. (KnownOrder e, FOEs es) => (a -> Eff es ans) -> AlgHandler e (Eff (e ': es)) (Eff es) ans -> Eff (e ': es) a -> Eff es ans Source #

Interprets the effect e at the head of the list using the provided value handler and algebraic handler.

interpretsBy :: forall (es :: [Effect]) (r :: [Effect]) ans a. (FOEs r, KnownLength es) => (a -> Eff r ans) -> AlgHandler (Union es) (Eff (es ++ r)) (Eff r) ans -> Eff (es ++ r) a -> Eff r ans Source #

Reinterpretation functions

reinterpret :: forall (e :: Effect) (es :: [Effect]) (es' :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Suffix es es', KnownOrder e, Free c ff) => (e ~~> Eff ff es') -> Eff ff (e ': es) a -> Eff ff es' a #

reinterprets :: forall (es :: [Effect]) (r :: [Effect]) (r' :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Suffix r r', KnownLength es, Free c ff) => (Union es (Eff ff r') ~> Eff ff r') -> Eff ff (es ++ r) a -> Eff ff r' a #

reinterpretBy :: forall (e :: Effect) (es :: [Effect]) (es' :: [Effect]) ans a. (KnownOrder e, FOEs es, Suffix es es') => (a -> Eff es' ans) -> AlgHandler e (Eff (e ': es)) (Eff es') ans -> Eff (e ': es) a -> Eff es' ans Source #

reinterpretsBy :: forall (es :: [Effect]) (r :: [Effect]) (r' :: [Effect]) ans a. (FOEs r, Suffix r r', KnownLength es) => (a -> Eff r' ans) -> AlgHandler (Union es) (Eff (es ++ r)) (Eff r') ans -> Eff (es ++ r) a -> Eff r' ans Source #

reinterpretWith :: forall (e :: Effect) (es' :: [Effect]) (es :: [Effect]) a. (Suffix es es', KnownOrder e, FOEs es) => AlgHandler e (Eff (e ': es)) (Eff es') a -> Eff (e ': es) a -> Eff es' a Source #

Interposition functions

interpose :: forall (e :: Effect) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (e :> es, Free c ff) => (e ~~> Eff ff es) -> Eff ff es a -> Eff ff es a #

interposeOn :: forall {k} (key :: k) (e :: Effect) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Has key e es, Free c ff) => (e ~~> Eff ff es) -> Eff ff es a -> Eff ff es a #

interposeIn :: forall (e :: Effect) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (In e es, Free c ff) => (e ~~> Eff ff es) -> Eff ff es a -> Eff ff es a #

interposeBy Source #

Arguments

:: forall (e :: Effect) (es :: [Effect]) ans a. (e :> es, FOEs es) 
=> (a -> Eff es ans)

Value handler

-> AlgHandler e (Eff es) (Eff es) ans

Effect handler

-> Eff es a 
-> Eff es ans 

Reinterprets (hooks) the effect e in the list using the provided value handler and algebraic handler.

interposeOnBy Source #

Arguments

:: forall {k} (key :: k) (e :: Effect) (es :: [Effect]) ans a. (Has key e es, FOEs es) 
=> (a -> Eff es ans)

Value handler

-> AlgHandler e (Eff es) (Eff es) ans

Effect handler

-> Eff es a 
-> Eff es ans 

Reinterprets (hooks) the effect e in the list using the provided value handler and algebraic handler.

interposeInBy Source #

Arguments

:: forall (e :: Effect) (es :: [Effect]) ans a. (In e es, FOEs es) 
=> (a -> Eff es ans)

Value handler

-> AlgHandler e (Eff es) (Eff es) ans

Effect handler

-> Eff es a 
-> Eff es ans 

Reinterprets (hooks) the effect e in the list using the provided value handler and algebraic handler.

interposeWith Source #

Arguments

:: forall (e :: Effect) (es :: [Effect]) a. (e :> es, FOEs es) 
=> AlgHandler e (Eff es) (Eff es) a

Effect handler

-> Eff es a 
-> Eff es a 

Reinterprets (hooks) the effect e in the list using the provided algebraic handler.

interposeOnWith Source #

Arguments

:: forall {k} (key :: k) (e :: Effect) (es :: [Effect]) a. (Has key e es, FOEs es) 
=> AlgHandler e (Eff es) (Eff es) a

Effect handler

-> Eff es a 
-> Eff es a 

Reinterprets (hooks) the effect e in the list using the provided algebraic handler.

interposeInWith Source #

Arguments

:: forall (e :: Effect) (es :: [Effect]) a. (In e es, FOEs es) 
=> AlgHandler e (Eff es) (Eff es) a

Effect handler

-> Eff es a 
-> Eff es a 

Reinterprets (hooks) the effect e in the list using the provided algebraic handler.

interposeFor :: forall (e :: Effect) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownOrder e, Free c ff) => Membership e es -> (e ~~> Eff ff es) -> Eff ff es a -> Eff ff es a #

interposeForWith Source #

Arguments

:: forall (e :: Effect) (es :: [Effect]) a. (KnownOrder e, FOEs es) 
=> Membership e es 
-> AlgHandler e (Eff es) (Eff es) a

Effect handler

-> Eff es a 
-> Eff es a 

Reinterprets (hooks) the effect e in the list using the provided algebraic handler.

interposeForBy Source #

Arguments

:: forall (e :: Effect) (es :: [Effect]) ans a. (KnownOrder e, FOEs es) 
=> Membership e es 
-> (a -> Eff es ans)

Value handler

-> AlgHandler e (Eff es) (Eff es) ans

Effect handler

-> Eff es a 
-> Eff es ans 

Transformation to monads

iterAllEff :: forall (es :: [Effect]) f (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Free c ff, c f) => (Union es ~~> f) -> Eff ff es a -> f a #

Utilities

stateless :: forall (e :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) (n :: Type -> Type) ans. Monad n => (e m ~> n) -> AlgHandler e m n ans Source #

Lifts a stateless handler into a algebraic handler.

interpretAll :: forall (es :: [Effect]) (es' :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). Free c ff => (Union es ~~> Eff ff es') -> Eff ff es a -> Eff ff es' a #

Ad-hoc stateful interpretation

Theses entities provides an ad-hoc specialized version to accelerate interpretations that have a single state type s, especially for effects like State or Writer.

type StateHandler s (e :: k -> Type -> Type) (m :: k) (n :: Type -> Type) ans = forall x. e m x -> s -> (s -> x -> n ans) -> n ans Source #

An ad-hoc stateful version of Handler for performance.

Interpretation functions

interpretStateBy :: forall s (e :: Effect) (es :: [Effect]) ans a. (KnownOrder e, FOEs es) => s -> (s -> a -> Eff es ans) -> StateHandler s e (Eff (e ': es)) (Eff es) ans -> Eff (e ': es) a -> Eff es ans Source #

reinterpretStateBy :: forall s (e :: Effect) (es' :: [Effect]) (es :: [Effect]) ans a. (Suffix es es', KnownOrder e, FOEs es) => s -> (s -> a -> Eff es' ans) -> StateHandler s e (Eff (e ': es)) (Eff es') ans -> Eff (e ': es) a -> Eff es' ans Source #

Interposition functions

interposeStateBy :: forall s (e :: Effect) (es :: [Effect]) ans a. (e :> es, FOEs es) => s -> (s -> a -> Eff es ans) -> StateHandler s e (Eff es) (Eff es) ans -> Eff es a -> Eff es ans Source #

interposeStateInBy :: forall s (e :: Effect) (es :: [Effect]) ans a. (In e es, FOEs es) => s -> (s -> a -> Eff es ans) -> StateHandler s e (Eff es) (Eff es) ans -> Eff es a -> Eff es ans Source #

interposeStateForBy :: forall s (e :: Effect) (es :: [Effect]) ans a. (KnownOrder e, FOEs es) => Membership e es -> s -> (s -> a -> Eff es ans) -> StateHandler s e (Eff es) (Eff es) ans -> Eff es a -> Eff es ans Source #

Transforming effects

Rewriting effectful operations

transform :: forall (e :: Effect) (e' :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, KnownOrder e', Free c ff) => (e (Eff ff (e' ': es)) ~> e' (Eff ff (e' ': es))) -> Eff ff (e ': es) a -> Eff ff (e' ': es) a #

translate :: forall (e :: Effect) (e' :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, e' :> es, Free c ff) => (e (Eff ff es) ~> e' (Eff ff es)) -> Eff ff (e ': es) a -> Eff ff es a #

translateOn :: forall {k} (key :: k) (e :: Effect) (e' :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, Has key e' es, Free c ff) => (e (Eff ff es) ~> e' (Eff ff es)) -> Eff ff (e ': es) a -> Eff ff es a #

translateIn :: forall (e :: Effect) (e' :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, In e' es, Free c ff) => (e (Eff ff es) ~> e' (Eff ff es)) -> Eff ff (e ': es) a -> Eff ff es a #

translateFor :: forall (e :: Effect) (e' :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, KnownOrder e', Free c ff) => Membership e' es -> (e (Eff ff es) ~> e' (Eff ff es)) -> Eff ff (e ': es) a -> Eff ff es a #

rewrite :: forall (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (e :> es, Free c ff) => (e (Eff ff es) ~> e (Eff ff es)) -> Eff ff es a -> Eff ff es a #

rewriteOn :: forall {k} (key :: k) (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (Has key e es, Free c ff) => (e (Eff ff es) ~> e (Eff ff es)) -> Eff ff es a -> Eff ff es a #

rewriteIn :: forall (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (In e es, Free c ff) => (e (Eff ff es) ~> e (Eff ff es)) -> Eff ff es a -> Eff ff es a #

rewriteFor :: forall (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, Free c ff) => Membership e es -> (e (Eff ff es) ~> e (Eff ff es)) -> Eff ff es a -> Eff ff es a #

Manipulating the effect list (without rewriting effectful operations)

Insertion functions

raise :: forall (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). Free c ff => Eff ff es a -> Eff ff (e ': es) a #

raises :: forall (es :: [Effect]) (es' :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (Suffix es es', Free c ff) => Eff ff es a -> Eff ff es' a #

raisesUnder :: forall (e :: Effect) (es :: [Effect]) (es' :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (Suffix es es', Free c ff) => Eff ff (e ': es) a -> Eff ff (e ': es') a #

raiseUnder :: forall (e0 :: Effect) (e1 :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). Free c ff => Eff ff (e0 ': es) a -> Eff ff (e0 ': (e1 ': es)) a #

class Suffix (es :: [Effect]) (es' :: [Effect]) #

Minimal complete definition

prefixLen

Instances

Instances details
Suffix es es 
Instance details

Defined in Data.Effect.OpenUnion

Methods

prefixLen :: Int #

Suffix es es' => Suffix es (e ': es') 
Instance details

Defined in Data.Effect.OpenUnion

Methods

prefixLen :: Int #

class SuffixUnder (es :: [Effect]) (es' :: [Effect]) #

Minimal complete definition

prefixLenUnder, offset

Instances

Instances details
Suffix es es' => SuffixUnder es es' 
Instance details

Defined in Data.Effect.OpenUnion

SuffixUnder es es' => SuffixUnder (e ': es) (e ': es') 
Instance details

Defined in Data.Effect.OpenUnion

onlyFOEs :: forall (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (Free c ff, WeakenHOEs es) => Eff ff (RemoveHOEs es) a -> Eff ff es a #

type WeakenHOEs (es :: [Effect]) = (WeakenHOEs_ es 0 (OrderOf (HeadOf es)), FOEs (RemoveHOEs es)) #

type family RemoveHOEs (es :: [Effect]) :: [Effect] where ... #

Equations

RemoveHOEs ('[] :: [Effect]) = '[] :: [Effect] 
RemoveHOEs (e ': es) = OrderCase (OrderOf e) (e ': RemoveHOEs es) (RemoveHOEs es) 

onlyPolys :: forall (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (Free c ff, WeakenExps es) => Eff ff (RemoveExps es) a -> Eff ff es a #

type WeakenExps (es :: [Effect]) = (WeakenExps_ es 0 (FormOf (HeadOf es)), PolyHFunctors (RemoveExps es)) #

type family RemoveExps (es :: [Effect]) :: [Effect] where ... #

Equations

RemoveExps ('[] :: [Effect]) = '[] :: [Effect] 
RemoveExps (e ': es) = FormCase (FormOf e) (e ': RemoveExps es) (RemoveExps es) 

raisePrefix :: forall (es' :: [Effect]) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownLength es', Free c ff) => Eff ff es a -> Eff ff (es' ++ es) a #

raiseSuffix :: forall (es' :: [Effect]) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). Free c ff => Eff ff es a -> Eff ff (es ++ es') a #

raisePrefix1 :: forall {k} (fs :: [k -> Effect]) (x :: k) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownLength fs, Free c ff) => Eff ff es a -> Eff ff (Each fs x ++ es) a #

subsume :: forall (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (In e es, Free c ff) => Eff ff (e ': es) a -> Eff ff es a #

subsumeUnder :: forall (e1 :: Effect) (e0 :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (In e1 es, KnownOrder e0, Free c ff) => Eff ff (e0 ': (e1 ': es)) a -> Eff ff (e0 ': es) a #

Manipulating Tags

tag :: forall {k} (tag :: k) (e :: Effect) (es :: [Effect]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, KnownOrder (e # tag), Free c ff) => Eff ff (e ': es) a -> Eff ff ((e # tag) ': es) a #

untag :: forall {k} (tag :: k) (e :: Effect) (es :: [(Type -> Type) -> Type -> Type]) a (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (KnownOrder e, KnownOrder (e # tag), Free c ff) => Eff ff ((e # tag) ': es) a -> Eff ff (e ': es) a #

Misc

type KnownOrder (e :: Effect) = Elem e (OrderOf e) #

type Type = TYPE LiftedRep #

The kind of types with lifted values. For example Int :: Type.

liftIO :: MonadIO m => IO a -> m a #

Lift a computation from the IO monad. This allows us to run IO computations in any monadic stack, so long as it supports these kinds of operations (i.e. IO is the base monad for the stack).

Example

Expand
import Control.Monad.Trans.State -- from the "transformers" library

printState :: Show s => StateT s IO ()
printState = do
  state <- get
  liftIO $ print state

Had we omitted liftIO, we would have ended up with this error:

• Couldn't match type ‘IO’ with ‘StateT s IO’
 Expected type: StateT s IO ()
   Actual type: IO ()

The important part here is the mismatch between StateT s IO () and IO ().

Luckily, we know of a function that takes an IO a and returns an (m a): liftIO, enabling us to run the program and see the expected results:

> evalStateT printState "hello"
"hello"

> evalStateT printState 3
3

type ($) (f :: Type -> Type) a = f a infixr 3 #

Type-level infix applcation for functors.

pass :: forall w a (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (Tell w :> es, WriterH w :> es, Monad (Eff ff es), Free c ff) => Eff ff es (w -> w, a) -> Eff ff es a #

For a given scope, uses the function (the first component of the pair returned by that scope) to modify the accumulated value of that scope, and then accumulates the result into the current outer scope.

pass m = do
    (w, (f, a)) <- listen m
    tell $ f w
    pure a

sub :: forall ref a b (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (CC ref :> es, Monad (Eff ff es), Free c ff) => (ref a -> Eff ff es b) -> (a -> Eff ff es b) -> Eff ff es b #

callCC_ :: forall (ref :: Type -> Type) a b (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) (c :: (Type -> Type) -> Constraint). (CC ref :> es, Monad (Eff ff es), Free c ff) => ((a -> Eff ff es b) -> Eff ff es a) -> Eff ff es a #

class (forall (f :: Type -> Type). c (ff f)) => Free (c :: (Type -> Type) -> Constraint) (ff :: (Type -> Type) -> Type -> Type) | ff -> c where #

Minimal complete definition

liftFree, (runFree | retract, hoist)

Methods

liftFree :: f a -> ff f a #

runFree :: c g => (forall x. f x -> g x) -> ff f a -> g a #

retract :: c f => ff f a -> f a #

hoist :: (forall x. f x -> g x) -> ff f a -> ff g a #

default hoist :: c (ff g) => (forall x. f x -> g x) -> ff f a -> ff g a #

Instances

Instances details
Free Alternative Alt 
Instance details

Defined in Control.Effect

Methods

liftFree :: f a -> Alt f a #

runFree :: Alternative g => (forall x. f x -> g x) -> Alt f a -> g a #

retract :: Alternative f => Alt f a -> f a #

hoist :: (forall x. f x -> g x) -> Alt f a -> Alt g a #

Free Alternative Alt 
Instance details

Defined in Control.Effect

Methods

liftFree :: f a -> Alt f a #

runFree :: Alternative g => (forall x. f x -> g x) -> Alt f a -> g a #

retract :: Alternative f => Alt f a -> f a #

hoist :: (forall x. f x -> g x) -> Alt f a -> Alt g a #

Free Applicative Ap 
Instance details

Defined in Control.Effect

Methods

liftFree :: f a -> Ap f a #

runFree :: Applicative g => (forall x. f x -> g x) -> Ap f a -> g a #

retract :: Applicative f => Ap f a -> f a #

hoist :: (forall x. f x -> g x) -> Ap f a -> Ap g a #

Free Applicative Ap 
Instance details

Defined in Control.Effect

Methods

liftFree :: f a -> Ap f a #

runFree :: Applicative g => (forall x. f x -> g x) -> Ap f a -> g a #

retract :: Applicative f => Ap f a -> f a #

hoist :: (forall x. f x -> g x) -> Ap f a -> Ap g a #

Free Applicative Ap 
Instance details

Defined in Control.Effect

Methods

liftFree :: f a -> Ap f a #

runFree :: Applicative g => (forall x. f x -> g x) -> Ap f a -> g a #

retract :: Applicative f => Ap f a -> f a #

hoist :: (forall x. f x -> g x) -> Ap f a -> Ap g a #

Free Functor Coyoneda 
Instance details

Defined in Control.Effect

Methods

liftFree :: f a -> Coyoneda f a #

runFree :: Functor g => (forall x. f x -> g x) -> Coyoneda f a -> g a #

retract :: Functor f => Coyoneda f a -> f a #

hoist :: (forall x. f x -> g x) -> Coyoneda f a -> Coyoneda g a #

Free Monad Freer Source # 
Instance details

Defined in Control.Monad.Hefty.Types

Methods

liftFree :: f a -> Freer f a #

runFree :: Monad g => (forall x. f x -> g x) -> Freer f a -> g a #

retract :: Monad f => Freer f a -> f a #

hoist :: (forall x. f x -> g x) -> Freer f a -> Freer g a #

type (~>) (f :: Type -> Type) (g :: Type -> Type) = forall x. f x -> g x infixr 2 #

A natural transformation.

type (~~>) (e :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) = e f ~> f infix 2 #

type ($$) (h :: (Type -> Type) -> Type -> Type) (f :: Type -> Type) = h f infixr 4 #

Type-level infix applcation for higher-order functors.

unEff :: Eff ff es a -> ff (Union es (Eff ff es)) a #

perform :: forall e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (e :> es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

perform' :: forall {k} (key :: k) e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Has key e es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

perform'' :: forall {k} (tag :: k) e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). ((e # tag) :> es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

send :: forall e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (In e es, Free c ff) => e (Eff ff es) a -> Eff ff es a #

sendAt :: forall (i :: Nat) (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownIndex i es, Free c ff) => At i es (Eff ff es) a -> Eff ff es a #

sendFor :: forall e (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (KnownOrder e, Free c ff) => Membership e es -> e (Eff ff es) a -> Eff ff es a #

emb :: forall f (es :: [Effect]) (ff :: (Type -> Type) -> Type -> Type) a (c :: (Type -> Type) -> Constraint). (Emb f :> es, Free c ff) => f a -> Eff ff es a #

convertEff :: forall (ff :: (Type -> Type) -> Type -> Type) (gg :: (Type -> Type) -> Type -> Type) (es :: [Effect]) a (c :: (Type -> Type) -> Constraint) (c' :: (Type -> Type) -> Constraint). (Free c ff, Free c' gg, forall (r :: Type -> Type). c (gg r)) => Eff ff es a -> Eff gg es a #

convertFree :: forall (c :: (Type -> Type) -> Constraint) ff (c' :: (Type -> Type) -> Constraint) gg (r :: Type -> Type) a. (Free c ff, Free c' gg, c (gg r)) => ff r a -> gg r a #