HORIZON HASKELLDocslts/ghc-10.0.x8c2f8e82026-10-19Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · 8c2f8e8 · 2026-10-19

Modulefused-effects-1.1.2.7Haskell2010

Control.Algebra

The Algebra class is the mechanism with which effects are interpreted.

An instance of the Algebra class defines an interpretation of an effect signature atop a given monad.

  • 3 types
  • 1 class
  • 4 values
classclass Monad m => Algebra (sig :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) | m -> sig where
#

The class of carriers (results) for algebras (effect handlers) over signatures (effects), whose actions are given by the alg method.

Methods

  • alg :: Functor ctx => Handler ctx n m -> sig n a -> ctx () -> m (ctx a)

    Interpret an effect, running any nested actions using a Handler starting from an initial state in ctx.

    Instances receive a signature of effects containing actions in n which can be lowered to m using the passed Handler and initial context. Continuations in n can be handled after mapping into contexts returned from previous actions.

    For example, considering the Algebra instance for Either e:

    instance Algebra (Error e) (Either e) where
      alg hdl sig ctx = case sig of
        L (Throw e)   -> Left e
        R (Catch m h) -> either (hdl . (<$ ctx) . h) pure (hdl (m <$ ctx))

    The Catch case holds actions m :: n x and h :: e -> n x (for some existentially-quantified type x), and a continuation k :: x -> n a. The algebra must return m (ctx a), so we have to ultimately use and lower the continuation in order to produce that type. The continuation takes an x, which we can get from either of the actions, after lowering them to values in Either e.

    To that end, the algebra lifts both the action m and the result of the error handler h into the initial context ctx before lowering them with hdl. The continuation k is fmaped into the resulting context and then itself lowered with hdl.

    By contrast, the Throw case can simply return a value in Left, since there is no continuation to call—it represents an exceptional return—and Left e :: forall a . Either e a (i.e. Left is polymorphic in a).

    Instances for monad transformers will most likely handle a signature containing multiple effects, with the tail of the signature handled by whatever monad the transformer wraps. In these cases, the tail of the signature can be delegated most conveniently using thread; see the Algebra instances for transformers types such as ReaderT and ExceptT for details.

Instances53Algebra, …
valuethread
  1. :: (Functor ctx1, Functor ctx2, Algebra sig m)
  2. => Handler (Compose ctx1 ctx2) n m
  3. -> sig n a
  4. -> ctx1 (ctx2 ())
  5. -> m (ctx1 (ctx2 a))
#

Thread a composed handler and input state through the algebra for some underlying signature.

valuerun :: Identity a -> a
#

Run an action exhausted of effects to produce its final result value.

typetype Has (eff :: (Type -> Type) -> Type -> Type) (sig :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) = (Members eff sig, Algebra sig m)
#

m is a carrier for sig containing eff.

Note that if eff is a sum, it will be decomposed into multiple Member constraints. While this technically allows one to combine multiple unrelated effects into a single Has constraint, doing so has two significant drawbacks:

  1. Due to a problem with recursive type families, this can lead to significantly slower compiles.

  2. It defeats ghc’s warnings for redundant constraints, and thus can lead to a proliferation of redundant constraints as code is changed.

valuesend :: (Member eff sig, Algebra sig m) => eff m a -> m a
#

Construct a request for an effect to be interpreted by some handler later on.

Re-exports

3 declarations
typetype Handler (ctx :: Type -> Type) (m :: Type -> Type) (n :: Type -> Type) = forall x. ctx (m x) -> n (ctx x)
#

Handlers take an action in m bundled up with some state in some context functor ctx, and return an action in n producing a derived state in ctx.

These are expected to be well-behaved distributive laws, and are required to adhere to the following laws:

handler . fmap pure = pure
handler . fmap (k =<<) = handler . fmap k <=< handler

respectively expressing that the handler does not alter the context of pure computations, and that the handler distributes over monadic composition.

Handlers compose with handlers, using e.g. Data.Functor.Compose.Compose to ensure that the result is itself well-typed as a Handler:

fmap Compose . handler1 . fmap handler2 . getCompose

and with monad homomorphisms on the left and right:

hom . handler
handler . fmap hom
datadata (:+:) (f :: (Type -> Type) -> Type -> Type) (g :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) k
#

Higher-order sums are used to combine multiple effects into a signature, typically by chaining on the right.

Constructors

  • L (f m k)
  • R (g m k)
Instances54LabelledMember, Member, Algebra, Functor, Foldable, Traversable, …