Awesome
A benchmark comparing the performance of different free monad implementations.
The benchmark simulates the state monad using various flavors of free monads, and compares them to the standard State monad from transformers.
Note that this is not a comparison of extensible effects system. Free monads may be used to implement an extensible effect system. Under most implementations, extensible effects introduce an even bigger overhead (dispatching upon the effect requests); this overhead is not present in this benchmark. However, if your free monad is slow (which it probably is, as this benchmark shows), any effect system based on it won't be fast.
Running the benchmark
stack build && stack exec freemonad-benchmark -- -o results.html
Results
Implementations
-
Free
data Free f a = Pure a | Free (f (Free f a))
-
Free/lazy
The same standard Free monad emulating the lazy State monad.
-
Church
The Church-encoded free monad:
newtype ChurchFree f a = ChurchFree { runChurchFree :: forall w. (a -> w) -> (f w -> w) -> w }
-
Codensity
The standard Free monad, codensity-transformed. See Asymptotic Improvement of Computations over Free Monads.
-
NoRemorse
A free monad from Reflection without Remorse.
-
Freer
The Freer monad from Freer Monads, More Extensible Effects, aka the operational monad.
-
Effects
An extensible effects implementation with higher-order effects based off Freer Monads, More Extensible Effects maintained by @joshvera and @robrix.
-
Fused
An implementation of Fusion for Free: Efficient Algebraic Effect Handlers by @brfk.
Workloads
For every implementation, there are two tests, for left- and right-associated chains of binds. Some free monads (e.g. the standard one) suffer from quadratic complexity on left-associated chains of binds.