7.4 KiB
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, fast extensible effects library with seamless integration with the existing Haskell ecosystem.
Main features:
-
Very fast (benchmarks).
-
Easy to use API (if you know how to use the MonadUnliftIO class, you know how to write effects).
-
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 (e.g.
StateT
provides a thread local state, whileMVar
holds a shared state, both approaches have their merits). -
Support for statically (implementation determined at compile time) and dynamically (implementation determined at run time) 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 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.
What about mtl
?
It's true that its "effects as classes" approach is widely known and used often.
However:
-
mtl
style effects are slow. -
All of most often used monad transformers (except
ReaderT
) used for effect implementations are rife with subtle issues.
These issues are problematic enough that the ReaderT design pattern was invented. Its fundamentals are solid, but it's not an effect system.
The solution? Use the ReaderT
pattern as a base and build around it to make it
an effect system! 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 representing effects.
This concept is quite simple, so:
-
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
and solves issues of monad transformers mentioned above.
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 function INLINE
or enable additional
optimization passes, it just works.
Any downsides?
As always, there's no free lunch. Eff
doesn't support NonDet
nor Coroutine
effects. However, the NonDet
effect in existing libraries is
broken
and none of the ones with support for higher order effects provide the
Coroutine
effect, so arguably it's not a big loss.
If you need such capability in your application, there are well established
libraries such as conduit or
list-t that can be used with
effectful
without any issues.
Summary
effectful
aims to replace "boring" transformer stacks (which consist of a
dozen of newtype'd ExceptT
, ReaderT
, StateT
and WriterT
transformers) by
providing equivalent effects with improved semantics, performance and usability
(it also makes it easy to reuse them for your own effects). It doesn't try to
make monad transformers obsolete, so you're free to use it with ConduitT
,
ContT
, ListT
etc. when necessary.
Ecosystem
The effect system is 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-th
library provides utilities for generating bits of effect-related boilerplate via Template Haskell. -
The
effectful
library re-exports public modules ofeffectful-core
and additionally provides most features of theunliftio
library 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.