MonadFail proposal (MFP): Moving fail out of Monad

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
26 messages Options
12
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-----
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

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

Herbert Valerio Riedel
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 :-)
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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?


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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-----
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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



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

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

Carter Schonwald
In reply to this post by David Luposchainsky
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


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

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

Malcolm Wallace-2
In reply to this post by Edward Kmett-2
I'm a +1 on this proposal as well. In our private Haskell compiler at work, we have had separate Monad and MonadFail classes since 2010, and it is clearly the more principled way to handle partiality: make it visible in the inferred types.  I found that there were very few instances when porting Hackage libraries to our compiler that we came across a need to change type signatures because of MonadFail, and making the change was in all cases easy anyway.

Regards,
    Malcolm

On 9 Jun 2015, at 23:19, Edward Kmett wrote:

> +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]: 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

_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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-10 at 00:42:12 +0200, David Luposchainsky wrote:

[...]

> 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).

...as a data-point, when turning on MonadFail during testing, I've seen
at least one place in GHC's own code-base that directly calls 'fail' for
a Monad instance which did *not* have its 'fail' method
overridden. Moreover, I've seen at least one (other) case, where
failable pattern matches occurred (intentionally or not[1]), but the
respective Monad instance didn't have the 'fail' method overridden
either.

 [1]: Failable patterns can in theory snuck in non-intentionally,
      e.g. they can be introduced during refactoring if e.g. a
      single-constructor type gets added new constructors. If the
      context was previously constraint to a 'Monad', after the
      refactoring the typechecker would point out that now there's a
      failable pattern not accounted for.
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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
On Wed, Jun 10, 2015 at 12:42 AM, David Luposchainsky <[hidden email]> wrote:
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).

The second is a bit overstated I think. Any function you call can still have partial pattern matches in all the other places Haskell allows them and you wouldn't know from the type.


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

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

Roman Cheplyaka-2
On 10/06/15 14:22, Johan Tibell wrote:

> On Wed, Jun 10, 2015 at 12:42 AM, David Luposchainsky
> <[hidden email] <mailto:[hidden email]>>
> wrote:
>
>     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).
>
>
> The second is a bit overstated I think. Any function you call can still
> have partial pattern matches in all the other places Haskell allows them
> and you wouldn't know from the type.
For most of them, at least you get a warning from GHC (not for patterns
inside lambda, sadly, although that should be fixable). But for

do
  Just x <- a
  ...

it's not possible in principle to give a warning, because it's not clear
whether the implicit call to fail is intended.

Roman


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

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

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

Ganesh Sittampalam
In reply to this post by Johan Tibell-2
On 09/06/2015 23:26, Johan Tibell 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?

Here's a concrete example:

https://mail.haskell.org/pipermail/libraries/2015-March/025166.html

I needed to change some code that used a monad with an explicit fail to
use one without, and I couldn't get the compiler to tell me if it was
using partial pattern matches or not. If it had been then the
refactoring would have caused a nasty behaviour change.

Ganesh

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

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

Thomas Bereknyei
In reply to this post by Roman Cheplyaka-2

+1 also improves the correctness of the monad laws

On Jun 10, 2015 7:46 AM, "Roman Cheplyaka" <[hidden email]> wrote:
On 10/06/15 14:22, Johan Tibell wrote:
> On Wed, Jun 10, 2015 at 12:42 AM, David Luposchainsky
> <[hidden email] <mailto:[hidden email]>>
> wrote:
>
>     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).
>
>
> The second is a bit overstated I think. Any function you call can still
> have partial pattern matches in all the other places Haskell allows them
> and you wouldn't know from the type.

For most of them, at least you get a warning from GHC (not for patterns
inside lambda, sadly, although that should be fixable). But for

do
  Just x <- a
  ...

it's not possible in principle to give a warning, because it's not clear
whether the implicit call to fail is intended.

Roman


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


_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
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 Roman Cheplyaka-2
On Wed, Jun 10, 2015 at 1:46 PM, Roman Cheplyaka <[hidden email]> wrote:
On 10/06/15 14:22, Johan Tibell wrote:
> On Wed, Jun 10, 2015 at 12:42 AM, David Luposchainsky
> <[hidden email] <mailto:[hidden email]>>
> wrote:
>
>     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).
>
>
> The second is a bit overstated I think. Any function you call can still
> have partial pattern matches in all the other places Haskell allows them
> and you wouldn't know from the type.

For most of them, at least you get a warning from GHC (not for patterns
inside lambda, sadly, although that should be fixable). But for

do
  Just x <- a
  ...

it's not possible in principle to give a warning, because it's not clear
whether the implicit call to fail is intended.

That's a good point. An alternative to changing fail would to add a warning for partial matches even in do-notation.
 

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

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

Erik Hesselink
On Wed, Jun 10, 2015 at 2:14 PM, Johan Tibell <[hidden email]> wrote:

> On Wed, Jun 10, 2015 at 1:46 PM, Roman Cheplyaka <[hidden email]> wrote:
>>
>> On 10/06/15 14:22, Johan Tibell wrote:
>> > On Wed, Jun 10, 2015 at 12:42 AM, David Luposchainsky
>> > <[hidden email] <mailto:[hidden email]>>
>> > wrote:
>> >
>> >     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).
>> >
>> >
>> > The second is a bit overstated I think. Any function you call can still
>> > have partial pattern matches in all the other places Haskell allows them
>> > and you wouldn't know from the type.
>>
>> For most of them, at least you get a warning from GHC (not for patterns
>> inside lambda, sadly, although that should be fixable). But for
>>
>> do
>>   Just x <- a
>>   ...
>>
>> it's not possible in principle to give a warning, because it's not clear
>> whether the implicit call to fail is intended.
>
>
> That's a good point. An alternative to changing fail would to add a warning
> for partial matches even in do-notation.

That would be an improvement, but only a small one. You'd have to turn
off the warning if you really meant to use fail, and then if you
changed the monad you're using, or you're writing a polymorphic
function, nothing will warn you.

I'm +1 on this change. The plan looks thorough, minimizing breakage.
It'll bring many advantages. For example, I'll feel much safer when I
use functions like time's parseTimeM, to give a concrete example.

Regards,

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

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

Wolfgang Jeltsch-2
In reply to this post by David Luposchainsky
Hi David,

thank you very much for this proposal. I think having fail in Monad is
just plain wrong, and I am therefore very happy to see it being moved
out.

I have some remarks, though:

> 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.

The part about single-constructor data types is not true. A
single-constructor data type has a value ⊥ that is different from
applying the data constructor to ⊥’s. For example, ⊥ and (⊥, ⊥) are two
different values. Matching ⊥ against the pattern (_, _) fails, matching
(⊥, ⊥) against (_, _) succeeds. So single-constructor data types are not
different from all other data types in this respect. The dividing line
really runs between data types and newtypes. So only matches against
patterns C p where C is a newtype constructor and p is unfailable should
be considered unfailable.

>   - 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.)

I think it would be very misleading having a MonadFail class that might
have instances that are not monads, and that this is a price we should
not pay. So we should not name the class MonadFail. Maybe, Fail would be
a good name.

> 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.

But exactly this will happen if we change the superclass of (Monad)Fail
from Monad to Applicative. So it might be better to impose a more
light-weight constraint in the first place. Functor m might be a good
choice.

All the best,
Wolfgang

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

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

David Turner-2
AIUI the point about ⊥ and (⊥, ⊥) being different doesn't matter here:
a bind for a single-constructor datatype never desugars in a way that
uses fail (which isn't to say that it can't be undefined)

For instance:

runErrorT (do { (_,_) <- return undefined; return () } :: ErrorT String IO ())

throws an exception, even though the bind is in ErrorT where fail just
returns left:

runErrorT (do { fail "oops"; return () } :: ErrorT String IO ())

=> Left "oops"

Hope that helps, and hope I understand correctly!

David


On 11 June 2015 at 16:08, Wolfgang Jeltsch <[hidden email]> wrote:

> Hi David,
>
> thank you very much for this proposal. I think having fail in Monad is
> just plain wrong, and I am therefore very happy to see it being moved
> out.
>
> I have some remarks, though:
>
>> 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.
>
> The part about single-constructor data types is not true. A
> single-constructor data type has a value ⊥ that is different from
> applying the data constructor to ⊥’s. For example, ⊥ and (⊥, ⊥) are two
> different values. Matching ⊥ against the pattern (_, _) fails, matching
> (⊥, ⊥) against (_, _) succeeds. So single-constructor data types are not
> different from all other data types in this respect. The dividing line
> really runs between data types and newtypes. So only matches against
> patterns C p where C is a newtype constructor and p is unfailable should
> be considered unfailable.
>
>>   - 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.)
>
> I think it would be very misleading having a MonadFail class that might
> have instances that are not monads, and that this is a price we should
> not pay. So we should not name the class MonadFail. Maybe, Fail would be
> a good name.
>
>> 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.
>
> But exactly this will happen if we change the superclass of (Monad)Fail
> from Monad to Applicative. So it might be better to impose a more
> light-weight constraint in the first place. Functor m might be a good
> choice.
>
> All the best,
> Wolfgang
>
> _______________________________________________
> ghc-devs mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
Reply | Threaded
Open this post in threaded view
|

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

Wolfgang Jeltsch-2
Are you sure that desugaring works this way? If yes, this should be
considered a bug and be fixed, I would say. It is very illogical.

All the best,
Wolfgang

Am Donnerstag, den 11.06.2015, 16:23 +0100 schrieb David Turner:

> AIUI the point about ⊥ and (⊥, ⊥) being different doesn't matter here:
> a bind for a single-constructor datatype never desugars in a way that
> uses fail (which isn't to say that it can't be undefined)
>
> For instance:
>
> runErrorT (do { (_,_) <- return undefined; return () } :: ErrorT String IO ())
>
> throws an exception, even though the bind is in ErrorT where fail just
> returns left:
>
> runErrorT (do { fail "oops"; return () } :: ErrorT String IO ())
>
> => Left "oops"
>
> Hope that helps, and hope I understand correctly!
>
> David
>
>
> On 11 June 2015 at 16:08, Wolfgang Jeltsch <[hidden email]> wrote:
> > Hi David,
> >
> > thank you very much for this proposal. I think having fail in Monad is
> > just plain wrong, and I am therefore very happy to see it being moved
> > out.
> >
> > I have some remarks, though:
> >
> >> 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.
> >
> > The part about single-constructor data types is not true. A
> > single-constructor data type has a value ⊥ that is different from
> > applying the data constructor to ⊥’s. For example, ⊥ and (⊥, ⊥) are two
> > different values. Matching ⊥ against the pattern (_, _) fails, matching
> > (⊥, ⊥) against (_, _) succeeds. So single-constructor data types are not
> > different from all other data types in this respect. The dividing line
> > really runs between data types and newtypes. So only matches against
> > patterns C p where C is a newtype constructor and p is unfailable should
> > be considered unfailable.
> >
> >>   - 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.)
> >
> > I think it would be very misleading having a MonadFail class that might
> > have instances that are not monads, and that this is a price we should
> > not pay. So we should not name the class MonadFail. Maybe, Fail would be
> > a good name.
> >
> >> 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.
> >
> > But exactly this will happen if we change the superclass of (Monad)Fail
> > from Monad to Applicative. So it might be better to impose a more
> > light-weight constraint in the first place. Functor m might be a good
> > choice.
> >
> > All the best,
> > Wolfgang
> >
> > _______________________________________________
> > ghc-devs mailing list
> > [hidden email]
> > http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs


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

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

David Feuer
Pattern matching on `undefined` is not like pattern match failure.
Single-constructor types are only special if they're unlifted:
`newtype` and GHC's unboxed tuples are the only examples I know of,
and you can't use unboxed tuples in this context.

On Thu, Jun 11, 2015 at 11:28 AM, Wolfgang Jeltsch
<[hidden email]> wrote:

> Are you sure that desugaring works this way? If yes, this should be
> considered a bug and be fixed, I would say. It is very illogical.
>
> All the best,
> Wolfgang
>
> Am Donnerstag, den 11.06.2015, 16:23 +0100 schrieb David Turner:
>> AIUI the point about ⊥ and (⊥, ⊥) being different doesn't matter here:
>> a bind for a single-constructor datatype never desugars in a way that
>> uses fail (which isn't to say that it can't be undefined)
>>
>> For instance:
>>
>> runErrorT (do { (_,_) <- return undefined; return () } :: ErrorT String IO ())
>>
>> throws an exception, even though the bind is in ErrorT where fail just
>> returns left:
>>
>> runErrorT (do { fail "oops"; return () } :: ErrorT String IO ())
>>
>> => Left "oops"
>>
>> Hope that helps, and hope I understand correctly!
>>
>> David
>>
>>
>> On 11 June 2015 at 16:08, Wolfgang Jeltsch <[hidden email]> wrote:
>> > Hi David,
>> >
>> > thank you very much for this proposal. I think having fail in Monad is
>> > just plain wrong, and I am therefore very happy to see it being moved
>> > out.
>> >
>> > I have some remarks, though:
>> >
>> >> 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.
>> >
>> > The part about single-constructor data types is not true. A
>> > single-constructor data type has a value ⊥ that is different from
>> > applying the data constructor to ⊥’s. For example, ⊥ and (⊥, ⊥) are two
>> > different values. Matching ⊥ against the pattern (_, _) fails, matching
>> > (⊥, ⊥) against (_, _) succeeds. So single-constructor data types are not
>> > different from all other data types in this respect. The dividing line
>> > really runs between data types and newtypes. So only matches against
>> > patterns C p where C is a newtype constructor and p is unfailable should
>> > be considered unfailable.
>> >
>> >>   - 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.)
>> >
>> > I think it would be very misleading having a MonadFail class that might
>> > have instances that are not monads, and that this is a price we should
>> > not pay. So we should not name the class MonadFail. Maybe, Fail would be
>> > a good name.
>> >
>> >> 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.
>> >
>> > But exactly this will happen if we change the superclass of (Monad)Fail
>> > from Monad to Applicative. So it might be better to impose a more
>> > light-weight constraint in the first place. Functor m might be a good
>> > choice.
>> >
>> > All the best,
>> > Wolfgang
>> >
>> > _______________________________________________
>> > 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
_______________________________________________
ghc-devs mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
12