Skip to content

Monad Transformer typeclass #4727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
johnynek opened this issue Mar 4, 2025 · 2 comments
Open

Monad Transformer typeclass #4727

johnynek opened this issue Mar 4, 2025 · 2 comments

Comments

@johnynek
Copy link
Contributor

johnynek commented Mar 4, 2025

I saw this: #4724 and wondered if we have a monad transformer typeclass similar to: https://hackage.haskell.org/package/transformers-0.6.1.2/docs/Control-Monad-Trans-Class.html

I don't see one. I can imagine something like:

trait MonadTrans[T[_[_], _]] {
  def liftK[M[_]: Monad]: FunctionK[M, [A] =>> T[M, A]]
  def lift[M[_]: Monad, A](ma: M[A]): T[M, A]

  implicit def transMonad[M[_]: Monad]: Monad[[A] =>> T[M, A]]
}

And possibly even:

trait MonadTrans[T[_[_], _]] {
  def liftK[M[_]: Monad]: FunctionK[M, [A] =>> T[M, A]]
  def lift[M[_]: Monad, A](ma: M[A]): T[M, A]
  
  implicit def transMonad[M[_]: Monad]: Monad[[A] =>> T[M, A]]
  
  type RunT[_]
  def run[M[_]: Monad, A](fa: T[M, A]): M[RunT[A]]
  def liftRun[M[_]: Monad, A](run: M[RunT[A]]): T[M, A]
}

since I think all the transformers seem to have some notion of a "run" version. The laws would be:

MonadTrans[T].lift(fa) == MonadTrans[T].liftK[M](fa)

MonadTrans[T].lift(Monad[M].pure(a)) == MonadTrans[T].transMonad[M].pure(a)

MonadTrans[T].lift(ma.flatMap(f)) == MonadTrans[T].lift(ma).flatMap(a => MonadTrans[T].lift(f(a)))

MonadTrans[T].liftRun(MonadTrans[T].run(fa)) == fa

The argument against this, I think, would be that it's not clear what useful functions you would write against MonadTrans except perhaps for having a clear way to lift: MonadTrans[OptionT].lift(fa)

@armanbilge
Copy link
Member

See also MonadPartialOrder

@djspiewak
Copy link
Member

djspiewak commented Mar 5, 2025

since I think all the transformers seem to have some notion of a "run" version. The laws would be:

Definitely not all! You may also be interested in this: https://hackage.haskell.org/package/layers-0.1/docs/Control-Monad-Layer.html This space has been explored quite a bit in the past.

Taking a step back, the first two functions in MonadTrans are basically a Pointed class lifted into an endofunctor category, and it has all the corresponding problems. Without run, you can't really talk much about laws for the pointing itself, though as you demonstrate you can constrain the nature of the categorical lifting, which is not quite the same thing.

Taking a step further back, what you're scratching at (the difficulty of un-layering a transformer stack once composed) is, IMO, the crux of the problem with monad transformers in general. Their generality means it's impossible to take them apart in the same way you put them together, which is very frustrating and causes a whole series of knock-on problems (see also: CE's Outcome.Succeeded case, the way that Dispatcher behaves in the presence of transformers, fun interactions with Traverse, etc etc). Broadly, this is because transformers are general datatypes and functions, rather than restricted handlers of some shape. This is in turn the reason that almost all abstracted machinery around transformers focuses on the construction of such effects rather than the interpretation.

Not to appeal to history, but having spent about a decade and a half grappling with this exact frustration, I think in the end the best compromise solution is MonadPartialOrder. It solves a decent number of problems that have solutions, without attempting to solve anything which doesn't have a general solution, and it kind of bows to the general asymmetry of transformer composition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants