haskell / mtl

The Monad Transformer Library
http://www.haskell.org/haskellwiki/Monad_Transformers
Other
362 stars 63 forks source link

Allow multiple MonadReaders #73

Closed Atry closed 4 years ago

Atry commented 4 years ago

Currently ReaderT does not pass through nested MonadReader instances. If this PR get merged, multiple MonadReaders can be supported. For example, given the following type:

type X = ReaderT String (ReaderT Double IO)

It should supports both the following type classes:

We can then read either String or Double with TypeApplications language extension:

do
  s <- ask @String
  d <- ask @Double
  return $ s <> (show d)
chessai commented 4 years ago

There are a few issues with this.

  1. There are some reasons we might want overlap that have been detailed on the GHC Trac 1, but they can have weird interactions and generally go against the design philosophy of mtl-style coupling (in this case, r and m). Overall it would cause breakage, confusion, and be at least mildly betraying to mtl. The benefit is not worth it at all in my estimation. If you want multiple effects ala State, Except, etc., a different effect system may be desirable to you. I urge you to read the conversation in 1 as it contains a wealth of useful information related to this topic.

  2. In the case of ReaderT, ReaderT r (ReaderT r' m) is the same as ReaderT (r, r') m.

Closing for now, if you have any questions or concerns or want to make a case that this should be merged, feel free to re-open. Though, to be clear, I think the chances of that happening are near zero. At the very least, this could spark useful discussion toward other improvements.

Thanks!

EncodePanda commented 4 years ago

I am aware that a) ticket is already closed b) I'm asking for free knowledge here so I'm ok if this goes unanswered but @chessai if you would be so kind, could you elaborate a bit more why:

  1. "it would cause breakage, confusion" Why ppl would be confused? What would break?

  2. "be at least mildly betraying to mtl." Why that would betray MTL?

As far as I see it, the limitations of Functional Dependencies were the first things that made me explore other alternatives (polysemy). At that time I did not even know (to be honest) that it was due too the FunDep, I just could not simply do in MTL multiple error types, multiple states.

ekmett commented 3 years ago

1.)

Every attempt to asks with a function that is at all polymorphic in the argument type breaks in the scenario painted here, so almost every single invocation of ask everywhere would have to use explicit type applications. If you want to work with anything polymorphic where you have instances to pick things out? Scoped type variables everywhere so you can even name the types involved. There is a ton of code out there written in a (HasFoo e, HasBar e, HasQuux s, MonadReader e m, MonadState s m) => m a or which works in Haskell 98 + fundeps. None of that survives.

It is picking out a different point in the design space, but not one that is better.

2.) I wasn't the one who chimed in with the 'betrayal' bit, but the mtl is a pretty stable beast. It has been around for a long time, and there is a huge edifice of code built on top of it. Changing it willy-nilly to move to a state that isn't actually observably better shouldn't be done lightly. I would strongly reject a proposal to make this sort of change to the library. There are other libraries that build on top of transformers and strip out the fundeps, or which replace the fundeps with type families. The fun thing is you don't even have to give up access to the mtl for everything else, because the types are shared. This was the motivation behind splitting the mtl and transformers in the first place.