<div dir="ltr">+1 from me for both the spirit and the substance of this proposal. We've been talking about this in the abstract for a while now (since ICFP 2013 or so) and as concrete plans go, this strikes me as straightforward and implementable.<div><div><br></div><div>-Edward</div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Jun 9, 2015 at 10:43 PM, David Luposchainsky <span dir="ltr"><<a href="mailto:dluposchainsky@googlemail.com" target="_blank">dluposchainsky@googlemail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">-----BEGIN PGP SIGNED MESSAGE-----<br>
Hash: SHA1<br>
<br>
Hello *,<br>
<br>
the subject says it all. After we successfully put `=>`<br>
into Monad, it is time to remove something in return: `fail`.<br>
<br>
Like with the AMP, I wrote up the proposal in Markdown<br>
format on Github, which you can find below as a URL, and in<br>
verbatim copy at the end of this email. It provides an<br>
overview over the intended outcome, which design decisions<br>
we had to take, and how our initial plan for the transition<br>
looks like. There are also some issues left open to<br>
discussion.<br>
<br>
<a href="https://github.com/quchen/articles/blob/master/monad_fail.md" target="_blank">https://github.com/quchen/articles/blob/master/monad_fail.md</a><br>
<br>
Here's a short abstract:<br>
<br>
- - Move `fail` from `Monad` into a new class `MonadFail`.<br>
- - Code using failable patterns will receive a more<br>
  restrictive `MonadFail` constraint. Code without this<br>
  constraint will be safe to use for all Monads.<br>
- - Transition will take at least two GHC releases.<br>
  GHC 7.12 will include the new class, and generate<br>
  warnings asking users to make their failable patterns<br>
  compliant.<br>
- - Stackage showed an upper bound of less than 500 breaking<br>
  code fragments when compiled with the new desugaring.<br>
<br>
For more details, refer to the link or the paste at the end.<br>
<br>
<br>
Let's get going!<br>
<br>
David aka quchen<br>
<br>
<br>
<br>
<br>
<br>
===============================================================<br>
===============================================================<br>
===============================================================<br>
<br>
<br>
<br>
<br>
<br>
`MonadFail` proposal (MFP)<br>
==========================<br>
<br>
A couple of years ago, we proposed to make `Applicative` a superclass of<br>
`Monad`, which successfully killed the single most ugly thing in Haskell<br>
as of GHC 7.10.<br>
<br>
Now, it's time to tackle the other major issue with `Monad`: `fail` being a<br>
part of it.<br>
<br>
You can contact me as usual via IRC/Freenode as *quchen*, or by email to<br>
*dluposchainsky at the email service of Google*. This file will also be posted<br>
on the ghc-devs@ and libraries@ mailing lists, as well as on Reddit.<br>
<br>
<br>
<br>
Overview<br>
- --------<br>
<br>
- - **The problem** - reason for the proposal<br>
- - **MonadFail class** - the solution<br>
- - **Discussion** - explaining our design choices<br>
- - **Adapting old code** - how to prepare current code to transition smoothly<br>
- - **Esimating the breakage** - how much stuff we will break (spoiler: not much)<br>
- - **Transitional strategy** - how to break as little as possible while<br>
transitioning<br>
- - **Current status**<br>
<br>
<br>
<br>
<br>
The problem<br>
- -----------<br>
<br>
Currently, the `<-` symbol is unconditionally desugared as follows:<br>
<br>
```haskell<br>
do pat <- computation     >>>     let f pat = more<br>
   more                   >>>         f _ = fail "..."<br>
                          >>>     in  computation >>= f<br>
```<br>
<br>
The problem with this is that `fail` cannot (!) be sensibly implemented for<br>
many monads, for example `State`, `IO`, `Reader`. In those cases it defaults to<br>
`error`. As a consequence, in current Haskell, you can not use<br>
`Monad`-polymorphic code safely, because although it claims to work for all<br>
`Monad`s, it might just crash on you. This kind of implicit non-totality baked<br>
into the class is *terrible*.<br>
<br>
The goal of this proposal is adding the `fail` only when necessary and<br>
reflecting that in the type signature of the `do` block, so that it can be used<br>
safely, and more importantly, is guaranteed not to be used if the type<br>
signature does not say so.<br>
<br>
<br>
<br>
`MonadFail` class<br>
- -----------------<br>
<br>
To fix this, introduce a new typeclass:<br>
<br>
```haskell<br>
class Monad m => MonadFail m where<br>
    fail :: String -> m a<br>
```<br>
<br>
Desugaring can now be changed to produce this constraint when necessary. For<br>
this, we have to decide when a pattern match can not fail; if this is the case,<br>
we can omit inserting the `fail` call.<br>
<br>
The most trivial examples of unfailable patterns are of course those that match<br>
anywhere unconditionally,<br>
<br>
```haskell<br>
do x <- action     >>>     let f x = more<br>
   more            >>>     in  action >>= f<br>
```<br>
<br>
In particular, the programmer can assert any pattern be unfailable by making it<br>
irrefutable using a prefix tilde:<br>
<br>
```haskell<br>
do ~pat <- action     >>>     let f ~pat = more<br>
   more               >>>     in  action >>= f<br>
```<br>
<br>
A class of patterns that are conditionally failable are `newtype`s, and single<br>
constructor `data` types, which are unfailable by themselves, but may fail<br>
if matching on their fields is done with failable paterns.<br>
<br>
```haskell<br>
data Newtype a = Newtype a<br>
<br>
- -- "x" cannot fail<br>
do Newtype x <- action            >>>     let f (Newtype x) = more<br>
   more                           >>>     in  action >>= f<br>
<br>
- -- "Just x" can fail<br>
do Newtype (Just x) <- action     >>>     let f (Newtype (Just x)) = more<br>
   more                           >>>         f _ = fail "..."<br>
                                  >>>     in  action >>= f<br>
```<br>
<br>
`ViewPatterns` are as failable as the pattern the view is matched against.<br>
Patterns like `(Just -> Just x)` should generate a `MonadFail` constraint even<br>
when it's "obvious" from the view's implementation that the pattern will always<br>
match. From an implementor's perspective, this means that only types (and their<br>
constructors) have to be looked at, not arbitrary values (like functions),<br>
which is impossible to do statically in general.<br>
<br>
```haskell<br>
do (view ->  pat) <- action     >>>     let f (view ->  pat) = more<br>
   more                         >>>         f _ = fail "..."<br>
                                >>>     in  action >>= f<br>
<br>
do (view -> ~pat) <- action     >>>     let f (view -> ~pat) = more<br>
   more                         >>>     in  action >>= f<br>
```<br>
<br>
A similar issue arises for `PatternSynonyms`, which we cannot inspect during<br>
compilation sufficiently. A pattern synonym will therefore always be considered<br>
failable.<br>
<br>
```haskell<br>
do PatternSynonym x <- action     >>>     let f PatternSynonym x = more<br>
   more                           >>>     in f _ = fail "..."<br>
                                  >>>     in  action >>= f<br>
```<br>
<br>
<br>
<br>
Discussion<br>
- ----------<br>
<br>
- - Although for many `MonadPlus` `fail _ = mzero`, a separate `MonadFail` class<br>
  should be created instead of just using that.<br>
<br>
    - A parser might fail with an error message involving positional<br>
      information. Some libraries, like `Binary`, provide `fail` as their<br>
      only interface to fail a decoding step.<br>
<br>
    - Although `STM` is `MonadPlus`, it uses the default `fail = error`. It<br>
      will therefore not get a `MonadFail` instance.<br>
<br>
- - What laws should `fail` follow? **Left zero**,<br>
<br>
  ```haskell<br>
  ∀ s f.  fail s >>= f  ≡  fail s<br>
  ```<br>
<br>
  A call to `fail` should abort the computation. In this sense, `fail` would<br>
  become a close relative of `mzero`. It would work well with the common<br>
  definition `fail _ = mzero`, and give a simple guideline to the intended<br>
  usage and effect of the `MonadFail` class.<br>
<br>
- - Rename `fail`? **No.** Old code might use `fail` explicitly and we might<br>
  avoid breaking it, the Report talks about `fail`, and we have a solid<br>
  migration strategy that does not require a renaming.<br>
<br>
- - Remove the `String` argument? **No.** The `String` might help error reporting<br>
  and debugging. `String` may be ugly, but it's the de facto standard for<br>
  simple text in GHC. No high performance string operations are to be<br>
  expected with `fail`, so this breaking change would in no way be justified.<br>
  Also note that explicit `fail` calls would break if we removed the argument.<br>
<br>
- - How sensitive would existing code be to subtle changes in the strictness<br>
  behaviour of `do` notation pattern matching? **It doesn't.** The<br>
  implementation does not affect strictness at all, only the desugaring step.<br>
  Care must be taken when fixing warnings by making patterns irrefutable using<br>
  `~`, as that *does* affect strictness. (Cf. difference between lazy/strict<br>
  State)<br>
<br>
- - The `Monad` constraint for `MonadFail` seems unnecessary. Should we drop or<br>
  relax it? What other things should be considered?<br>
<br>
  - Applicative `do` notation is coming sooner or later, `fail` might be useful<br>
    in this more general scenario. Due to the AMP, it is trivial to change<br>
    the `MonadFail` superclass to `Applicative` later. (The name will be a bit<br>
    misleading, but it's a very small price to pay.)<br>
  - The class might be misused for a strange pointed type if left without<br>
    any constraint. This is not the intended use at all.<br>
<br>
  I think we should keep the `Monad` superclass for three main reasons:<br>
<br>
  - We don't want to see `(Monad m, MonadFail m) =>` all over the place.<br>
  - The primary intended use of `fail` is for desugaring do-notation anyway.<br>
  - Retroactively removing superclasses is easy, but adding them is hard<br>
    (see AMP).<br>
<br>
<br>
<br>
<br>
Adapting old code<br>
- -----------------<br>
<br>
- - Help! My code is broken because of a missing `MonadFail` instance!<br>
<br>
  *Here are your options:*<br>
<br>
    1. Write a `MonadFail` instance (and bring it into scope)<br>
<br>
       ```haskell<br>
       #if !MIN_VERSION_base(4,11,0)<br>
       -- Control.Monad.Fail import will become redundant in GHC 7.16+<br>
       import qualified Control.Monad.Fail as Fail<br>
       #endif<br>
       import Control.Monad<br>
<br>
       instance Monad Foo where<br>
         (>>=) = <...bind impl...><br>
         -- NB: `return` defaults to `pure`<br>
<br>
       #if !MIN_VERSION_base(4,11,0)<br>
         -- Monad(fail) will be removed in GHC 7.16+<br>
         fail = Fail.fail<br>
       #endif<br>
<br>
       instance MonadFail Foo where<br>
         fail = <...fail implementation...><br>
       ```<br>
<br>
    2. Change your pattern to be irrefutable<br>
<br>
    3. Emulate the old behaviour by desugaring the pattern match by hand:<br>
<br>
       ```haskell<br>
       do Left e <- foobar<br>
          stuff<br>
       ```<br>
<br>
       becomes<br>
<br>
       ```haskell<br>
       do x <- foobar<br>
          e <- case foobar of<br>
              Left e' -> e'<br>
              Right r -> error "Pattern match failed" -- Boooo<br>
          stuff<br>
       ```<br>
<br>
       The point is you'll have to do your dirty laundry yourself now if you<br>
       have a value that *you* know will always match, and if you don't handle<br>
       the other patterns you'll get incompleteness warnings, and the compiler<br>
       won't silently eat those for you.<br>
<br>
- - Help! My code is broken because you removed `fail` from `Monad`, but my class<br>
  defines it!<br>
<br>
  *Delete that part of the instance definition.*<br>
<br>
<br>
<br>
Esimating the breakage<br>
- ----------------------<br>
<br>
Using our initial implementation, I compiled stackage-nightly, and grepped the<br>
logs for found "invalid use of fail desugaring". Assuming my implementation<br>
is correct, the number of "missing `MonadFail`" warnings generated is 487.<br>
Note that I filtered out `[]`, `Maybe` and `ReadPrec`, since those can be given<br>
a `MonadFail` instance from within GHC, and no breakage is expected from them.<br>
<br>
The build logs can be found [here][stackage-logs]. Search for "failable<br>
pattern" to find your way to the still pretty raw warnings.<br>
<br>
<br>
<br>
<br>
Transitional strategy<br>
- ---------------------<br>
<br>
The roadmap is similar to the [AMP][amp], the main difference being that since<br>
`MonadFail` does not exist yet, we have to introduce new functionality and then<br>
switch to it.<br>
<br>
* **GHC 7.12 / base-4.9**<br>
<br>
    - Add module `Control.Monad.Fail` with new class `MonadFail(fail)` so<br>
      people can start writing instances for it.<br>
<br>
      `Control.Monad` only re-exports the class `MonadFail`, but not its<br>
      `fail` method.<br>
<br>
      NB: At this point, `Control.Monad.Fail.fail` clashes with<br>
      `Prelude.fail` and `Control.Monad.fail`.<br>
<br>
    - *(non-essential)* Add a language extension `-XMonadFail` that<br>
      changes desugaring to use `MonadFail(fail)` instead of `Monad(fail)`.<br>
<br>
      This has the effect that typechecking will infer a `MonadFail` constraint<br>
      for `do` blocks with failable patterns, just as it is planned to do when<br>
      the entire thing is done.<br>
<br>
    - Warn when a `do`-block that contains a failable pattern is<br>
      desugared, but there is no `MonadFail`-instance in scope: "Please add the<br>
      instance or change your pattern matching." Add a flag to control whether<br>
      this warning appears.<br>
<br>
    - Warn when an instance implements the `fail` function (or when `fail`<br>
      is imported as a method of `Monad`), as it will be removed from the<br>
      `Monad` class in the future. (See also [GHC #10071][trac-10071])<br>
<br>
3. GHC 7.14<br>
<br>
    - Switch `-XMonadFail` on by default.<br>
    - Remove the desugaring warnings.<br>
<br>
3. GHC 7.16<br>
<br>
    - Remove `-XMonadFail`, leaving its effects on at all times.<br>
    - Remove `fail` from `Monad`.<br>
    - Instead, re-export `Control.Monad.Fail.fail` as `Prelude.fail` and<br>
      `Control.Monad.fail`.<br>
    - `Control.Monad.Fail` is now a redundant module that can be considered<br>
      deprecated.<br>
<br>
<br>
<br>
Current status<br>
- --------------<br>
<br>
- - [ZuriHac 2015 (29.5. - 31.5.)][zurihac]: Franz Thoma (@fmthoma) and me<br>
  (David Luposchainsky aka @quchen) started implementing the MFP in GHC.<br>
<br>
    - Desugaring to the new `fail` can be controlled via a new langauge<br>
      extension, `MonadFailDesugaring`.<br>
    - If the language extension is turned off, a warning will be emitted for<br>
      code that would break if it was enabled.<br>
    - Warnings are emitted for types that *have* a *MonadFail* instance. This<br>
      still needs to be fixed.<br>
    - The error message are readable, but should be more so. We're still<br>
      on this.<br>
- - 2015-06-09: Estimated breakage by compiling Stackage. Smaller than expected.<br>
<br>
<br>
<br>
[amp]: <a href="https://github.com/quchen/articles/blob/master/applicative_monad.md
[stackage-logs" target="_blank">https://github.com/quchen/articles/blob/master/applicative_monad.md<br>
[stackage-logs</a>]: <a href="https://www.dropbox.com/s/knz0i979skam4zs/stackage-build.tar.xz?dl=0
[trac-10071" target="_blank">https://www.dropbox.com/s/knz0i979skam4zs/stackage-build.tar.xz?dl=0<br>
[trac-10071</a>]: <a href="https://ghc.haskell.org/trac/ghc/ticket/10071" target="_blank">https://ghc.haskell.org/trac/ghc/ticket/10071</a><br>
[zurihac]: <a href="https://wiki.haskell.org/ZuriHac2015" target="_blank">https://wiki.haskell.org/ZuriHac2015</a><br>
<br>
-----BEGIN PGP SIGNATURE-----<br>
Version: GnuPG v1<br>
<br>
iQEcBAEBAgAGBQJVd0/yAAoJELrQsaT5WQUshbUH/A3W0itVAk7ao8rtxId5unCJ<br>
7StriKVkTyLAkkrbRJngM4MHEKiCsoyIgr8kBIwSHgk194GxeP2NCF4ijuBZoDBt<br>
+Uci+6BCBinV8+OzfrfTcJb4+8iw1j+eLWJ/Nz/JDMDNCiyzyC0SMsqGa+ssOz7H<br>
/2mqPkQjQgpHuP5PTRLHKPPIsayCQvTbZR1f14KhuMN2SPDE+WY4rqugu//XuIkN<br>
u1YssIf5l8mEez/1ljaqGL55cTI0UNg2z0iA0bFl/ajHaeQ6mc5BAevWfSohAMW7<br>
7PIt13p9NIaMHnikmI+YJszm2IEaXuv47mGgbyDV//nHq3fwWN+naB+1mPX2eSU=<br>
=vPAL<br>
-----END PGP SIGNATURE-----<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" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries</a><br>
</blockquote></div><br></div>