fused-effects
A fast, flexible, fused effect system.
https://github.com/fused-effects/fused-effects
| LTS Haskell 24.16: | 1.1.2.5 |
| Stackage Nightly 2025-10-24: | 1.1.2.5 |
| Latest on Hackage: | 1.1.2.5 |
fused-effects-1.1.2.5@sha256:5bc272ab44bf95c7f2e1f6338823eb5947f17b3a3116f969862b3d7e6c68c9a6,5782Module documentation for 1.1.2.5
- Control
- Control.Algebra
- Control.Carrier
- Control.Carrier.Accum
- Control.Carrier.Choose
- Control.Carrier.Cull
- Control.Carrier.Cut
- Control.Carrier.Empty
- Control.Carrier.Error
- Control.Carrier.Fail
- Control.Carrier.Fresh
- Control.Carrier.Interpret
- Control.Carrier.Lift
- Control.Carrier.NonDet
- Control.Carrier.Reader
- Control.Carrier.State
- Control.Carrier.Throw
- Control.Carrier.Trace
- Control.Carrier.Writer
- Control.Effect
- Control.Effect.Accum
- Control.Effect.Catch
- Control.Effect.Choose
- Control.Effect.Cull
- Control.Effect.Cut
- Control.Effect.Empty
- Control.Effect.Error
- Control.Effect.Fail
- Control.Effect.Fresh
- Control.Effect.Labelled
- Control.Effect.Lift
- Control.Effect.NonDet
- Control.Effect.Reader
- Control.Effect.State
- Control.Effect.Sum
- Control.Effect.Throw
- Control.Effect.Trace
- Control.Effect.Writer
A fast, flexible, fused effect system for Haskell
Overview
fused-effects is an effect system for Haskell that values expressivity, efficiency, and rigor. It provides an encoding of algebraic, higher-order effects, includes a library of the most common effects, and generates efficient code by fusing effect handlers through computations. It is suitable for use in hobbyist, research, and industrial contexts.
Readers already familiar with effect systems may wish to start with the usage instead. For those interested, this talk at Strange Loop outlines the history of and motivation behind effect systems and fused-effects itself.
Algebraic effects
In fused-effects and other systems with algebraic (or, sometimes, extensible) effects, effectful programs are split into two parts: the specification (or syntax) of the actions to be performed, and the interpretation (or semantics) given to them.
In fused-effects, effect types provide syntax and carrier types provide semantics. Effect types are datatypes with one constructor for each action, invoked using the send builtin. Carriers are monads, with an Algebra instance specifying how an effect’s constructors should be interpreted. Carriers can handle more than one effect, and multiple carriers can be defined for the same effect, corresponding to different interpreters for the effect’s syntax.
Higher-order effects
Unlike some other effect systems, fused-effects offers higher-order (or scoped) effects in addition to first-order algebraic effects. In a strictly first-order algebraic effect system, operations like local or catchError, which specify some action limited to a given scope, must be implemented as interpreters, hard-coding their meaning in precisely the manner algebraic effects were designed to avoid. By specifying effects as higher-order functors, this limitation is removed, meaning that these operations admit a variety of interpretations. This means, for example, that you can introspect and redefine both the local and ask operations provided by the Reader effect, rather than solely ask (as is the case with certain formulations of algebraic effects).
As Nicolas Wu et al. showed in Effect Handlers in Scope, this has implications for the expressiveness of effect systems. It also has the benefit of making effect handling more consistent, since scoped operations are just syntax which can be interpreted like any other, and are thus simpler to reason about.
Fusion
In order to maximize efficiency, fused-effects applies fusion laws, avoiding the construction of intermediate representations of effectful computations between effect handlers. In fact, this is applied as far as the initial construction as well: there is no representation of the computation as a free monad parameterized by some syntax type. As such, fused-effects avoids the overhead associated with constructing and evaluating any underlying free or freer monad.
Instead, computations are performed in a carrier type for the syntax, typically a monad wrapping further monads, via an instance of the Carrier class. This carrier is specific to the effect handler selected, but since it isn’t described until the handler is applied, the separation between specification and interpretation is maintained. Computations are written against an abstract effectful signature, and only specialized to some concrete carrier when their effects are interpreted.
Since the interpretation of effects is written as a typeclass instance which ghc is eager to inline, performance is excellent: approximately on par with mtl.
Finally, since the fusion of carrier algebras occurs as a result of the selection of the carriers, it doesn’t depend on complex RULES pragmas, making it easy to reason about and tune.
Usage
Package organization
The fused-effects package is organized into two module hierarchies:
- those under
Control.Effect, which provide effects and functions that invoke these effects’ capabilities. - those under
Control.Carrier, which provide carrier types capable of executing the effects described by a given effect type.
An additional module, Control.Algebra, provides the Algebra interface that carrier types implement to provide an interpretation of a given effect. You shouldn’t need to import it unless you’re defining your own effects.
Invoking effects
Each module under the Control.Effect hierarchy provides a set of functions that invoke effects, each mapping to a constructor of the underlying effect type. These functions are similar to, but more powerful than, those provided by mtl. In this example, we invoke the get and put functions provided by Control.Effect.State, first extracting the state and then updating it with a new value:
action1 :: Has (State String) sig m => m ()
action1 = get >>= \ s -> put ("hello, " ++ s)
The Has constraint requires a given effect (here State) to be present in a signature (sig), and relates that signature to be present in a carrier type (m). We generally, but not always, program against an abstract carrier type, usually called m, as carrier types always implement the Monad typeclass.
To add effects to a given computation, add more Has constraints to the signature/carrier pair sig and m. For example, to add a Reader effect managing an Int, we would write:
action2 :: (Has (State String) sig m, Has (Reader Int) sig m) => m ()
action2 = do
i <- ask
put (replicate i '!')
Running effects
Effects are run with effect handlers, specified as functions (generally starting with run…) unpacking some specific monad with a Carrier instance. For example, we can run a State computation using runState, imported from the Control.Carrier.State.Strict carrier module:
example1 :: Algebra sig m => [a] -> m (Int, ())
example1 list = runState 0 $ do
i <- get
put (i + length list)
runState returns a tuple of both the computed value (the ()) and the final state (the Int), visible in the result of the returned computation. The get function is resolved with a visible type application, due to the fact that effects can contain more than one state type (in contrast with mtl’s MonadState, which limits the user to a single state type).
Since this function returns a value in some carrier m, effect handlers can be chained to run multiple effects. Here, we get the list to compute the length of from a Reader effect:
example2 :: Algebra sig m => m (Int, ())
example2 = runReader "hello" . runState 0 $ do
list <- ask
put (length (list :: String))
(Note that the type annotation on list is necessary to disambiguate the requested value, since otherwise all the typechecker knows is that it’s an arbitrary Foldable. For more information, see the comparison to mtl.)
When all effects have been handled, a computation’s final value can be extracted with run:
example3 :: (Int, ())
example3 = run . runReader "hello" . runState 0 $ do
list <- ask
put (length (list :: String))
run is itself actually an effect handler for the Lift Identity effect, whose only operation is to lift a result value into a computation.
Alternatively, arbitrary Monads can be embedded into effectful computations using the Lift effect. In this case, the underlying Monadic computation can be extracted using runM. Here, we use the MonadIO instance for the LiftC carrier to lift putStrLn into the middle of our computation:
example4 :: IO (Int, ())
example4 = runM . runReader "hello" . runState 0 $ do
list <- ask
liftIO (putStrLn list)
put (length list)
(Note that we no longer need to give a type annotation for list, since putStrLn constrains the type for us.)
Required compiler extensions
When defining your own effects, you may need -XKindSignatures if GHC cannot correctly infer the type of your constructor; see the documentation on common errors for more information about this case.
When defining carriers, you’ll need -XTypeOperators to declare a Carrier instance over (:+:), -XFlexibleInstances to loosen the conditions on the instance, -XMultiParamTypeClasses since Carrier takes two parameters, and -XUndecidableInstances to satisfy the coverage condition for this instance.
The following invocation, taken from the teletype example, should suffice for most use or construction of effects and carriers:
{-# LANGUAGE FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-}
Defining new effects
The process of defining new effects is outlined in docs/defining_effects.md, using the classic Teletype effect as an example.
Project overview
This project builds a Haskell package named fused-effects. The library’s sources are in src. Unit tests are in test, and library usage examples are in examples. Further documentation can be found in docs.
This project adheres to the Contributor Covenant code of conduct. By participating, you are expected to uphold this code.
Finally, this project is licensed under the BSD 3-clause license.
Development
Development of fused-effects is typically done using cabal v2-build:
cabal v2-build # build the library
cabal v2-test # build and run the examples and tests
The package is available on hackage, and can be used by adding it to a component’s build-depends field in your .cabal file.
Testing
fused-effects comes with a rigorous test suite. Each law or property stated in the Haddock documentation is checked using generative tests powered by the hedgehog library.
Versioning
fused-effects adheres to the Package Versioning Policy standard.
Benchmarks
To run the provided benchmark suite, use cabal v2-bench. You may wish to provide the -O2 compiler option to view performance under aggressive optimizations. fused-effects has been benchmarked against a number of other effect systems. See also @patrickt’s benchmarks.
Related work
fused-effects is an encoding of higher-order algebraic effects following the recipes in Effect Handlers in Scope (Nicolas Wu, Tom Schrijvers, Ralf Hinze), Monad Transformers and Modular Algebraic Effects: What Binds Them Together (Tom Schrijvers, Maciej Piróg, Nicolas Wu, Mauro Jaskelioff), and Fusion for Free—Efficient Algebraic Effect Handlers (Nicolas Wu, Tom Schrijvers).
Contributed packages
Though we aim to keep the fused-effects core minimal, we encourage the development of external fused-effects-compatible libraries. If you’ve written one that you’d like to be mentioned here, get in touch!
fused-effects-lensprovides combinators to use thelenslibrary fluently inside effectful computations.fused-effects-exceptionsprovides handlers for exceptions thrown in theIOmonad.fused-effects-resumableprovides resumable exceptions, which can also serve as a limited form of coroutines.fused-effects-mwc-randomprovides a performant, high-quality source of random data, as well as values from common numerical distributions.fused-effects-readlineprovides a REPL effect that interfaces withhaskelinefor its UI.fused-effects-parserprovides parser-combinator style effects similar to parsing libraries such astrifecta.fused-effects-opticsprovides combinators for theopticsecosystem.
Projects using fused-effects
semantic, a program analysis toolkitnow-haskell, a client library for AWS LambdaFOSSA, a tool for open-source risk management.
Comparison to other effect libraries
Comparison to mtl
Like mtl, fused-effects provides a library of monadic effects which can be given different interpretations. In mtl this is done by defining new instances of the typeclasses encoding the actions of the effect, e.g. MonadState. In fused-effects, this is done by defining new instances of the Carrier typeclass for the effect.
Also like mtl, fused-effects allows scoped operations like local and catchError to be given different interpretations. As with first-order operations, mtl achieves this with a final tagless encoding via methods, whereas fused-effects achieves this with an initial algebra encoding via Carrier instances.
In addition, mtl and fused-effects are similar in that they provide instances for the monad types defined in the transformers package (Control.Monad.Reader, Control.Monad.Writer, etc). This means that applications using mtl can migrate many existing transformers-based monad stacks to fused-effects with minimal code changes. fused-effects provides its own hierarchy of carrier monads (under the Control.Carrier namespace) that provide a more fluent interface for new code, though it may be useful to use transformers types when working with third-party libraries.
Unlike mtl, effects are automatically available regardless of where they occur in the signature; in mtl this requires instances for all valid orderings of the transformers (O(n²) of them, in general).
Also unlike mtl, there can be more than one State or Reader effect in a signature. This is a tradeoff: mtl is able to provide excellent type inference for effectful operations like get, since the functional dependencies can resolve the state type from the monad type.
Unlike fused-effects, mtl provides a ContT monad transformer; however, it’s worth noting that many behaviours possible with delimited continuations (e.g. resumable exceptions) are directly encodable as effects.
Finally, thanks to the fusion and inlining of carriers, fused-effects is only marginally slower than equivalent mtl code (see benchmarks).
Comparison to freer-simple
Like freer-simple, fused-effects uses an initial encoding of library- and user-defined effects as syntax which can then be given different interpretations. In freer-simple, this is done with a family of interpreter functions (which cover a variety of needs, and which can be extended for more bespoke needs), whereas in fused-effects this is done with Carrier instances for newtypes.
Unlike fused-effects, in freer-simple, scoped operations like catchError and local are implemented as interpreters, and can therefore not be given new interpretations.
Unlike freer-simple, fused-effects has relatively little attention paid to compiler error messaging, which can make common (compile-time) errors somewhat more confusing to diagnose. Similarly, freer-simple’s family of interpreter functions can make the job of defining new effect handlers somewhat easier than in fused-effects. Further, freer-simple provides many of the same effects as fused-effects, plus a coroutine effect, but minus resource management and random generation.
Finally, fused-effects has been benchmarked as faster than freer-simple.
Comparison to polysemy
Like polysemy, fused-effects is a batteries-included effect system capable of scoped, reinterpretable algebraic effects.
As of GHC 8.8, fused-effects outperforms polysemy, though new effects take more code to define in fused-effects than polysemy (though the Control.Carrier.Interpret module provides a low-friction API for rapid prototyping of new effects). Like freer-simple and unlike fused-effects, polysemy provides custom type errors if a given effect invocation is ambigous or invalid in the current context.
Comparison to eff
eff is similar in many ways to fused-effects, but is slightly more performant due to its representation of effects as typeclasses. This approach lets GHC generate better code in exchange for sacrificing the flexibility associated with effects represented as data types. eff also uses the monad-control package to lift effects between contexts rather than implementing an Algebra-style class itself.
Acknowledgements
The authors of fused-effects would like to thank:
- Tom Schrijvers, Nicholas Wu, and all their collaborators for the research that led to
fused-effects; - Alexis King for thoughtful discussions about and suggestions regarding our methodology;
- the authors of other effect libraries, including
eff,polysemy, andcapabilities, for their exploration of the space.
Changes
v1.1.2.5
- Fixes Accum carriers (Strict and Church) to not duplicate state (#449) (by @byorgey)
v1.1.2.4
- Adds support for
ghc9.12 andbase4.21.
v1.1.2.3
- Adds support for
ghc9.8 and 9.10,base4.19 and 4.20, andcontainers0.7.
v1.1.2.2
- Adds support for
ghc9.6,base4.18, andtransformers0.6.
v1.1.2.1
- Adds support for
ghc9.4 andbase4.17.
v1.1.2.0
-
Adds
MonadUnliftIOinstances forReaderC,LiftC, andInterpretC. (#420) -
Adds
Accum(#391) (by @turion)- Adds an
Accumeffect - Adds a church-encoded
Control.Carrier.Accum.Churchcarrier - Adds a strict
Control.Carrier.Accum.Strictcarrier - Adds an impure
Control.Carrier.Accum.IORefcarrier (#430) - Defines
Algebrainstances for the three mentioned carriers, and forControl.Monad.Trans.Accumfromtransformers
- Adds an
-
Defines
Algebra,Alternative,Applicative,Foldable,Functor,Monad,MonadFail,MonadFix,MonadIO,MonadPlus,MonadTrans,MonadUnliftIO,MonadZip, andTraversableinstances forControl.Effect.Choose.Choosing. (#419) -
Adds an
IORef-based carrier inControl.Carrier.State.IORef. (#422)
v1.1.1.3
- Adds support for
inspection-testing0.5.
v1.1.1.2
- Adds support for
ghc9.2.1 andbase4.16.
v1.1.1.1
- Adds support for
ghc9.0 &base4.15.
v1.1.1
- Defines
MonadFixinstances forLabelledandUnderLabel. (#402)
v1.1
-
Adds a church-encoded
Statecarrier inControl.Carrier.State.Church. (#363) -
Adds a church-encoded
Errorcarrier inControl.Carrier.Error.Church. (#203) -
Adds a church-encoded
Emptycarrier inControl.Carrier.Empty.Church. (#203) -
Adds a church-encoded
Writercarrier inControl.Carrier.Writer.Church. (#369) -
Adds a church-encoded
Freshcarrier inControl.Carrier.Fresh.Church. (#373) -
Defines
Algebrainstances forControl.Monad.Trans.Maybe.MaybeT,Control.Monad.Trans.RWS.CPS, andControl.Monad.Trans.Writer.CPS. (#366) -
Adds
evalEmptyandexecEmptyhandlers for theEmptycarriers as conveniences for usingemptyto signal early returns. (#371)
Backwards-incompatible changes
-
Changes
alg’s signature, giving it an initial state, and a distributive law which must be applied to each computation in the signature. This change allowsAlgebrainstances to be derived usingGeneralizedNewtypeDerivingandDerivingVia, while also obviating the need forhmap,handleCoercible, or thethreadmethod ofEffect. This furthermore increases the expressiveness of effects, allowing effects with higher-order positions yielding concrete types, e.g.m (), to be run anywhere in the stack, not just above anyEffect-requiring algebras. (#359, #361) -
Changes the signatures of
runInterpretandrunInterpretStateanalogously; also reorders the parameters torunInterpretStateto take the signature before the state parameter. (#359) -
Removes
Algebra’s superclass constraint requiring aHFunctorinstance for the signature. (#359) -
Removes
handleCoercible. Algebras which formerly used it when handling the tail of the signature may now composecoerceonto the homomorphism passed toalg. (#359) -
Removes
HFunctor. Effects are no longer required to haveHFunctorinstances, and so the class is redundant. (#359) -
Removes
Effect. The new signature foralg(see above) obviates the need for threading handlers through effects, replacing that by threading them through algebras instead. (#361) -
Redefines
threadas a wrapper aroundalg, composing context functors and distributive laws together. (Note that its type has also changed to take the context last and to decompose the handler for the two carriers.) (#361) -
Renames
Control.Effect.Interpret.HandlertoInterpreter. (#361) -
Reorders the parameters to the higher-order function passed to
Control.Effect.Lift.liftWithfor consistency withalgand to reflect its purpose of lifting Kleisli arrows in some underlying monad into the context modulo the context’s state. (#361) -
Redefines all effects as GADTs. Since we no longer require
Functor,HFunctor, orEffectinstances, we no longer need to use continuations to allow distinct result types per constructor.Algebrainstances for these effects can be ported forwards by removing the continuations. User-defined effects are not impacted, but we recommend migrating to GADT definitions of them for convenience and ease of comprehension going forwards. (#365) -
Removes
Control.Carrier.State.Lazy.runStateC, which was supposed to have been removed in 1.0.
v1.0.2.2
- Adds support for
ghc8.10 &base4.14. (#376)
v1.0.2.0
-
Adds a
stateoperation for theStateeffect. (#353) -
Adds a function reassociating sums leftwards to
Control.Effect.Sum. (#354) -
Inlines
inj. (#354) -
Adds labelled effects in
Control.Effect.Labelled. Labelled effects allow flexible disambiguation and dependency for parametric effects, enabling better type inference, restricted usage, and associated type parameters. (#354) -
Adds labelled interface for
ReaderandStateeffects inControl.Effect.Reader.LabelledandControl.Effect.State.Labelled. The functions in this interface are identical to their parent effect save that they accept a label parameter as an explicit type argument, suitable for use with an explicit type application; this can clean up code that would otherwise need an invocation ofrunUnderLabelto associate a labelled operation with its label. (#354) -
Adds a
sendIOoperation for theLift IOeffect. (#360) -
Inlines the
Readeroperations. (#347)
v1.0.0.1
- Adds passthrough
Algebrainstances forApandAlt, allowing the invocation of effects inside these structures without extraneous constructor applications.
v1.0.0.0
-
Adds an
Emptyeffect, modelling nondeterminism without choice (#196). -
Adds an
EmptyCcarrier forEmpty. (#196) -
Adds a
Chooseeffect, modelling nondeterminism without failure (#198). -
Adds a
Throweffect, modelling failure with a value. (#247) -
Adds a
Catcheffect which can be used withThrow(or other kinds of failure) to model recoverable failure. (#247) -
Adds a
oneOffunction toControl.Effect.NonDetto provide an idiom for the common case of nondeterministically selecting from a container. (#201) -
Adds a
foldMapAfunction toControl.Effect.NonDetmapping containers into nondeterministic computations using a supplied function. (#204) -
Defines a new
Hasconstraint synonym, conveniently combiningCarrierandMemberconstraints and used for all effect constructors. (#217) -
Allows effects to be defined and handled as sums of other effects, while still using the constructors for the component effects. This has been used to redefine
NonDetas a sum ofEmptyandChoose, andErroras a sum ofThrowandCatch. (#199, #219, #247) -
Defines
Carrierinstances for a number of types inbase, includingEither,Maybe,[], andIO. (#206) -
Defines
Carrierinstances for a number of types intransformers. (#226) -
Defines an
evalFreshhandler forControl.Carrier.Strict.FreshC, taking the initial value. (#267)
Backwards-incompatible changes
-
Renames the
Carrierclass toAlgebraand itseffmethod toalg, and moved the responsibilities ofControl.CarriertoControl.Algebra. This makes the library more consistent with the literature and encourages a style of naming that focuses on morphisms rather than objects. (#285, #294) -
Fixes unlawful behaviour in the
Applicativeinstance forErrorC, which had different behaviour between<*>andapin the presence of a divergent rhs. In order to accomplish this,ErrorChas been defined as a wrapper aroundControl.Monad.Trans.Except.ExceptT. (#228) -
Improves the performance of
runInterpretusing reflection, changing its signature slightly (#193, h/t @ocharles). -
Removes
Control.Effect.Random(and the dependencies onrandom&MonadRandom) in favour of a newfused-effects-randompackage (#200). -
Removes
fmap'andhandlePure, both deprecated in 0.5.0.0 (#205). -
Redefines
NonDetCas a Church-encoded binary tree instead of a Church-encoded list (#197). -
Removes the
OnceCcarrier forCulleffects, replacing it with the composition ofCullCon some otherAlternativecarrier, e.g.NonDetC(#204). -
Moves all the carriers into their own modules in the
Control.Carriernamespace. Several have also been renamed, e.g. the variousTracecarriers are all namedTraceCwithin their separate modules, and should be imported qualified if disambiguation is required. This simplifies naming schemes, and ensures that the choice of e.g. strict or lazy carrier is always made consciously and expliclty, instead of defaulting to whichever is exported by the effect module (#204). -
Removes the re-export of
Memberfrom all carrier modules, re-exportingHasin its place.Hasconstraints should generally be used instead, and specialist cases can importControl.Effect.SumforMember. (#217) -
Redesigns & renames the handlers for church-encoded nondeterminism carriers to standardize naming and usage patterns. (#207)
- The primary handlers (
runChoose,runNonDet,runCut,runCull) take multiple continuations. - Handlers which return an
Alternativeare suffixed withA, e.g.runNonDetA. - Handlers which return a
Monoidare suffixed withM, e.g.runNonDetM. - Handlers which return a
Semigroupare suffixed withS, e.g.runChooseS.
- The primary handlers (
-
Removes
InterposeC&runInterposedue to their inefficiency. They can be replaced with use ofInterpretC/runInterpretfor the desired effect. (#223) -
Removes
prjfromMember, as it was only used inInterposeC(see above), and was generally inadvisable due to its lack of modularity. (#223) -
Removes the
Resourceeffect and carrier. Both have been relocated tofused-effects-exceptions. (#268) -
Redefines
Failas a synonym forThrow String. (#247) -
Removes
Resumableand its carriers. Both have been relocated tofused-effects-resumable; they can also be usefully and flexibly replaced by arbitrary effects,Lift, andInterpretC. (#269) -
Changes
Control.Carrier.Fresh.Strict.runFreshto take and return the initial & final values, respectively, allowing for safer operation. (#267) -
Removes
resetFresh, as it was unsafe. Greater safety and control over the generation of fresh values can be obtained by use ofrunFresh. (#267) -
Removes
PureC;Data.Functor.Identity.Identityshould be used instead. Note thatrunis still provided as a convenient synonym forrunIdentity. (#307) -
Removes the
Pureeffect. It’s unlikely that this will require changes, asPurehad no operations, butLift Identityshould be used instead. (#307) -
Redefines the
Lifteffect, allowing inner contexts to run actions in outer contexts, e.g. to interoperate withControl.Exception. (#306) -
Removes
MonadUnliftIOinstances as they’ve been subsumed by the new definition ofLift. Additionally, theReaderT&IdentityTtypes defined intransformersmay be useful. (#306)
v0.5.0.1
- Adds support for ghc 8.8.1.
v0.5.0.0
-
Derives
Generic1instances for all non-existentially-quantified effect datatypes. -
Derives
Foldable&Traversableinstances for:+:. -
Defines
MonadFixinstances for all of the carriers. -
Re-exports
run,:+:, andMemberfromControl.Effect.Carrier, reducing the number of imports needed when defining new effects. -
Re-exports
Carrier,Member, andrunfrom the various effect modules, reducing the number of imports needed when using existing effects.
Backwards-incompatible changes
-
Replaces
runResourcewith an equivalent function that usesMonadUnliftIOto select the correct unlifting function (a lawithResource, which is removed in favor ofrunResource). -
Changes the signature of
efffromsig m (m a) -> m atosig m a -> m a, requiring effects to holdm kin their continuation positions instead of merelyk. This was done in order to improve interoperability with other presentations of higher-order syntax, e.g.bound; syntax used withboundcan now be givenHFunctorandCarrierinstances.To upgrade effects used with previous versions, change any continuations from
ktom k. If no existential type variables appear in the effect, you can deriveGeneric1, and thenceHFunctor&Effectinstances. Otherwise, implement the required instances by hand. Since continuation positions now occur inm,hmapdefinitions will have to apply the higher-order function to these as well. -
Adds
Functorconstraints tohmapandMonadconstraints tohandle, allowing a greater variety of instances to be defined (e.g. for recursively-nested syntax). -
Replaces the default definitions of
hmapandhandlewith derivations based onGeneric1instead ofCoercible. Therefore, first-order effects wishing to derive these instances will requireGeneric1instances, presumably derived using-XDeriveGeneric. -
Moves
sendfromControl.Effect.SumtoControl.Effect.Carrier. Likewise removes the re-export ofsendfromControl.Effect. -
Deprecates
fmap'in favour offmap. -
Deprecates
handlePurein favour ofhmap.
v0.4.0.0
Backwards-incompatible changes
- Removes APIs deprecated in 0.3.0.0, including
Eff,interpret,ret, and thehandle*family of helper functions.
Other changes
- Adds the ability to derive default instances of
HFunctorandEffectfor first-order effects, using the-XDeriveAnyClassextension. - Adds a generic
Interposeeffect that enables arbitrary “eavesdropping” on other effects.
0.3.1.0
- Improved speed of
Reader,State,Writer, andPureeffects by defining and inlining auxiliaryApplicativemethods. - Adds
runInterpret&runInterpretStatehandlers inControl.Effect.Interpretas a convenient way to experiment with effect handlers without defining a new carrier type andCarrierinstance. Such handlers are somewhat less efficient than customCarriers, but allow for a smooth upgrade path when more efficiency is required. - Added
unliftio-coreas a dependency so as to provide a blessed API for unlift-style effects and a solution to the cubic-caller problem.
0.3.0.0
Backwards-incompatible changes
- Adds
Monadas a superclass ofCarrier, obviating the need for a lot of constraints, andMonadinstances for all carrier types. This is a backwards-incompatible change, as any carriers users have defined now requireMonadinstances. Note that in many cases carriers can be composed out of existing carriers and monad transformers, and thus these instances can often be derived using-XGeneralizedNewtypeDeriving. We also recommend compiling with-Wredundant-constraintsas many of these can now be removed. - Replaces
AltCwith a new carrier,NonDetC, based on Ralf Hinze’s work in Deriving Backtracking Monad Transformers. This is a backwards-incompatible change.AltCwas equivalent to theListTmonad transformer, and had the same well-known limitation to commutative monads. Therefore, the elimination ofEffrequired a more durable approach. - Removes
Branch. This is a backwards-incompatible change, but was necessitated by the difficulty of implementing correctApplicative&Monadinstances for carriers which used it. Carriers which were employingBranchinternally should be reimplemented usingNonDetCor a similar approach; seeCutCandCullCfor examples. - Renames
Control.Effect.Void,Void, andVoidCtoControl.Effect.Pure,Pure, andPureCrespectively. This is a backwards-incompatible change for code mentioningVoidC; it should be updated to referencePureCinstead.
Deprecations
Effandinterpret, in favour of computing directly in the carriers. This enables the compiler to perform significant optimizations; see the benchmarks for details. Handlers can simply remove theEffwrapping the carrier type & any use ofinterpret. As above, we also recommend compiling with-Wredundant-constraintsas many of these can now be removed.ret, in favor ofpureorreturn.handleEither,handleReader,handleState,handleSum, andhandleTraversablein favour of composing carrier types directly. Carriers can be composed from other carriers andeffdefined withhandleCoercible; and other definitions can usehandlePure&handledirectly.
All deprecated APIs will be removed in the next release.
Other changes
- Adds a lazy
Statecarrier inControl.Effect.State.Lazy - Rewrites
CutCusing an approach related toNonDetC, with the addition of a continuation to distinguishemptyfromcutfail. - Rewrites
CullCusingListCandReaderC. - Moves
OnceCfromControl.Effect.NonDettoControl.Effect.Cullto avoid cyclic dependencies. - Adds a
runCutAllhandler forCuteffects, returning a collection of all results.
0.2.0.2
- Loosens the bounds on QuickCheck to accommodate 2.x.
0.2.0.1
- Fixes the benchmarks, and builds them in CI to avoid regressing them again.
0.2.0.0
- Adds
listen,listens, andcensoroperations toWriter. - Provides explicit type parameters to
run-style functions inState,Reader,Writer, andError. This is a backwards-incompatible change for clients using these functions in combination with visible type applications. - Adds benchmarks of
WriterC/VoidCwrapped withEffagainst their unwrapped counterparts. - Adds
Functor,Applicative, andMonadinstances forWriterC. - Adds
Functor,Applicative, andMonadinstances forVoidC. - Fixes a space leak with
WriterC. - Removes the
Functorconstraint onasksandgets. - Adds
bracketOnError,finally, andonExceptiontoResource. - Adds
sendMtoLift.
0.1.2.1
- Loosens the bounds on QuickCheck to accommodate 0.12.
0.1.2.0
- Adds support for ghc 8.6.2, courtesy of @jkachmar.
- Adds a
Cuteffect which adds committed choice to nondeterminism. - Adds a
Culleffect which adds pruning to nondeterminism. - Adds an example of using
NonDet,Cut, and a character parser effect to define parsers. - Fixes the table of contents links in the README.
0.1.1.0
- Adds a
runNonDetOncehandler which terminates immediately upon finding a solution.
0.1.0.0
Initial release.