fused-effects and mtl - Haskell

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Peter J. Jones

That article reminded me that I need to look at fused-effects again. I used it in an app about a year ago and it was pretty painful. Internal code was easy enough to write, but using any libraries that assumed mtl classes was horrible.

Alistair Burrowes

@Peter J. Jones they do have 'Algebra' instances for the mtl types https://hackage.haskell.org/package/fused-effects-1.1.1.0/docs/Control-Algebra.html (search for StateT etc.). I haven't done it but I think this means you can just put plain old MTL types in your fused-effects 'carrier' stack.

Alistair Burrowes

It did find it quite challenging wiring up mtl and fused-effects, but other than that I don't think any of the other libraries I used exposed a mtl style interface.

Peter J. Jones

@Allan Erskine I think you are correct. IIRC, the problem is with using functions that have mtl-like type constraints. I suppose you just end up with a mess of constraints like MonadSomething m, Has (Reader x) sig m => .... It's been awhile so I'm drawing a blank as to why I had so many problems.

Peter J. Jones

I think my biggest issue was using MonadRandom from cryptonite and an orphan instance in the jwt library.

Alistair Burrowes

yeah interesting. I wonder if it easier now.

Peter J. Jones

In the end I'm not sure it's worth all the trouble. MTL-style classes work well most of the time. In practice you hit the n+m instance problem rarely. I think I prefer to encode my business logic as an EDSL via Free Monads vs. using an effect system.

Peter J. Jones

Effect systems are super interesting, and on paper they are really exciting. But when you just want to get work done I think they get in the way.

Alistair Burrowes

I agree, I think people should default to MTL style. It is easier to learn / less magic and integrates better with the rest of the ecosystem. Ergonomically I think effect systems are nicer to work with though. polysemy showed me how nice they can be. Although I went with fused-effects because I trusted it to have better performance / memory usage. fused-effects is quite ugly to work with though.. :) (at least until your learn it)

Peter J. Jones

A lesson that I've recently (re-)learned is that MTL (and other effect systems) can be a crutch for when our minds are stuck in an imperative programming world. It's a good exercise to try writing a function without them, and almost always you can.

Georgi Lyubenov // googleson78

Peter J. Jones said:

MTL-style classes work well most of the time.

One immediate common use case where mtl falls apart is having more than one error type. Sure you could patch that up with the various hacks cooked up exactly for this purpose but it feels bad having to hack around it in the first place

In practice you hit the n+m instance problem rarely.

This is only "true" imo, because mtl has already conditioned us to avoid making new effects exactly because of this reason. In systems where it's not boilerplatey it's very convenient to make many custom effects, even ones that are only used for intermediate interpretation.

I think I prefer to encode my business logic as an EDSL via Free Monads vs. using an effect system.

That's exactly what things like freer-simple and polysemy do. What's the difference between what you're thinking and those?

bradrn

Georgi Lyubenov // googleson78 said:

In practice you hit the n+m instance problem rarely.

This is only "true" imo, because mtl has already conditioned us to avoid making new effects exactly because of this reason. In systems where it's not boilerplatey it's very convenient to make many custom effects, even ones that are only used for intermediate interpretation.

Could you give an example of this please?

Peter J. Jones

@Georgi Lyubenov // googleson78 you have some really good insight, thank you for sharing.

I let this bounce around in my mind for a couple of days and you are absolutely right. MTL has conditioned me to think in a specific way. Your example w.r.t. multiple error types makes your point very clearly. Because of such pain I've completely given up on using MonadError and instead have such functions return m (Either e a). It's pretty easy to use the Bifunctor instance of Either to change the error type as needed. I'm curious though, how do you deal with the multiple error issue? Do you write functions with constraints that allow them to throw many different types of errors?

As far as effects vs. EDSLs: First, let me admit that I've not used freer-simple or polysemy. My experience mostly involves mtl and fused-effects. I also need to admit that, as far as I can tell, I only have an emotional response to give you: coding to an EDSL feels more restrictive---in a good way. A function using the EDSL can't add any additional effects. To do so requires changing the EDSL itself, forcing me to think more about what I'm doing and if that effect is really needed. On the other hand, writing functions that can run in any monad given some constraints makes it really easy to add new effects. When using something like fused-effects I found myself constantly adding more effects that then get inherited up the call stack. A large part of this problem was probably my immaturity with an effect system.

Hmm, as I think on this I realize that my EDSL idea could be written as a large, single effect. Then as individual functions require additional effects I could localize those without needing to alter the EDSL. It sounds like I have some more thinking to do on this. :smile:

Torsten Schmits

@Peter J. Jones if you want some more inspiration about errors in polysemy, I recommend looking at the readme of my library for cross-interpreter error tracking: https://github.com/tek/polysemy-resume :smile:

cross-interpreter errors for polysemy. Contribute to tek/polysemy-resume development by creating an account on GitHub.
TheMatten

On the other hand, writing functions that can run in any monad given some constraints makes it really easy to add new effects. When using something like fused-effects I found myself constantly adding more effects that then get inherited up the call stack. A large part of this problem was probably my immaturity with an effect system.

Hmm, those effects still need to be interpreted somewhere - if you don't add them to your interpreter, they shouldn't work, should they?

Peter J. Jones

TheMatten said:

On the other hand, writing functions that can run in any monad given some constraints makes it really easy to add new effects. When using something like fused-effects I found myself constantly adding more effects that then get inherited up the call stack. A large part of this problem was probably my immaturity with an effect system.

Hmm, those effects still need to be interpreted somewhere - if you don't add them to your interpreter, they shouldn't work, should they?

Obviously this isn't very clear in my head. I don't think of functions written in an EDSL to be using effects per se. However, the interpreter may need to use various effects while evaluating the EDSL. I believe that I see it this way because the EDSLs that I write only allow functions to use a single "effect", the EDSL itself.

At this point I feel like the EDSL vs. effects points I was trying (and failing) to make are two sides of the same coin. The EDSL itself could be implemented as an effect, allowing programmers who use the EDSL to mix in other effects as needed. I suppose it depends on the domain and the target programmers whether you want to allow that sort of freedom.