<p dir="ltr">For the benefit of people trying out code from papers, tutorials, etc., would it be possible to craft a permanent custom error message for explicit return definitions? I don't remember AMP having such for the new constraint; if it doesn't, maybe it should.</p>
<div class="gmail_quote">On Sep 24, 2015 5:43 PM, "Herbert Valerio Riedel" <<a href="mailto:hvr@gnu.org">hvr@gnu.org</a>> wrote:<br type="attribution"><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>