[Haskell-cafe] Algebraic Effects?

Alexis King lexi.lambda at gmail.com
Thu Sep 20 20:45:09 UTC 2018


> On Sep 18, 2018, at 10:49, Viktor Dukhovni <ietf-dane at dukhovni.org> wrote:
> 
> I took a quick look at:
> 
>  https://hackage.haskell.org/package/extensible-0.4.10.
> 
> The author claims good performance:
> 
>  https://www.schoolofhaskell.com/user/fumieval/extensible/the-world-s-fastest-extensible-effects-framework

That’s an interesting post. I am not totally sure why precisely it would be faster than freer/freer-effects/freer-simple, but it’s worth looking into to see if its optimizations can be incorporated into freer-simple. In any case, it seems like it’s “only” faster by a factor of ~2x, while the difference in performance between mtl and extensible is a factor of ~12x, so the difference is not massive. Still, it’s worth investigating.

> With this library, at least when building effects out of mtl transformers,
> the order of the effects in the Eff type declaration has to match in
> reverse order the composition of the "runFooDef" functions.  That is,
> the types:
> 
> 	Eff '[ReaderDef r, StateDef s]
> and
> 	Eff '[StateDef s, ReaderDef r]
> 
> are not the same.  Perhaps this is a feature?

Generally, when working with extensible effect frameworks, you do not work with concrete lists of effects, since those two types are indeed different. Instead, you write functions polymorphic over the set of effects, and you include constraints on what must be somewhere in the list. Therefore, you’d express your example like this in freer-simple:

    Members '[Reader r, State s] effs => Eff effs a

This constraint expresses that `Reader r` and `State s` must both exist somewhere in the list of effects `eff`, but it doesn’t matter where they are or what order they’re in. You only pick a concrete set of effects when finally running them.

In a sense, this is similar, but not quite equivalent to, the difference between using transformers directly and using mtl typeclasses. Specifically, I mean the difference between these three types:

    ReaderT r (State s) a
    StateT s (Reader r) a
    (MonadReader r m, MonadState s m) => m a

The first two specify a concrete transformer stack, which specifies both the order of the transformers and the whole contents of the stack. The mtl classes parameterize over the precise transformer stack, which enables easier composition. However, in a sense, a use of Eff with a concrete list is sort of in between these two types — since each effect can still be handled with an arbitrary effect handler, a concrete list enforces effect order relative to other effects, but it doesn’t enforce which handler must be used to handle each effect.

The relationship between those things is largely unimportant when actually using extensible effects, however. Just use the `Member` or `Members` constraints instead of specifying a concrete list, and you’ll be alright.

> Thanks.  I'll take a look.  Any comments on similarities to or
> differences from the "extensible" package above?

I am not intimately familiar with the extensible package, but it has a much broader scope than simply implementing an extensible effects library: it also implements open records/sums, and a whole bunch of other things (of, in my opinion, questionable usefulness). As for a comparison between exclusively the effects subset of extensible and freer-simple, extensible seems to have a very different API, which labels each effect in an effect stack.

Personally, I am skeptical of extensible’s API. It seems to make some tradeoffs with benefits that I can’t imagine are worth the costs. Still, I haven’t used it in practice, so I can’t seriously judge it.

> One concern for me is whether using Effects is likely to cause more allocations
> and work for the GC, or is the memory pressure about the same?

It’s hard to say. My conservative answer is “yes, extensible-effects will allocate more”, since extensible effects systems reify computations as data structures by design, and sadly, GHC isn’t clever enough to eliminate those data structures even when effect handlers are used in such a way that all effects can be statically determined. In contrast, the GHC optimizer is really good at optimizing away the dictionaries inserted by mtl-style typeclasses.

That said, I don’t feel like I can really say for sure without being intimately familiar with a given program. The problem with things like extensible-effects is that microbenchmarks are always going to be misleading; the performance characteristics (relative to mtl style, anyway) can vary wildly depending on how they’re used in practice. I think this is an area where you just have to benchmark and see.

> On Sep 20, 2018, at 09:29, Lana Black <lanablack at amok.cc> wrote:
> 
> extensible-effects is also based on freer monads. One advantage over freer-simple and other libraries is that it includes monad-control instances for most used effects, allowing to mostly painlessly use things like forkIO. The limitations of monad-control apply though.

Thanks for the correction. I peeked at the documentation, and it looks like extensible-effects switched to using freer in November of last year, which sounds about right, since I was looking into extensible effects libraries last July, prior to the switch. I guess I’m a little behind the times.

The MonadBaseControl instances are neat, though they seem to be rather limited
in that they hardcode particular effect handlers. I don’t know of a way around that restriction, but it does seem potentially misleading to me. In any case, the effort of implementing MonadBaseControl instances is still quite high, and since it still needs to be done for user-defined effects, I’m not sure how useful it would be in practice (as when I use either mtl style or extensible effects, I define a lot of my own effects rather than only using the ones provided by the library).

Alexis



More information about the Haskell-Cafe mailing list