The proposal monad of no return has been in the process of being implemented for a while. I’m collecting community feedback on the proposal (and specifically phase 3) in the hopes that as a community we can move forwards in making Haskell a better language. I encourage people to read the proposal above, but I’ll summarise and quote below where relevant.
What is this proposal?
The proposal outlines that with making Applicative
a superclass of Monad
(which happened with Functor-Applicative-Monad proposal pre-2015), the definitions of return
and (>>)
in the Monad
typeclass are redundant, as they can always be lawfully expressed in terms of pure
and (*>)
respectively. As such, they can and (by this proposal) should be removed from the typeclass, and defined in terms of their Applicative
equivalents.
Why should we do this?
Having return
and (>>)
introduces redundancy, redundancy that
violates the “making illegal states unrepresentable” idiom: Due to the default implementation of return this redundancy leads to error-prone situations which aren’t caught by the compiler; for instance, when
return
is removed while theApplicative
instance is left with apure = return
definition, this leads to a cyclic definition which can be quite tedious to debug as it only manifests at runtime by a hanging process. [1]
Additionally, keeping return
and (>>)
as Monad
methods means that in cases where a weaker Applicative
constraint would suffice we instead get a Monad
constraint.
Currently the status quo is to optimise (>>)
over (*>)
, which means that there are unexpected performance regressions[1] when generalising code; this is particularly noteworthy in the case of Foldable
/Traversable
methods, and additionally blocks the collation of their methods (for example mapM
vs traverse
) too.
A minor benefit would be to reduce the sizes of the class dictionaries, as well as avoid additional indirection through Monad
when Applicative
will suffice.
What’s been done so far?
So far we’ve completed phase 1 of the migration strategy. Phase 1 introduced a warning when non-lawful instances of return
and (>>)
are defined. This was implemented in GHC 8.0, and added to the default warning set in GHC 9.2.
What’s next?
Phase 2 moves return
and (>>)
to be top level definitions, and lets GHC ignore lawful definitions of return
and (>>)
in Monad
. As part of this, non-lawful definitions would result in a compile time error, while lawful definitions are left alone.
Phase 3 would then start warning about lawful implementations, similar to phase 1’s warning on unlawful implementations.
Phase 4 would finish off the proposal by removing GHC support for return
and (>>)
overrides, turning any such override into a compile time error.
So why are you posting about this?
It’s been a while since there’s been much update on this front, which is a shame for a proposal that would make Haskell a cleaner and more coherent language to program in. For many, the move to phase 2 won’t impact them, as unlawful definitions have been warned against for a time. Phase 3 has the potential to impact more people, so that is the primary focus of this post.
Why is phase 3 a sticking point?
It isn’t! What is currently lacking is community input in order to make the changes that phase 3, and this proposal, represents digestible and easily understood by the majority of Haskell users. We’re in no rush to break setups or have confused beginners ask why their tutorials are failing them.
What are we meant to do about this?
Getting feedback from the community is the primary purpose of this post, but I’m also taking the opportunity to ask for volunteers to split the work and make progress on this issue. If you want to take part, please let people know! GHC is a community project after all.