MonadFail proposal (MFP): Moving fail out of Monad

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
54 messages Options
123
Reply | Threaded
Open this post in threaded view
|

MonadFail proposal (MFP): Moving fail out of Monad

David Luposchainsky
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hello *,

the subject says it all. After we successfully put `=>`
into Monad, it is time to remove something in return: `fail`.

Like with the AMP, I wrote up the proposal in Markdown
format on Github, which you can find below as a URL, and in
verbatim copy at the end of this email. It provides an
overview over the intended outcome, which design decisions
we had to take, and how our initial plan for the transition
looks like. There are also some issues left open to
discussion.

https://github.com/quchen/articles/blob/master/monad_fail.md

Here's a short abstract:

- - Move `fail` from `Monad` into a new class `MonadFail`.
- - Code using failable patterns will receive a more
  restrictive `MonadFail` constraint. Code without this
  constraint will be safe to use for all Monads.
- - Transition will take at least two GHC releases.
  GHC 7.12 will include the new class, and generate
  warnings asking users to make their failable patterns
  compliant.
- - Stackage showed an upper bound of less than 500 breaking
  code fragments when compiled with the new desugaring.

For more details, refer to the link or the paste at the end.


Let's get going!

David aka quchen





===============================================================
===============================================================
===============================================================





`MonadFail` proposal (MFP)
==========================

A couple of years ago, we proposed to make `Applicative` a superclass of
`Monad`, which successfully killed the single most ugly thing in Haskell
as of GHC 7.10.

Now, it's time to tackle the other major issue with `Monad`: `fail` being a
part of it.

You can contact me as usual via IRC/Freenode as *quchen*, or by email to
*dluposchainsky at the email service of Google*. This file will also be posted
on the ghc-devs@ and libraries@ mailing lists, as well as on Reddit.



Overview
- --------

- - **The problem** - reason for the proposal
- - **MonadFail class** - the solution
- - **Discussion** - explaining our design choices
- - **Adapting old code** - how to prepare current code to transition smoothly
- - **Esimating the breakage** - how much stuff we will break (spoiler: not much)
- - **Transitional strategy** - how to break as little as possible while
transitioning
- - **Current status**




The problem
- -----------

Currently, the `<-` symbol is unconditionally desugared as follows:

```haskell
do pat <- computation     >>>     let f pat = more
   more                   >>>         f _ = fail "..."
                          >>>     in  computation >>= f
```

The problem with this is that `fail` cannot (!) be sensibly implemented for
many monads, for example `State`, `IO`, `Reader`. In those cases it defaults to
`error`. As a consequence, in current Haskell, you can not use
`Monad`-polymorphic code safely, because although it claims to work for all
`Monad`s, it might just crash on you. This kind of implicit non-totality baked
into the class is *terrible*.

The goal of this proposal is adding the `fail` only when necessary and
reflecting that in the type signature of the `do` block, so that it can be used
safely, and more importantly, is guaranteed not to be used if the type
signature does not say so.



`MonadFail` class
- -----------------

To fix this, introduce a new typeclass:

```haskell
class Monad m => MonadFail m where
    fail :: String -> m a
```

Desugaring can now be changed to produce this constraint when necessary. For
this, we have to decide when a pattern match can not fail; if this is the case,
we can omit inserting the `fail` call.

The most trivial examples of unfailable patterns are of course those that match
anywhere unconditionally,

```haskell
do x <- action     >>>     let f x = more
   more            >>>     in  action >>= f
```

In particular, the programmer can assert any pattern be unfailable by making it
irrefutable using a prefix tilde:

```haskell
do ~pat <- action     >>>     let f ~pat = more
   more               >>>     in  action >>= f
```

A class of patterns that are conditionally failable are `newtype`s, and single
constructor `data` types, which are unfailable by themselves, but may fail
if matching on their fields is done with failable paterns.

```haskell
data Newtype a = Newtype a

- -- "x" cannot fail
do Newtype x <- action            >>>     let f (Newtype x) = more
   more                           >>>     in  action >>= f

- -- "Just x" can fail
do Newtype (Just x) <- action     >>>     let f (Newtype (Just x)) = more
   more                           >>>         f _ = fail "..."
                                  >>>     in  action >>= f
```

`ViewPatterns` are as failable as the pattern the view is matched against.
Patterns like `(Just -> Just x)` should generate a `MonadFail` constraint even
when it's "obvious" from the view's implementation that the pattern will always
match. From an implementor's perspective, this means that only types (and their
constructors) have to be looked at, not arbitrary values (like functions),
which is impossible to do statically in general.

```haskell
do (view ->  pat) <- action     >>>     let f (view ->  pat) = more
   more                         >>>         f _ = fail "..."
                                >>>     in  action >>= f

do (view -> ~pat) <- action     >>>     let f (view -> ~pat) = more
   more                         >>>     in  action >>= f
```

A similar issue arises for `PatternSynonyms`, which we cannot inspect during
compilation sufficiently. A pattern synonym will therefore always be considered
failable.

```haskell
do PatternSynonym x <- action     >>>     let f PatternSynonym x = more
   more                           >>>     in f _ = fail "..."
                                  >>>     in  action >>= f
```



Discussion
- ----------

- - Although for many `MonadPlus` `fail _ = mzero`, a separate `MonadFail` class
  should be created instead of just using that.

    - A parser might fail with an error message involving positional
      information. Some libraries, like `Binary`, provide `fail` as their
      only interface to fail a decoding step.

    - Although `STM` is `MonadPlus`, it uses the default `fail = error`. It
      will therefore not get a `MonadFail` instance.

- - What laws should `fail` follow? **Left zero**,

  ```haskell
  ∀ s f.  fail s >>= f  ≡  fail s
  ```

  A call to `fail` should abort the computation. In this sense, `fail` would
  become a close relative of `mzero`. It would work well with the common
  definition `fail _ = mzero`, and give a simple guideline to the intended
  usage and effect of the `MonadFail` class.

- - Rename `fail`? **No.** Old code might use `fail` explicitly and we might
  avoid breaking it, the Report talks about `fail`, and we have a solid
  migration strategy that does not require a renaming.

- - Remove the `String` argument? **No.** The `String` might help error reporting
  and debugging. `String` may be ugly, but it's the de facto standard for
  simple text in GHC. No high performance string operations are to be
  expected with `fail`, so this breaking change would in no way be justified.
  Also note that explicit `fail` calls would break if we removed the argument.

- - How sensitive would existing code be to subtle changes in the strictness
  behaviour of `do` notation pattern matching? **It doesn't.** The
  implementation does not affect strictness at all, only the desugaring step.
  Care must be taken when fixing warnings by making patterns irrefutable using
  `~`, as that *does* affect strictness. (Cf. difference between lazy/strict
  State)

- - The `Monad` constraint for `MonadFail` seems unnecessary. Should we drop or
  relax it? What other things should be considered?

  - Applicative `do` notation is coming sooner or later, `fail` might be useful
    in this more general scenario. Due to the AMP, it is trivial to change
    the `MonadFail` superclass to `Applicative` later. (The name will be a bit
    misleading, but it's a very small price to pay.)
  - The class might be misused for a strange pointed type if left without
    any constraint. This is not the intended use at all.

  I think we should keep the `Monad` superclass for three main reasons:

  - We don't want to see `(Monad m, MonadFail m) =>` all over the place.
  - The primary intended use of `fail` is for desugaring do-notation anyway.
  - Retroactively removing superclasses is easy, but adding them is hard
    (see AMP).




Adapting old code
- -----------------

- - Help! My code is broken because of a missing `MonadFail` instance!

  *Here are your options:*

    1. Write a `MonadFail` instance (and bring it into scope)

       ```haskell
       #if !MIN_VERSION_base(4,11,0)
       -- Control.Monad.Fail import will become redundant in GHC 7.16+
       import qualified Control.Monad.Fail as Fail
       #endif
       import Control.Monad

       instance Monad Foo where
         (>>=) = <...bind impl...>
         -- NB: `return` defaults to `pure`

       #if !MIN_VERSION_base(4,11,0)
         -- Monad(fail) will be removed in GHC 7.16+
         fail = Fail.fail
       #endif

       instance MonadFail Foo where
         fail = <...fail implementation...>
       ```

    2. Change your pattern to be irrefutable

    3. Emulate the old behaviour by desugaring the pattern match by hand:

       ```haskell
       do Left e <- foobar
          stuff
       ```

       becomes

       ```haskell
       do x <- foobar
          e <- case foobar of
              Left e' -> e'
              Right r -> error "Pattern match failed" -- Boooo
          stuff
       ```

       The point is you'll have to do your dirty laundry yourself now if you
       have a value that *you* know will always match, and if you don't handle
       the other patterns you'll get incompleteness warnings, and the compiler
       won't silently eat those for you.

- - Help! My code is broken because you removed `fail` from `Monad`, but my class
  defines it!

  *Delete that part of the instance definition.*



Esimating the breakage
- ----------------------

Using our initial implementation, I compiled stackage-nightly, and grepped the
logs for found "invalid use of fail desugaring". Assuming my implementation
is correct, the number of "missing `MonadFail`" warnings generated is 487.
Note that I filtered out `[]`, `Maybe` and `ReadPrec`, since those can be given
a `MonadFail` instance from within GHC, and no breakage is expected from them.

The build logs can be found [here][stackage-logs]. Search for "failable
pattern" to find your way to the still pretty raw warnings.




Transitional strategy
- ---------------------

The roadmap is similar to the [AMP][amp], the main difference being that since
`MonadFail` does not exist yet, we have to introduce new functionality and then
switch to it.

* **GHC 7.12 / base-4.9**

    - Add module `Control.Monad.Fail` with new class `MonadFail(fail)` so
      people can start writing instances for it.

      `Control.Monad` only re-exports the class `MonadFail`, but not its
      `fail` method.

      NB: At this point, `Control.Monad.Fail.fail` clashes with
      `Prelude.fail` and `Control.Monad.fail`.

    - *(non-essential)* Add a language extension `-XMonadFail` that
      changes desugaring to use `MonadFail(fail)` instead of `Monad(fail)`.

      This has the effect that typechecking will infer a `MonadFail` constraint
      for `do` blocks with failable patterns, just as it is planned to do when
      the entire thing is done.

    - Warn when a `do`-block that contains a failable pattern is
      desugared, but there is no `MonadFail`-instance in scope: "Please add the
      instance or change your pattern matching." Add a flag to control whether
      this warning appears.

    - Warn when an instance implements the `fail` function (or when `fail`
      is imported as a method of `Monad`), as it will be removed from the
      `Monad` class in the future. (See also [GHC #10071][trac-10071])

3. GHC 7.14

    - Switch `-XMonadFail` on by default.
    - Remove the desugaring warnings.

3. GHC 7.16

    - Remove `-XMonadFail`, leaving its effects on at all times.
    - Remove `fail` from `Monad`.
    - Instead, re-export `Control.Monad.Fail.fail` as `Prelude.fail` and
      `Control.Monad.fail`.
    - `Control.Monad.Fail` is now a redundant module that can be considered
      deprecated.



Current status
- --------------

- - [ZuriHac 2015 (29.5. - 31.5.)][zurihac]: Franz Thoma (@fmthoma) and me
  (David Luposchainsky aka @quchen) started implementing the MFP in GHC.

    - Desugaring to the new `fail` can be controlled via a new langauge
      extension, `MonadFailDesugaring`.
    - If the language extension is turned off, a warning will be emitted for
      code that would break if it was enabled.
    - Warnings are emitted for types that *have* a *MonadFail* instance. This
      still needs to be fixed.
    - The error message are readable, but should be more so. We're still
      on this.
- - 2015-06-09: Estimated breakage by compiling Stackage. Smaller than expected.



[amp]: https://github.com/quchen/articles/blob/master/applicative_monad.md
[stackage-logs]: https://www.dropbox.com/s/knz0i979skam4zs/stackage-build.tar.xz?dl=0
[trac-10071]: https://ghc.haskell.org/trac/ghc/ticket/10071
[zurihac]: https://wiki.haskell.org/ZuriHac2015

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJVd0/yAAoJELrQsaT5WQUshbUH/A3W0itVAk7ao8rtxId5unCJ
7StriKVkTyLAkkrbRJngM4MHEKiCsoyIgr8kBIwSHgk194GxeP2NCF4ijuBZoDBt
+Uci+6BCBinV8+OzfrfTcJb4+8iw1j+eLWJ/Nz/JDMDNCiyzyC0SMsqGa+ssOz7H
/2mqPkQjQgpHuP5PTRLHKPPIsayCQvTbZR1f14KhuMN2SPDE+WY4rqugu//XuIkN
u1YssIf5l8mEez/1ljaqGL55cTI0UNg2z0iA0bFl/ajHaeQ6mc5BAevWfSohAMW7
7PIt13p9NIaMHnikmI+YJszm2IEaXuv47mGgbyDV//nHq3fwWN+naB+1mPX2eSU=
=vPAL
-----END PGP SIGNATURE-----
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Henning Thielemann

On Tue, 9 Jun 2015, David Luposchainsky wrote:

> the subject says it all. After we successfully put `=>`
> into Monad, it is time to remove something in return: `fail`.

Let me start the "+1" flood with my own +1.


> - - Stackage showed an upper bound of less than 500 breaking
>  code fragments when compiled with the new desugaring.

I found that code breakage generally is not a big problem for me. I can
fix code and that's it. I am more concerned with writing code that can be
compiled by several GHC versions.
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Evan Laforge
In reply to this post by David Luposchainsky
Definitely +1 here, but if you have an irrefutable pattern:

~(Just x) <- action
... expresison involving x ...

My understanding is that 'fail' is only called if you evaluate 'x'.
So I guess any expression with 'x' in it would also imply the
MonadFail constraint.  So ~ might just move the constraint until the
next line.  Or if you pass 'x' to someone else, do they now get
MonadFail?

Would it be worth pointing that out?

On Tue, Jun 9, 2015 at 1:43 PM, David Luposchainsky
<[hidden email]> wrote:

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

Re: MonadFail proposal (MFP): Moving fail out of Monad

Henning Thielemann

On Tue, 9 Jun 2015, Evan Laforge wrote:

> Definitely +1 here, but if you have an irrefutable pattern:
>
> ~(Just x) <- action
> ... expresison involving x ...
>
> My understanding is that 'fail' is only called if you evaluate 'x'.

I think you would no longer have an (overloadable) "fail", but a
(hardwired) "error".
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Herbert Valerio Riedel
In reply to this post by David Luposchainsky
On 2015-06-09 at 22:43:30 +0200, David Luposchainsky wrote:

[...]

> https://github.com/quchen/articles/blob/master/monad_fail.md
>
> Here's a short abstract:
>
> - Move `fail` from `Monad` into a new class `MonadFail`.

[...]

+1 obviously :-)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Joachim Breitner-2
In reply to this post by David Luposchainsky
Hi,

Am Dienstag, den 09.06.2015, 22:43 +0200 schrieb David Luposchainsky:
> the subject says it all. After we successfully put `=>`
> into Monad, it is time to remove something in return: `fail`.

your proposal is solid work and very thorough. Thanks!

Greetings,
Joachim


--
Joachim “nomeata” Breitner
  [hidden email]http://www.joachim-breitner.de/
  Jabber: [hidden email]  • GPG-Key: 0xF0FBF51F
  Debian Developer: [hidden email]

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Henning Thielemann
In reply to this post by David Luposchainsky

now some details to the discussion:


On Tue, 9 Jun 2015, David Luposchainsky wrote:

> https://github.com/quchen/articles/blob/master/monad_fail.md
>
> Here's a short abstract:
>
> - - Move `fail` from `Monad` into a new class `MonadFail`.

You think about making MonadFail a subclass of Applicative where the name
MonadFail becomes misleading - how about naming it Fail instead?


> - - Remove the `String` argument? **No.** The `String` might help error reporting
>  and debugging. `String` may be ugly, but it's the de facto standard for
>  simple text in GHC. No high performance string operations are to be
>  expected with `fail`, so this breaking change would in no way be justified.
>  Also note that explicit `fail` calls would break if we removed the argument.

You know that I am concerned with a strict separation of programming
errors and exceptions. "fail" is part of the confusion between the two
concepts. If "fail" is inserted by the compiler as desugaring of a pattern
match then it gets a compiler generated string as argument. This string
refers to program details like source locations and constructor names that
have no meaning for the program user. They are intended for debugging,
they address the programmer. This implies that "fail" indicates a
programming error. In contrast to that, in a monad like Maybe we want to
convert a mismatching pattern to Nothing. The compiler generated argument
to "fail" does not show up in the Nothing result. This usecase is clearly
not for debugging but for regular use in a program. We only want to get
the exceptional value Nothing.

We should clearly decide what "fail" is intended for - for programming
errors or for exceptions. I guess that people want to use it in the second
sense, e.g. something like "guard" driven by a pattern match in the Maybe
or list monad. But then "fail" must not have an argument because what user
related information can the compiler add automatically? Instead of
removing the String argument from "fail", the compiler could simply call
"mzero" instead. If we want to use "fail" for exceptions then it would be
also ok for parser libraries to use it for parser failures. Nonetheless
they might be better served with an Except-like monad where they can
choose more specific types for the exception messages, e.g. parser errors
with source location. In summary I think that the cleanest solution would
be to phase out "fail" completely in the long run. But using "mzero"
instead of "fail" for monadic pattern matches would alter the proposal
considerably. Using "mzero" requires a monad with "mplus" and for support
of monads with "mzero" but no "mplus" we need to split MonadPlus, too.
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Edward Kmett-2
In reply to this post by David Luposchainsky
+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.

-Edward

On Tue, Jun 9, 2015 at 10:43 PM, David Luposchainsky <[hidden email]> wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hello *,

the subject says it all. After we successfully put `=>`
into Monad, it is time to remove something in return: `fail`.

Like with the AMP, I wrote up the proposal in Markdown
format on Github, which you can find below as a URL, and in
verbatim copy at the end of this email. It provides an
overview over the intended outcome, which design decisions
we had to take, and how our initial plan for the transition
looks like. There are also some issues left open to
discussion.

https://github.com/quchen/articles/blob/master/monad_fail.md

Here's a short abstract:

- - Move `fail` from `Monad` into a new class `MonadFail`.
- - Code using failable patterns will receive a more
  restrictive `MonadFail` constraint. Code without this
  constraint will be safe to use for all Monads.
- - Transition will take at least two GHC releases.
  GHC 7.12 will include the new class, and generate
  warnings asking users to make their failable patterns
  compliant.
- - Stackage showed an upper bound of less than 500 breaking
  code fragments when compiled with the new desugaring.

For more details, refer to the link or the paste at the end.


Let's get going!

David aka quchen





===============================================================
===============================================================
===============================================================





`MonadFail` proposal (MFP)
==========================

A couple of years ago, we proposed to make `Applicative` a superclass of
`Monad`, which successfully killed the single most ugly thing in Haskell
as of GHC 7.10.

Now, it's time to tackle the other major issue with `Monad`: `fail` being a
part of it.

You can contact me as usual via IRC/Freenode as *quchen*, or by email to
*dluposchainsky at the email service of Google*. This file will also be posted
on the ghc-devs@ and libraries@ mailing lists, as well as on Reddit.



Overview
- --------

- - **The problem** - reason for the proposal
- - **MonadFail class** - the solution
- - **Discussion** - explaining our design choices
- - **Adapting old code** - how to prepare current code to transition smoothly
- - **Esimating the breakage** - how much stuff we will break (spoiler: not much)
- - **Transitional strategy** - how to break as little as possible while
transitioning
- - **Current status**




The problem
- -----------

Currently, the `<-` symbol is unconditionally desugared as follows:

```haskell
do pat <- computation     >>>     let f pat = more
   more                   >>>         f _ = fail "..."
                          >>>     in  computation >>= f
```

The problem with this is that `fail` cannot (!) be sensibly implemented for
many monads, for example `State`, `IO`, `Reader`. In those cases it defaults to
`error`. As a consequence, in current Haskell, you can not use
`Monad`-polymorphic code safely, because although it claims to work for all
`Monad`s, it might just crash on you. This kind of implicit non-totality baked
into the class is *terrible*.

The goal of this proposal is adding the `fail` only when necessary and
reflecting that in the type signature of the `do` block, so that it can be used
safely, and more importantly, is guaranteed not to be used if the type
signature does not say so.



`MonadFail` class
- -----------------

To fix this, introduce a new typeclass:

```haskell
class Monad m => MonadFail m where
    fail :: String -> m a
```

Desugaring can now be changed to produce this constraint when necessary. For
this, we have to decide when a pattern match can not fail; if this is the case,
we can omit inserting the `fail` call.

The most trivial examples of unfailable patterns are of course those that match
anywhere unconditionally,

```haskell
do x <- action     >>>     let f x = more
   more            >>>     in  action >>= f
```

In particular, the programmer can assert any pattern be unfailable by making it
irrefutable using a prefix tilde:

```haskell
do ~pat <- action     >>>     let f ~pat = more
   more               >>>     in  action >>= f
```

A class of patterns that are conditionally failable are `newtype`s, and single
constructor `data` types, which are unfailable by themselves, but may fail
if matching on their fields is done with failable paterns.

```haskell
data Newtype a = Newtype a

- -- "x" cannot fail
do Newtype x <- action            >>>     let f (Newtype x) = more
   more                           >>>     in  action >>= f

- -- "Just x" can fail
do Newtype (Just x) <- action     >>>     let f (Newtype (Just x)) = more
   more                           >>>         f _ = fail "..."
                                  >>>     in  action >>= f
```

`ViewPatterns` are as failable as the pattern the view is matched against.
Patterns like `(Just -> Just x)` should generate a `MonadFail` constraint even
when it's "obvious" from the view's implementation that the pattern will always
match. From an implementor's perspective, this means that only types (and their
constructors) have to be looked at, not arbitrary values (like functions),
which is impossible to do statically in general.

```haskell
do (view ->  pat) <- action     >>>     let f (view ->  pat) = more
   more                         >>>         f _ = fail "..."
                                >>>     in  action >>= f

do (view -> ~pat) <- action     >>>     let f (view -> ~pat) = more
   more                         >>>     in  action >>= f
```

A similar issue arises for `PatternSynonyms`, which we cannot inspect during
compilation sufficiently. A pattern synonym will therefore always be considered
failable.

```haskell
do PatternSynonym x <- action     >>>     let f PatternSynonym x = more
   more                           >>>     in f _ = fail "..."
                                  >>>     in  action >>= f
```



Discussion
- ----------

- - Although for many `MonadPlus` `fail _ = mzero`, a separate `MonadFail` class
  should be created instead of just using that.

    - A parser might fail with an error message involving positional
      information. Some libraries, like `Binary`, provide `fail` as their
      only interface to fail a decoding step.

    - Although `STM` is `MonadPlus`, it uses the default `fail = error`. It
      will therefore not get a `MonadFail` instance.

- - What laws should `fail` follow? **Left zero**,

  ```haskell
  ∀ s f.  fail s >>= f  ≡  fail s
  ```

  A call to `fail` should abort the computation. In this sense, `fail` would
  become a close relative of `mzero`. It would work well with the common
  definition `fail _ = mzero`, and give a simple guideline to the intended
  usage and effect of the `MonadFail` class.

- - Rename `fail`? **No.** Old code might use `fail` explicitly and we might
  avoid breaking it, the Report talks about `fail`, and we have a solid
  migration strategy that does not require a renaming.

- - Remove the `String` argument? **No.** The `String` might help error reporting
  and debugging. `String` may be ugly, but it's the de facto standard for
  simple text in GHC. No high performance string operations are to be
  expected with `fail`, so this breaking change would in no way be justified.
  Also note that explicit `fail` calls would break if we removed the argument.

- - How sensitive would existing code be to subtle changes in the strictness
  behaviour of `do` notation pattern matching? **It doesn't.** The
  implementation does not affect strictness at all, only the desugaring step.
  Care must be taken when fixing warnings by making patterns irrefutable using
  `~`, as that *does* affect strictness. (Cf. difference between lazy/strict
  State)

- - The `Monad` constraint for `MonadFail` seems unnecessary. Should we drop or
  relax it? What other things should be considered?

  - Applicative `do` notation is coming sooner or later, `fail` might be useful
    in this more general scenario. Due to the AMP, it is trivial to change
    the `MonadFail` superclass to `Applicative` later. (The name will be a bit
    misleading, but it's a very small price to pay.)
  - The class might be misused for a strange pointed type if left without
    any constraint. This is not the intended use at all.

  I think we should keep the `Monad` superclass for three main reasons:

  - We don't want to see `(Monad m, MonadFail m) =>` all over the place.
  - The primary intended use of `fail` is for desugaring do-notation anyway.
  - Retroactively removing superclasses is easy, but adding them is hard
    (see AMP).




Adapting old code
- -----------------

- - Help! My code is broken because of a missing `MonadFail` instance!

  *Here are your options:*

    1. Write a `MonadFail` instance (and bring it into scope)

       ```haskell
       #if !MIN_VERSION_base(4,11,0)
       -- Control.Monad.Fail import will become redundant in GHC 7.16+
       import qualified Control.Monad.Fail as Fail
       #endif
       import Control.Monad

       instance Monad Foo where
         (>>=) = <...bind impl...>
         -- NB: `return` defaults to `pure`

       #if !MIN_VERSION_base(4,11,0)
         -- Monad(fail) will be removed in GHC 7.16+
         fail = Fail.fail
       #endif

       instance MonadFail Foo where
         fail = <...fail implementation...>
       ```

    2. Change your pattern to be irrefutable

    3. Emulate the old behaviour by desugaring the pattern match by hand:

       ```haskell
       do Left e <- foobar
          stuff
       ```

       becomes

       ```haskell
       do x <- foobar
          e <- case foobar of
              Left e' -> e'
              Right r -> error "Pattern match failed" -- Boooo
          stuff
       ```

       The point is you'll have to do your dirty laundry yourself now if you
       have a value that *you* know will always match, and if you don't handle
       the other patterns you'll get incompleteness warnings, and the compiler
       won't silently eat those for you.

- - Help! My code is broken because you removed `fail` from `Monad`, but my class
  defines it!

  *Delete that part of the instance definition.*



Esimating the breakage
- ----------------------

Using our initial implementation, I compiled stackage-nightly, and grepped the
logs for found "invalid use of fail desugaring". Assuming my implementation
is correct, the number of "missing `MonadFail`" warnings generated is 487.
Note that I filtered out `[]`, `Maybe` and `ReadPrec`, since those can be given
a `MonadFail` instance from within GHC, and no breakage is expected from them.

The build logs can be found [here][stackage-logs]. Search for "failable
pattern" to find your way to the still pretty raw warnings.




Transitional strategy
- ---------------------

The roadmap is similar to the [AMP][amp], the main difference being that since
`MonadFail` does not exist yet, we have to introduce new functionality and then
switch to it.

* **GHC 7.12 / base-4.9**

    - Add module `Control.Monad.Fail` with new class `MonadFail(fail)` so
      people can start writing instances for it.

      `Control.Monad` only re-exports the class `MonadFail`, but not its
      `fail` method.

      NB: At this point, `Control.Monad.Fail.fail` clashes with
      `Prelude.fail` and `Control.Monad.fail`.

    - *(non-essential)* Add a language extension `-XMonadFail` that
      changes desugaring to use `MonadFail(fail)` instead of `Monad(fail)`.

      This has the effect that typechecking will infer a `MonadFail` constraint
      for `do` blocks with failable patterns, just as it is planned to do when
      the entire thing is done.

    - Warn when a `do`-block that contains a failable pattern is
      desugared, but there is no `MonadFail`-instance in scope: "Please add the
      instance or change your pattern matching." Add a flag to control whether
      this warning appears.

    - Warn when an instance implements the `fail` function (or when `fail`
      is imported as a method of `Monad`), as it will be removed from the
      `Monad` class in the future. (See also [GHC #10071][trac-10071])

3. GHC 7.14

    - Switch `-XMonadFail` on by default.
    - Remove the desugaring warnings.

3. GHC 7.16

    - Remove `-XMonadFail`, leaving its effects on at all times.
    - Remove `fail` from `Monad`.
    - Instead, re-export `Control.Monad.Fail.fail` as `Prelude.fail` and
      `Control.Monad.fail`.
    - `Control.Monad.Fail` is now a redundant module that can be considered
      deprecated.



Current status
- --------------

- - [ZuriHac 2015 (29.5. - 31.5.)][zurihac]: Franz Thoma (@fmthoma) and me
  (David Luposchainsky aka @quchen) started implementing the MFP in GHC.

    - Desugaring to the new `fail` can be controlled via a new langauge
      extension, `MonadFailDesugaring`.
    - If the language extension is turned off, a warning will be emitted for
      code that would break if it was enabled.
    - Warnings are emitted for types that *have* a *MonadFail* instance. This
      still needs to be fixed.
    - The error message are readable, but should be more so. We're still
      on this.
- - 2015-06-09: Estimated breakage by compiling Stackage. Smaller than expected.



[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
[stackage-logs]: <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
[trac-10071]: https://ghc.haskell.org/trac/ghc/ticket/10071
[zurihac]: https://wiki.haskell.org/ZuriHac2015

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJVd0/yAAoJELrQsaT5WQUshbUH/A3W0itVAk7ao8rtxId5unCJ
7StriKVkTyLAkkrbRJngM4MHEKiCsoyIgr8kBIwSHgk194GxeP2NCF4ijuBZoDBt
+Uci+6BCBinV8+OzfrfTcJb4+8iw1j+eLWJ/Nz/JDMDNCiyzyC0SMsqGa+ssOz7H
/2mqPkQjQgpHuP5PTRLHKPPIsayCQvTbZR1f14KhuMN2SPDE+WY4rqugu//XuIkN
u1YssIf5l8mEez/1ljaqGL55cTI0UNg2z0iA0bFl/ajHaeQ6mc5BAevWfSohAMW7
7PIt13p9NIaMHnikmI+YJszm2IEaXuv47mGgbyDV//nHq3fwWN+naB+1mPX2eSU=
=vPAL
-----END PGP SIGNATURE-----
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

David Luposchainsky
In reply to this post by Henning Thielemann
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 09.06.2015 23:56, Henning Thielemann wrote:
> You think about making MonadFail a subclass of Applicative where the name
> MonadFail becomes misleading - how about naming it Fail instead?

I discussed this with Herbert a bit already. The problem with the name "Fail" is
that, if Monad being superclasses of it, hides the fact that there is such a
Monad constraint. Instead, the user has to know that Fail happens to be a
subclass of Monad, and the Monad part is probably the most important thing in the
type, not that there might be some failable pattern somewhere. The name MonadFail
solves this issue by containing "Monad" in its name, but the price we pay for
that is the awkwardness if we relax the superclass.

> We should clearly decide what "fail" is intended for - for programming errors
>  or for exceptions. I guess that people want to use it in the second sense,
> e.g. something like "guard" driven by a pattern match in the Maybe or list
> monad. But then "fail" must not have an argument because what user related
> information can the compiler add automatically? Instead of removing the
> String argument from "fail", the compiler could simply call "mzero" instead.

mzero is only available in MonadPlus, which we want to be independent of
MonadFail. I don't see a way to accomplish this goal without adding more
complicated desugaring rules.

> If we want to use "fail" for exceptions then it would be also ok for parser
> libraries to use it for parser failures. Nonetheless they might be better
> served with an Except-like monad where they can choose more specific types
> for the exception messages, e.g. parser errors with source location.  But
> using "mzero" instead of "fail" for monadic pattern matches would alter the
> proposal considerably. Using "mzero" requires a monad with "mplus" and for
> support of monads with "mzero" but no "mplus" we need to split MonadPlus,
> too.

> In summary I think that the cleanest solution would be to phase out "fail"
> completely in the long run.

If we desugar to anything but simply >>=, we're in a realm of mixups already.
Splitting MonadPlus would be quite an effort, and I don't think it would be worth it.

I agree that <- should desugar straight to >>=. I have not considered removing
the fail desugaring altogether, but I have no idea how much code that would
break. The build logs I posted show roughly 100 Maybe/List invocations with
failable patterns, but we should also not forget about all the explicit "fail"
calls that libraries might do.
All things considered, I don't think we're ready for (judging) this, but maybe we
can revisit the issue some day and simply deprecate MonadFail; in that sense,
we're not going in the wrong direction right now at least.

Greetings,
David/quchen
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJVd2bNAAoJELrQsaT5WQUsCOMH/0e73pZbXH8r98RpgB4sRU33
ZMZwmkA2wPA5nN996E+cqDmkbCm2qj+O0X3nVkdsM7qi+oRnubnlKWTe+YpPnBsP
in1GGwo9eSry7z4Qyw6ZVIeRXufrjQrze+zQH+dBG/O6Qrrze+t9w9YGAWJxDMVa
KcBdpd4gRXHZH6y0aXEK1eyroIF1k7XVrK4XVyI+WYu2JjpTHWLjaCBzDVS0+PXU
mnTbnA0CrLpGrjCdSiIBeU4ezc57Sn3aNy5gKlZNIchTV81Q/5y5wGE6t/V9wqz4
2fTohs4GWhyjCjLnr6NXWSQDtJN8CUQOadgz9qYnyGOawrKjWQND/ybi3zWE2ZY=
=1hmy
-----END PGP SIGNATURE-----
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Johan Tibell-2
In reply to this post by David Luposchainsky
Thanks for putting this together.

The proposal says:

"As a consequence, in current Haskell, you can not use Monad-polymorphic code safely, because although it claims to work for all Monads, it might just crash on you. This kind of implicit non-totality baked into the class is terrible."

Is this actually a problem in practice? Is there any code we can point to that suffers because of the current state of affairs? Could it be included in the proposal?


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

John Wiegley-2
In reply to this post by David Luposchainsky
>>>>> David Luposchainsky <[hidden email]> writes:

> the subject says it all. After we successfully put `=>`
> into Monad, it is time to remove something in return: `fail`.

+1

John
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

David Luposchainsky
In reply to this post by Johan Tibell-2
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 10.06.2015 00:26, Johan Tibell wrote:
> "As a consequence, in current Haskell, you can not use Monad-polymorphic code
> safely, because although it claims to work for all Monads, it might just crash
> on you. This kind of implicit non-totality baked into the class is terrible."
>
> Is this actually a problem in practice? Is there any code we can point to
> that suffers because of the current state of affairs? Could it be included in
> the proposal?

I don't have hard evidence, but the Monad class being partial strikes me as
pretty backwards in a language where totality and no implicit failures are
important to the programmers. We try our best to advocate not using certain
functions like "head" carelessly, but do-notation comes with similar partiality.

One concrete example that I know is bimap, but that's something I stumbled upon
months ago by accident. Another is that Binary does not have a monomorphic "fail"
function and it hurts me a bit to use the Monad-"fail" and write a comment on how
that is safe to do in the current context.

I think there are two important consequences of MonadFail. First of all, we can
all safely write failable patterns if we so desire. Second, the compiler can
ensure other people's codebases do not lie to us (knowingly or unknowingly).

David/quchen
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJVd2vEAAoJELrQsaT5WQUs+m8IAOWA9Hd52MG1wZ6g6FoOcXd6
x64dRDlilmkVu2IRxHADzip75Oji254yKQ5VY9yMGjYpFajtgf0Q8LrmA0ePTzhg
E/oxdm1vyRoJab1C5TfdrzPM/voP+wHi7y2ak1j0hTNky+wETj4MKtJ/Jef225nd
APUq05t6nPwzEDCz37RitfbA6/nwwYShaVjNe0tRluPrJuxdBu0+aobFc2lzVL+s
J7egnV1kqEOhc7INOhWYsvAJPAJSiY950y/Nmxb2/r5orTfN3tsr98d1zwRxhCmq
UNXhUaj5xD7BK2Rn1Zy7VwUv1T8IRLZuOQrlZh3HWz4t1SI0tTu3tdS468s/B1g=
=4mEU
-----END PGP SIGNATURE-----
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Tikhon Jelvis
+1. This sounds like a good change and, for the most part, doesn't seem all that painful.

As far as naming goes, perhaps we could make the name refer to do-notation? Something like DoFail. I mean, that doesn't sound great, but it makes it clear where fail comes in and how it might apply to non-monad type stuff. (ApplicativeDo, for example?)

It's also good because nothing about fail is integral to the abstract idea of a monad or even an applicative. It's a function of convenience to make pattern matches in do-notation play out better in cases where it's meaningful.

That's true in the other direction too: nothing about a `fail` function is particularly tied to monads or even applicatives. A Monad/Applicative superclass might make the desugaring easier and closer to current behavior, but the idea of a fail :: String -> f a is broadly applicable to fs that aren't necessarily either of those.

On Tue, Jun 9, 2015 at 3:42 PM, David Luposchainsky <[hidden email]> wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 10.06.2015 00:26, Johan Tibell wrote:
> "As a consequence, in current Haskell, you can not use Monad-polymorphic code
> safely, because although it claims to work for all Monads, it might just crash
> on you. This kind of implicit non-totality baked into the class is terrible."
>
> Is this actually a problem in practice? Is there any code we can point to
> that suffers because of the current state of affairs? Could it be included in
> the proposal?

I don't have hard evidence, but the Monad class being partial strikes me as
pretty backwards in a language where totality and no implicit failures are
important to the programmers. We try our best to advocate not using certain
functions like "head" carelessly, but do-notation comes with similar partiality.

One concrete example that I know is bimap, but that's something I stumbled upon
months ago by accident. Another is that Binary does not have a monomorphic "fail"
function and it hurts me a bit to use the Monad-"fail" and write a comment on how
that is safe to do in the current context.

I think there are two important consequences of MonadFail. First of all, we can
all safely write failable patterns if we so desire. Second, the compiler can
ensure other people's codebases do not lie to us (knowingly or unknowingly).

David/quchen
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJVd2vEAAoJELrQsaT5WQUs+m8IAOWA9Hd52MG1wZ6g6FoOcXd6
x64dRDlilmkVu2IRxHADzip75Oji254yKQ5VY9yMGjYpFajtgf0Q8LrmA0ePTzhg
E/oxdm1vyRoJab1C5TfdrzPM/voP+wHi7y2ak1j0hTNky+wETj4MKtJ/Jef225nd
APUq05t6nPwzEDCz37RitfbA6/nwwYShaVjNe0tRluPrJuxdBu0+aobFc2lzVL+s
J7egnV1kqEOhc7INOhWYsvAJPAJSiY950y/Nmxb2/r5orTfN3tsr98d1zwRxhCmq
UNXhUaj5xD7BK2Rn1Zy7VwUv1T8IRLZuOQrlZh3HWz4t1SI0tTu3tdS468s/B1g=
=4mEU
-----END PGP SIGNATURE-----
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Edward Kmett-2
In reply to this post by Johan Tibell-2
I can give a couple of "rather academic" issues that the status quo causes:

An example of where this has bit us in the hindquarters in the past is that the old Error class based instance for Monad (Either a) from the mtl incurred a constraint on the entire Monad instance in order to support 'fail'.

This ruled out many applications for the Either monad, e.g. apo/gapo are based on the real (Either e) monad, just as para/zygo are based on the "real" ((,) e) comonad. This rather complicated the use of recursion schemes and in fact was what drove me to write what turned into the "either" package in the first place.

Now we don't try to support 'fail' at all for that Monad. Under this proposal though, one _could_ add a MonadFail instance that incurred a rather ad hoc constraint on the left hand side of the sum without encumbering the Monad itself.

In general you have no way of knowing that you stick to the product-like structure of the Monad in the current ecosystem, because 'fail' is always there, you can get to values in the Monad you couldn't reach with just return and (>>=). 

Ideally you'd have (forall m. Monad m => m a) being isomorphic to a, this can be useful for ensuring we can plumb user-defined effects through code:


but in Haskell as it exists today you always have to worry about it invoking a call to fail, and having a special form of _distinguishable_ bottom available from that computation so it is really more like `Either String a`.

Can you just say "don't do that?" 

Sure, but it is the moral equivalent of programming with nulls all over your code.

-Edward

On Wed, Jun 10, 2015 at 12:26 AM, Johan Tibell <[hidden email]> wrote:
Thanks for putting this together.

The proposal says:

"As a consequence, in current Haskell, you can not use Monad-polymorphic code safely, because although it claims to work for all Monads, it might just crash on you. This kind of implicit non-totality baked into the class is terrible."

Is this actually a problem in practice? Is there any code we can point to that suffers because of the current state of affairs? Could it be included in the proposal?


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs



_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Felipe Lessa
+1, even more so after the not-so-academic issues pointed out by Edward.

Cheers,

--
Felipe.


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

signature.asc (836 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

David Feuer
+1 to taking fail out of Monad. +0 to any particular failure class;
the issues seem a bit complex, and I haven't studied them closely
enough to have an opinion.

On Tue, Jun 9, 2015 at 10:01 PM, Felipe Lessa <[hidden email]> wrote:

> +1, even more so after the not-so-academic issues pointed out by Edward.
>
> Cheers,
>
> --
> Felipe.
>
>
> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Bardur Arantsson-2
In reply to this post by David Luposchainsky
On 06/09/2015 10:43 PM, David Luposchainsky wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>

+1

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Carter Schonwald
i'll add my token +1 to the land slide

On Tue, Jun 9, 2015 at 11:19 PM, Bardur Arantsson <[hidden email]> wrote:
On 06/09/2015 10:43 PM, David Luposchainsky wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>

+1

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Nikita Volkov
+1 
Been dreaming about this for so long!

2015-06-10 7:27 GMT+03:00 Carter Schonwald <[hidden email]>:
i'll add my token +1 to the land slide

On Tue, Jun 9, 2015 at 11:19 PM, Bardur Arantsson <[hidden email]> wrote:
On 06/09/2015 10:43 PM, David Luposchainsky wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>

+1

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries



_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|

Re: MonadFail proposal (MFP): Moving fail out of Monad

Herbert Valerio Riedel
In reply to this post by Henning Thielemann
On 2015-06-09 at 23:56:59 +0200, Henning Thielemann wrote:

[...]

> You think about making MonadFail a subclass of Applicative where the
> name MonadFail becomes misleading - how about naming it Fail instead?

'Fail' would be a good name, if it became an independent class (w/o a
superclass to either 'Applicative' or 'Monad'). But the cost for that is
having to use `(Fail m, Monad m) =>`

Or rely on e.g. constraint synonyms, via e.g.

  {-# LANGUAGE ConstraintKinds #-}
  type MonadFail m       = (Fail m, Monad m)
  type ApplicativeFail m = (Fail m, Applicative m)

for convenience.

I've also heard the suggestion 'FailDo', which could make sense if it
became a superclass of Applicative (but then we'd still have to use
'(FailDo m, Monad m)' or a constraint-synonym for the majority of cases,
where 'fail' is used in a 'Monad' context)

Otoh, the reason I suggested to just go w/ 'MonadFail' to keep it simple
was that I doubted that that 'fail' in a '-XApplicativeDo' made much
sense. But maybe it does make sense?

I.e. while 'fail' has laws w/ 'Monad', what would be the respective laws
for 'fail' in an 'Applicative' context?

Cheers,
  hvr
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
123