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 theeffectful-core
/effectful
package. Theeffectful
package has multiple effects thatbluefin
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:
-
Poor inference with multi-parameter effects without a plugin. As mentioned above, plugin fixes this, but ideally it shouldn’t be needed.
-
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:
- Passing effects as arguments.
- Unclear story for higher order effects.
- 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.