I’ll try to sum up the Haskell effect system lore that I have had some participation in, which is from a reasonably early point, i.e. from circa 2020 up to now. Since you can’t watch Alexis’ videos, this hopefully serves as a rudimentary substitute as well.
There had been free(r) monads-based implementations for quite some years as of 2020, most popular (read: most usable) ones are freer-simple
and polysemy
. They are slow because they essentially construct a program tree at runtime, and then the handlers traverse the tree to slowly materialize a program. freer-simple
is actually often respectably fast though, but this brings us to another topic of our story: higher-order effects.
Higher-order effects means you can create effects with operations that take effectful computations as arguments. That probably sounds confusing; essentially, it allows operations like WithFile :: FilePath -> (Handle -> Eff es a) -> Eff es a
. The second argument is an effectful computation because it returns an Eff
. If your effect library supports higher-order effects, you can write different handlers for this operation: for example, one reading files from a web server while the other reading from your local hard disk. Many people think this is a good thing to have; freer-simple
doesn’t have it however, but polysemy
does (in an awkward way, but it does the job most of the times).
Then there are fused-effects
which everyone believes is fast because it is supposed to “fuse away” intermediate structures… well, it has the same problem as mtl
: if the code doesn’t specialize, compiler has no way to fuse them away! For your code to specialize reliably, the only way is to add {-# SPECIALIZE #-}
all over your functions, which is very not ideal; not only is it tedious to write, this also slows down compilation.
So the situation is not exactly good. Alexis King and Ningning Xie almost simultaneously had an idea circa 2020 (I believe; I don’t know if they had communicated about the idea before), which is to use evidence-passing and delimited continuations to build an effect system in Haskell that would be both fast and expressive enough; specifically, in Alexis’ vision, it will support fast higher-order effects.
What is evidence-passing? It basically means to pass the effect handlers around in the Eff
monad, so instead of handlers traversing a big program tree like in free monads, we directly call the handlers in place. This is a much more performant approach, and it’s pretty shocking that nobody thought of this before. Nowadays, eff
, eveff
, mpeff
, speff
, cleff
, and effectful
all use evidence passing.
Delimited continuations are nothing new actually, and has been used to develop other languages with effects like Koka since at least 2015. Probably nobody thought of doing this in Haskell before because Haskell didn’t support it natively, and they didn’t think it would be efficient. Ningning came up with a monad that can do delimited continuations fairly fast, which is used in eveff
, mpeff
and speff
. On the other hand Alexis decides to just add native delimited continuations to GHC and went down that way with eff
.
OK, then what did cleff
and effectful
use instead of delimited continuations? The answer is nothing: they had different tradeoffs in expressiveness compared to the other libraries mentioned - they do not support nondeterminism, so that they can support MonadUnliftIO
. This consequently eliminates the need for delimited continuations. Why we can’t support both nondeterminism and MonadUnliftIO
? It is perhaps less well known that many IO functions accessible via MonadUnliftIO
, mainly those that use fork
and bracket
, only work when the control flow is deterministic. This has not been a problem since the beginning since Haskell was a deterministic language - but not now! Future effect system users will probably face a choice between nondeterminism-capable libraries and MonadUnliftIO
-capable libraries and they will need to choose based on their specific needs.
Hope that has fulfilled your curiosity. About your questions on effect system implementations in Haskell:
In reality, implementations could be way faster than they are right now (eff, polysemy, eveff, speff, mpeff)
As you can see, evidence-passing based implementations are already very fast, and increasing user adoption, instead of squeezing out more performance, is our primary goal now.
Eff uses delimited continuations but is waiting for some primops to be merged to gain speedups
It’s merged and to be shipped in GHC 9.6!
eveff/speff/mpeff seem to use a new paper to implement effects that seems really weird
One thing I keep saying is that users of effect systems should not need to know about the internals. If an effect system forces users to do so, it has failed. But on the flip side, “implementation technique sounds weird” should not scare you away from using a library - you only need to interact with the API after all! However I must warn you that these 3 libraries are all proofs of concepts, and should not be used in production.
In some cases, they’re more complex than required.
Polysemy’s API is… not ideal. But as you mentioned, new libraries’ APIs are getting intuitive and easy-to-use, so you can try out those libraries more instead. We can’t control what others think, really; and the notoriety of the last wave of effect libraries cannot be simply erased. But I’d continually encourage Haskellers I meet to give the new generation of effect libraries a chance.