.github/workflows | ||
effectful | ||
effectful-core | ||
effectful-th | ||
.gitignore | ||
cabal.haskell-ci | ||
cabal.project | ||
CHANGELOG.md | ||
doctest.sh | ||
LICENSE | ||
README.md |
effectful
Note: this is a pre-release of the 0.1 version. Please disregard the 0.0.0.0 version available on Hackage as the API has been completely redesigned since then.
An easy to use, performant extensible effects library with seamless integration with the existing Haskell ecosystem.
Main features:
-
Very fast (benchmark results with GHC 8.8.4: countdown, filesize, comparison with eff).
-
Easy to use API (no boilerplate code and dealing with arcane types).
-
Correct semantics in presence of runtime exceptions (no more discarded state updates).
-
Seamless integration with the existing ecosystem (
exceptions
,monad-control
,unliftio-core
,resourcet
etc.). -
Support for thread local and shared state.
-
Support for statically and dynamically dispatched effects.
Motivation
Do we really need yet another library for handling effects? There's freer-simple, fused-effects, polysemy, eff and probably a few more.
Unfortunately, of all of them only eff
is a promising proposition because of
reasonable performance characteristics (see the talk "Effects for Less" linked
below for more information) and potential for good interoperability with the
existing ecosystem.
The second point is arguably the most important, because it allows focusing on things that matter instead of reinventing all kinds of wheels, hence being a necessary condition for broader adoption of the library.
However, eff
uses delimited continuations underneath, which:
-
Are not yet supported by GHC (though the proposal for including support for them has been accepted).
-
Are quite hard to understand.
-
Make the library "too powerful" in a sense as it faces a few issues with no clear path towards their resolution.
On the other hand, if support for continuations is excluded from the handler
monad, then the ability to define effects with non-linear control flow (such as
NonDet
) is lost. Arguably it's a small price to pay for predictability,
because such specialized effects are needed rarely and locally, at which point a
dedicated, well established solution such as
conduit or
list-t can be used.
This is where effectful
comes in. The Eff
monad it uses is essentially a
ReaderT
over IO
on steroids, allowing us to dynamically extend its
environment with data types that represent effects or their handlers.
Because this concept is so simple:
-
It's reasonably easy to understand what is going on under the hood.
-
The
Eff
monad being a reader allows for seamless interoperability with ubiquitous classes such asMonadBaseControl
andMonadUnliftIO
as well as support for handling runtime exceptions without worrying about discarded state updates (see the talk "Monad Transformer State" linked below for more information).
What is more:
-
The
Eff
monad is concrete, so GHC has many possibilities for optimization, which results in a very fast code at a default optimization level. There is no need to mark every functionINLINE
or enable additional optimization passes, it just works. -
If an advanced capability with non-linear control flow is needed, you can always stick a transformer that implements it on top of
Eff
in a local context.
In conclusion, effectful
aims to reduce duplication and bring back performance
to "boring" transformer stacks, most of which are a dozen of newtype'd StateT
or ReaderT
transformers, each with a few associated operations (usually tied
to a type class), not to replace monad transformers altogether.
Usage
The effect system and its effects are split among several libraries:
-
The
effectful-core
library contains the main machinery of the effect system itself and basic effects. It aims for a small dependency footprint and provides building blocks for more advanced effects. -
The
effectful
library re-exports public modules ofeffectful-core
and in addition provides functionality of theunliftio
package divided into appropriate effects.
Example
A Filesystem
effect with two handlers, one that runs in IO
and another that
uses an in-memory virtual file system can be found
here.
Resources
Resources that inspired the rise of this library and had a lot of impact on its design.
Talks:
-
Effects for Less by Alexis King.
-
Monad Transformer State by Michael Snoyman.
Blog posts:
-
ReaderT design pattern by Michael Snoyman.
-
Exceptions Best Practices by Michael Snoyman.