| Safe Haskell | Safe-Inferred | 
|---|---|
| Language | Haskell2010 | 
Bluefin.Algae
Description
Algebraic effects and named handlers
Algebraic effect handlers are a powerful framework for user-defined effects with a simple equational intuition.
Algebraic effect handlers are expressive enough to define various effects
 from scratch. In comparison, the runState handler from
 Bluefin.State requires mutable references (IORef), relying on IO's
 built-in statefulness. In terms of pure expressiveness, delimited
 continuations are all you need.
An "algebraic effect" is a signature for a set of operations which we
 represent with a GADT. For example, the "state effect" State s contains
 two operations: Get takes no parameter and returns a value of type s,
 and Put takes a value of type s and returns (). The constructors
 Get and Put are "uninterpreted operations": they only describe the types
 of arguments and results, with no intrinsic meaning.
dataStates r where Get ::States s Put :: s ->States ()
Below is an example of a stateful computation: a term of some type Eff zz ah ::  in scope (Handler (State s) zz :> zz).
 The State operations can be called using call and the state handler h.
-- Increment a counter and return its previous value. incr :: z :> zz =>Handler(StateInt) z ->Effzz Int incr h = do n <- get put (n + 1) pure n where get =callh Get put s =callh (Put s)
We handle the state effect by giving an interpretation of the Get and Put
 operations, as a function f :: .HandlerBody (State s) zz a
To call Get or Put is to call the function f supplied by handle.
 The following equations show how handle propagates an interpretation
 f throughout a computation that calls Get and Put:
handlef (\h ->callh Get >>= k h) = f Get (handlef (\h -> k h))handlef (\h ->callh (Put s) >>= k h) = f (Put s) (handlef (\h -> k h))handlef (\h -> pure r) = pure r
With those equations, handle fincr simplifies to:
handle f incr =
  f Get \n ->
  f (Put (n+1)) \() ->
  pure n
References
- Handling Algebraic Effects (2013) by Gordon D. Plotkin and Matija Pretnar.
- First-class names for effect handlers (2021) by Ningning Xie, Youyou Cong, and Daan Leijen.
Synopsis
- type AEffect = Type -> Type
- type HandlerBody f ss a = forall x. f x -> (x -> Eff ss a) -> Eff ss a
- data Handler f s
- type ScopedEff f ss a = forall s. Handler f s -> Eff (s :& ss) a
- handle :: HandlerBody f ss a -> ScopedEff f ss a -> Eff ss a
- call :: s :> ss => Handler f s -> f a -> Eff ss a
- type HandlerBody' f ss a = forall ss0 x. f x -> Continuation ss0 ss x a -> Eff ss a
- handle' :: HandlerBody' f ss a -> ScopedEff f ss a -> Eff ss a
- continue :: Continuation t s b a -> b -> Eff s a
- cancel :: Continuation t s b a -> Eff s ()
Documentation
Simple interface
type HandlerBody f ss a = forall x. f x -> (x -> Eff ss a) -> Eff ss a Source #
Interpretation of an algebraic effect f: a function to handle the operations of f.
handle :: HandlerBody f ss a -> ScopedEff f ss a -> Eff ss a Source #
Handle operations of f.
Warning for exception-like effects
If the handler might not call the continuation (like for an exception effect), and
 if the handled computation manages resources (e.g., opening files, transactions)
 prefer handle' to trigger resource clean up with cancellable continuations.
call :: s :> ss => Handler f s -> f a -> Eff ss a Source #
Call an operation of algebraic effect f with a handler.
Cancellable continuations
Cancellable continuations are useful to work with resource-management schemes
 with exception handlers such as bracket
Cancellable continuations should be called exactly once (via continue or cancel):
- at least once to ensure resources are eventually freed (no leaks);
- at most once to avoid use-after-free errors.
Enforcing this requirement with linear types would be a welcome contribution.
Example
Problem
Given bracket and a Fail effect,
 the simple handle may cause resource leaks:
handle(\_e _k -> pure Nothing) (bracketex acquire release (\_ ->callh Fail))
bracket is intended to ensure that the acquired resource is
 released even if the bracketed function throws an exception. However, when
 the Fail operation is called, the handler (\_e _k -> pure Nothing)
 discards the continuation _k which contains the exception handler
 installed by bracket.
 The resource leaks because release will never be called.
Solution
Using handle' instead lets us cancel the continuation.
handle'(\_e k ->cancelk >> pure Nothing) (bracketacquire release (\_ ->callh Fail))
type HandlerBody' f ss a = forall ss0 x. f x -> Continuation ss0 ss x a -> Eff ss a Source #
Generalization of HandlerBody with cancellable continuations.
handle' :: HandlerBody' f ss a -> ScopedEff f ss a -> Eff ss a Source #
Generalization of handle with cancellable continuations.
continue :: Continuation t s b a -> b -> Eff s a Source #
Resume a cancellable continuation with a result.
In other words, this converts a cancellable continuation to a simple continuation.
cancel :: Continuation t s b a -> Eff s () Source #
Cancel a continuation: resume by throwing a scoped exception and catch it.
The continuation SHOULD re-throw unknown exceptions. (That is guaranteed if you don't use Exception.Dynamic.)