<div dir="ltr">Strong +1 <div><br></div><div>this needs to happen. </div><div><br></div><div>I'm ok with the migration overhead, and nows a good time to clean up these core things now that we understand what they should be / how they're interrelated. Most of the concerns i've seen seem to be a rehash of those from AMP and friends, which worked out pretty well I think! </div><div><br></div><div>cheers!</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Sep 24, 2015 at 5:43 PM, Herbert Valerio Riedel <span dir="ltr"><<a href="mailto:hvr@gnu.org" target="_blank">hvr@gnu.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hello *,<br>
<br>
Concluding AMP and MFP, We (David and I) proudly present you the final<br>
installment of the Monad trilogy:<br>
<br>
<br>
Monad of no `return` Proposal<br>
=============================<br>
<br>
TLDR: To complete the AMP, turn `Monad(return)` method into a<br>
top-level binding aliasing `Applicative(pure)`.<br>
<br>
<br>
Current Situation<br>
-----------------<br>
<br>
With the implementation of Functor-Applicative-Monad Proposal (AMP)[1] and<br>
(at some point) the MonadFail proposal (MFP)[2] the AMP class hierarchy<br>
becomes<br>
<br>
<br>
class Functor f where<br>
fmap :: (a -> b) -> f a -> f b<br>
<br>
<br>
class Functor f => Applicative f where<br>
pure :: a -> f a<br>
(<*>) :: f (a -> b) -> f a -> f b<br>
<br>
(*>) :: f a -> f b -> f b<br>
u *> v = …<br>
<br>
(<*) :: f a -> f b -> f a<br>
u <* v = …<br>
<br>
<br>
class Applicative m => Monad m where<br>
(>>=) :: m a -> (a -> m b) -> m b<br>
<br>
return :: a -> m a<br>
return = pure<br>
<br>
(>>) :: m a -> m b -> m b<br>
m >> k = …<br>
<br>
<br>
class Monad m => MonadFail m where<br>
fail :: String -> m a<br>
<br>
<br>
Consequently, the `Monad` class is left with a now redundant `return`<br>
method as a historic artifact, as there's no compelling reason to<br>
have `pure` and `return` implemented differently.<br>
<br>
Traditionally, `return` is often used where `pure` would suffice<br>
today, forcing a `Monad` constraint even if a weaker `Applicative`<br>
would have sufficed.<br>
<br>
As a result, language extensions like `ApplicativeDo`[3] have to<br>
rewrite `return` to weaken its `Monad m =>` constraint to<br>
`Applicative m =>` in order to benefit existing code at the cost<br>
of introducing magic behavior at the type level.<br>
<br>
Finally, this redundancy becomes even more significant when viewed in<br>
light of the renewed Haskell standardisation process[7]: The next<br>
Haskell Report will almost certainly incorporate the AMP (and MFP)<br>
changes, and there's no justification for the Report to retain<br>
`return` as a method of `Monad`. A good reason would have been to<br>
retain backward compatibility with Haskell 2010. However, as the AMP<br>
superclass hierarchy requires `Monad` instances to be accompanied by<br>
`Applicative` instances (which aren't part of Haskell 2010, c.f. [6]),<br>
backward compatibility with Haskell 2010 goes out the window when it<br>
comes to defining `Monad` instances (unless via use of `-XCPP` or<br>
similar). Consequently, meeting the high bar for a formal document<br>
such as the Haskell Report demands that `Monad` shall not carry a<br>
redundant `return` method that serves no purpose anymore. Moreover,<br>
getting `return` out of the way is desirable to facilitate<br>
standardising potential candidates such as the earlier mentioned<br>
`ApplicativeDo` in the future and avoids the technical debt incurred<br>
by keeping around this language wart.<br>
<br>
<br>
Proposed Change<br>
---------------<br>
<br>
Remove `return` as a method from the `Monad` class and in its place<br>
define a top-level binding with the weaker `Applicative` typeclass<br>
constraint:<br>
<br>
<br>
-- | Legacy alias for 'pure'<br>
return :: Applicative f => a -> f a<br>
return = pure<br>
<br>
<br>
This allows existing code using `return` to benefit from a weaker<br>
typeclass constraint as well as cleaning the `Monad` class from a<br>
redundant method in the post-AMP world.<br>
<br>
A possible migration strategy is described further below.<br>
<br>
<br>
Compatibility Considerations<br>
----------------------------<br>
<br>
Generalizing the type signature of a function from a `Monad`<br>
constraint to its superclass `Applicative` doesn't cause new<br>
type-errors in existing code.<br>
<br>
However, moving a method to a top-level binding obviously breaks code<br>
that assumes `return` to be a class method. Foremost, code that<br>
defines `Monad` instances it at risk:<br>
<br>
### Instance Definitions<br>
<br>
Code defining `return` as part of an instance definition<br>
breaks. However, we had the foresight to provide a default<br>
implementation in `base-4.8` for `return` so that the following<br>
represents a proper minimal instance definition post-AMP:<br>
<br>
<br>
instance Functor Foo where<br>
fmap g foo = …<br>
<br>
instance Applicative Foo where<br>
pure x = …<br>
a1 <*> a2 = …<br>
<br>
instance Monad Foo where<br>
m >>= f = …<br>
<br>
-- NB: No mention of `return`<br>
<br>
<br>
Consequently, it is possible to write forward-compatible instances<br>
that are valid under this proposal starting with GHC 7.10/`base-4.8`.<br>
<br>
Heuristically `grep`ing through Hackage source-code reveals a<br>
non-negligible number of packages defining `Monad` instances with<br>
explicit `return` definitions[4]. This has a comparable impact to the<br>
AMP, and similarly will require a transition scheme aided by compiler<br>
warnings.<br>
<br>
### Module Import/Export Specifications<br>
<br>
A second source of incompatibility may be due to<br>
`import`s. Specifically module import that assert `return` to be a<br>
method of `Monad`, e.g.:<br>
<br>
import Control.Monad (Monad ((>>=), return))<br>
<br>
or<br>
<br>
import Prelude hiding (Monad(..))<br>
import Control.Monad (Monad(..)) as Monad<br>
<br>
f = Monad.return ()<br>
<br>
The dual situation can occur when re-exporting `return` via module<br>
export specifications.<br>
<br>
However, given that `return` is exported by `Prelude` and the examples<br>
above are rather artificial, we don't expect this to be a major source<br>
of breakage in the case of `return`. In fact, a heuristic grep[5] over<br>
Hackage source-code revealed only 21 packages affected.<br>
<br>
### Example for writing compatible code<br>
<br>
<br>
instance Functor Foo where<br>
fmap g foo = …<br>
<br>
instance Applicative Foo where<br>
pure x = …<br>
a1 <*> a2 = …<br>
<br>
instance Monad Foo where<br>
m >>= f = …<br>
<br>
#if !(MIN_VERSION_base(4,8,0))<br>
return = pure<br>
#endif<br>
<br>
<br>
Migration Strategy<br>
------------------<br>
<br>
The migration strategy is straightforward:<br>
<br>
**Phase 1** *(GHC 8.0)*: Implement new warning in GHC which gets<br>
triggered when `Monad` instances explicitly override the<br>
default `return` method implementation.<br>
<br>
**Phase 2** *(GHC 8.2 or later)*: When we're confident that the<br>
majority of Hackage has reacted to the warning (with the help of<br>
Stackage actively pursuing maintainers to update their packages) we<br>
turn the `return` method into a top-level binding and remove the<br>
warning implemented in Phase 1 from GHC again.<br>
<br>
<br>
Discussion period<br>
-----------------<br>
<br>
A discussion period of three weeks (until 2015-10-15) should be enough<br>
to allow everyone to chime in as well as leave enough time to make the<br>
required preparations for GHC 8.0 should this proposal pass as we hope.<br>
<br>
----<br>
<br>
[1]: <a href="https://wiki.haskell.org/Functor-Applicative-Monad_Proposal" rel="noreferrer" target="_blank">https://wiki.haskell.org/Functor-Applicative-Monad_Proposal</a><br>
[2]: <a href="https://wiki.haskell.org/MonadFail_Proposal" rel="noreferrer" target="_blank">https://wiki.haskell.org/MonadFail_Proposal</a><br>
[3]: <a href="https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo" rel="noreferrer" target="_blank">https://ghc.haskell.org/trac/ghc/wiki/ApplicativeDo</a><br>
[4]: <a href="https://gist.github.com/hvr/b0e34463d85b58f169d9" rel="noreferrer" target="_blank">https://gist.github.com/hvr/b0e34463d85b58f169d9</a><br>
[5]: <a href="https://gist.github.com/hvr/afcd040783d980594883" rel="noreferrer" target="_blank">https://gist.github.com/hvr/afcd040783d980594883</a><br>
[6]: <a href="https://ghc.haskell.org/trac/ghc/ticket/9590" rel="noreferrer" target="_blank">https://ghc.haskell.org/trac/ghc/ticket/9590</a><br>
[7]: <a href="https://mail.haskell.org/pipermail/haskell-prime/2015-September/003936.html" rel="noreferrer" target="_blank">https://mail.haskell.org/pipermail/haskell-prime/2015-September/003936.html</a><br>
<br>
--<br>
<br>_______________________________________________<br>
Libraries mailing list<br>
<a href="mailto:Libraries@haskell.org">Libraries@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries</a><br>
<br></blockquote></div><br></div>