Bluefin compared to effectful [video]

Thanks for making the presentation showcasing how effects are an improvement over the ReaderT pattern or transformers/mtl.

I have a few comments.

“Bluefin takes streaming seriously” slide

The text “you cannot do this in effectful” is misleading. As pointed out shortly after, the reality is there is simply no such effect in the effectful-core and effectful package, nothing prevents someone interested in streaming to experiment with how the API should look like and publish their own effectful-streaming (or something) library. Considering that there exist multiple streaming libraries with large APIs that were developed and polished over the years, I suspect providing a good streaming API as an effect is not as easy as you make it out to be, but perhaps I’m wrong.

“Positives for effectful and Bluefin” slide

  • Inference is better - no argument there. Poor inference with multi-parameter effects is my main gripe with effectful. effectful-plugin solves the problem, but it would be nice to have a solution that doesn’t require a plugin. Maybe one day I’ll have a brilliant idea or something.

  • Creating new effects is just creating new data types (“normal Haskell”) - the same is true for effectful, the only difference is the representation - product types vs sum types.

  • Streams - again, it’s just an effect that exists in the bluefin package and not in the effectful-core/effectful package. The effectful package has multiple effects that bluefin doesn’t, but it’s not mentioned (and rightly so), so I’m not sure why this particular one is.


Let’s talk about downsides. The main ones of effectful IMO at the moment are:

  1. Poor inference with multi-parameter effects without a plugin. As mentioned above, plugin fixes this, but ideally it shouldn’t be needed.

  2. Awkward interoperability between regular effects and their labeled counterparts. In bluefin all effects are labeled (at the term level) and this uniformity is nice.

If it comes to bluefin, IMO its major issues are:

  1. Passing effects as arguments.
  2. Unclear story for higher order effects.
  3. No good story for interop with MTL style effects.

Re (1), it looks nice in small examples since you can clearly see where the effects go and there are no inference issues. However, if effects are embraced in a non-trivial code base, there’s going to be quite a few of them. As @michaelpj pointed out, their code base has functions with 20+ effects, passing these explicitly won’t work. I personally developed at work a small-ish service with a limited scope with effectful and functions there use around 5-10 effects, that’s already too much for explicit passing.

You can probably create a bunch of data types and pack your effects into groups and pass a single parameter, but then at some point you will have to add or remove some or refactor the code and dabble in it again, not great.

Re (2), see this ticket. A good, simple litmus test for higher order effects is here - you run Reader, then use ask in a downstream handler and later you can call local to influence the behavior of the handler. Not sure how that would look in bluefin (one can be tempted to use State with IORef underneath instead, but what if you then add forkIO to the equation?). There was actually a question from @ocharles about local at the end of the presentation that went unanswered.

Re (3), see this ticket. When you look at bluefin’s Eff, it pretty much has no instances (compare that to instances of the effectful’s Eff). How do you run MonadIO or MonadFail stuff? There are some special functions for this with very limited usability (because m is universally quantified in the continuation). You have a function f :: (MonadIO m, MonadFail m) => m ()? You can’t run it easily in bluefin’s Eff. This also means that you will most likely have a bad time trying to run any library/code that uses mtl style effects and there is a lot of such code out there.

14 Likes