Re: [Haskell-cafe] [Haskell] Functor => Applicative => Monad

Hi, John Smith wrote:
I would like to formally propose that Monad become a subclass of Applicative
A lot of code would break because of this change, but all problems should be reported at compile time, and are easy to fix. In most of the cases, either adding obvious Functor and Applicative instances to a library; or deleting such instances from a client, I would expect. This kind of clean-up would actually increase the quality of the library, however, and might therefore be acceptable.
The change is described on the wiki at http://haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal
There you write, among other things:
fail should be removed from Monad; a failed pattern match could error in the same way as is does for pure code.
How is this part of the proposal related to Functor and Applicative? Since code depending on the current behavior can not be detected at compile time, this is a more serious change in a way: code keeps compiling but changes its meaning. Can you estimate how much code would break because of this change? Would it be possible to keep user-defined failure handling in do notation without keeping fail in Monad? Tillmann

2010/12/14 Tillmann Rendel
Hi,
John Smith wrote:
I would like to formally propose that Monad become a subclass of Applicative
A lot of code would break because of this change, but all problems should be reported at compile time, and are easy to fix. In most of the cases, either adding obvious Functor and Applicative instances to a library; or deleting such instances from a client, I would expect. This kind of clean-up would actually increase the quality of the library, however, and might therefore be acceptable.
The change is described on the wiki at http://haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal
There you write, among other things:
fail should be removed from Monad; a failed pattern match could error in the same way as is does for pure code.
How is this part of the proposal related to Functor and Applicative?
Since code depending on the current behavior can not be detected at compile time, this is a more serious change in a way: code keeps compiling but changes its meaning. Can you estimate how much code would break because of this change?
Would it be possible to keep user-defined failure handling in do notation without keeping fail in Monad?
When this change was made for the 'Either' monad, I remember that it introduced sneaky runtime bugs into the, I think, HTTP library. So your concerns are founded. I really think that these proposals should be considered separately, even if they would be enacted at the same time if they both passed. Antoine

Fail can't just be removed. That would just break too much code. For example, I find myself writing code like the following:
[a,b,c] <- Just someList
in place of
let [a,b,c] = someList
so that pattern match failure is "lifted" into the maybe monad (as long as I'm already in the maybe monad). I would like to see a MonadFail class, with one method 'fail', such that it is a compile-time error to try 'failable' pattern matches in a monad that is not an instance of MonadFail. --Jonathan

On 15/12/2010 04:01, Jonathan Geddes wrote:
Fail can't just be removed. That would just break too much code. For example, I find myself writing code like the following:
[a,b,c]<- Just someList
in place of
let [a,b,c] = someList
so that pattern match failure is "lifted" into the maybe monad (as long as I'm already in the maybe monad).
I would like to see a MonadFail class, with one method 'fail', such that it is a compile-time error to try 'failable' pattern matches in a monad that is not an instance of MonadFail.
--Jonathan
Perhaps pattern match failures in a MonadPlus should bind to mzero - I believe that this is what your example and similar wish to achieve. I've updated http://haskell.org/haskellwiki/Functor-Applicative-Monad_Proposal to mention this. Does anyone know why fail and Pointed (in the paragraph "This would eliminate...") aren't beginning a new paragraph? They are both preceded by a blank line in the wiki source.

Hi John, John Smith wrote:
Perhaps pattern match failures in a MonadPlus should bind to mzero - I believe that this is what your example and similar wish to achieve.
You updated the proposal to say:
a failed pattern match should error in the same way as is does for pure code, while in MonadPlus, the current behaviour could be maintained with mzero
Can you be more specific as to how that would interact with polymorphism and type inference? What does it mean to be "in MonadPlus"? How does the compiler know? For example, what would be the static types and dynamic semantics of the following expressions: 1. \a -> do {Just x <- return (Just a); return x} 2. do {Just x <- return Nothing; return x} 3. \a -> do {Just x <- a; return x} 4. \a b -> do {(x, _) <- return (a, b); return x} 5. \a -> do {(x, _) <- return a; return x} Tillmann

Any refutable pattern match in do would force MonadFail (or MonadPlus if you
prefer). So
1. (MonadFail m) => a -> m a, \ a -> return a
2. (MonadFail m) => m a, mfail "..."
3. (MonadFail m) => Maybe a -> m a, \ a -> case a of Nothing -> mfail
"..."; Just x -> return x
4. (Monad m) => a -> b -> m a, \ a b -> return a
5. (Monad m) => (a, b) -> m a, \ (a, b) -> return a
As far as type inference and desugaring goes, it seems very little would
have to be changed in an implementation.
-- Lennart
2010/12/15 Tillmann Rendel
Hi John,
John Smith wrote:
Perhaps pattern match failures in a MonadPlus should bind to mzero - I believe that this is what your example and similar wish to achieve.
You updated the proposal to say:
a failed pattern match should error in the same way as is does for pure code, while in MonadPlus, the current behaviour could be maintained with mzero
Can you be more specific as to how that would interact with polymorphism and type inference? What does it mean to be "in MonadPlus"? How does the compiler know?
For example, what would be the static types and dynamic semantics of the following expressions:
1. \a -> do {Just x <- return (Just a); return x}
2. do {Just x <- return Nothing; return x}
3. \a -> do {Just x <- a; return x}
4. \a b -> do {(x, _) <- return (a, b); return x}
5. \a -> do {(x, _) <- return a; return x}
Tillmann
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

John Smith proposed:
a failed pattern match should error in the same way as is does for pure code, while in MonadPlus, the current behaviour could be maintained with mzero
Lennart Augustsson wrote:
Any refutable pattern match in do would force MonadFail (or MonadPlus if you prefer).
I guess that would work out, but I think John proposed something else. Tillmann

On 15/12/2010 11:39, Lennart Augustsson wrote:
Any refutable pattern match in do would force MonadFail (or MonadPlus if you prefer). So 1. (MonadFail m) => a -> m a, \ a -> return a 2. (MonadFail m) => m a, mfail "..." 3. (MonadFail m) => Maybe a -> m a, \ a -> case a of Nothing -> mfail "..."; Just x -> return x 4. (Monad m) => a -> b -> m a, \ a b -> return a 5. (Monad m) => (a, b) -> m a, \ (a, b) -> return a
As far as type inference and desugaring goes, it seems very little would have to be changed in an implementation.
Is there a need for a MonadFail, as distinct from mzero? fail always seems to be defined as error in ordinary monads, and as mzero in MonadPlus (or left at the default error).

Yes, I think there should be a MonadFail distinct from MonadPlus.
Some types, like IO, are not in MonadPlus, but have a special implementation
of the fail method.
Personally, I think fail should just be removed, but that would break
existing code.
The fail method was introduced for the wrong reasons (better error messages
was the excuse).
-- Lennart
On Wed, Dec 15, 2010 at 11:51 AM, John Smith
On 15/12/2010 11:39, Lennart Augustsson wrote:
Any refutable pattern match in do would force MonadFail (or MonadPlus if you prefer). So 1. (MonadFail m) => a -> m a, \ a -> return a 2. (MonadFail m) => m a, mfail "..." 3. (MonadFail m) => Maybe a -> m a, \ a -> case a of Nothing -> mfail "..."; Just x -> return x 4. (Monad m) => a -> b -> m a, \ a b -> return a 5. (Monad m) => (a, b) -> m a, \ (a, b) -> return a
As far as type inference and desugaring goes, it seems very little would have to be changed in an implementation.
Is there a need for a MonadFail, as distinct from mzero? fail always seems to be defined as error in ordinary monads, and as mzero in MonadPlus (or left at the default error).
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On 15/12/2010 14:31, Lennart Augustsson wrote:
Yes, I think there should be a MonadFail distinct from MonadPlus. Some types, like IO, are not in MonadPlus, but have a special implementation of the fail method.
Personally, I think fail should just be removed, but that would break existing code. The fail method was introduced for the wrong reasons (better error messages was the excuse).
Which other monads (other than MonadPlus subclasses) define fail?

On Thu, Dec 16, 2010 at 12:03 PM, John Smith
On 15/12/2010 14:31, Lennart Augustsson wrote:
Yes, I think there should be a MonadFail distinct from MonadPlus. Some types, like IO, are not in MonadPlus, but have a special implementation of the fail method.
Personally, I think fail should just be removed, but that would break existing code. The fail method was introduced for the wrong reasons (better error messages was the excuse).
Which other monads (other than MonadPlus subclasses) define fail?
STM is in MonadPlus, but I don't think pattern-match failure should be the same as STM.retry. Antoine

IO
On Thu, Dec 16, 2010 at 6:03 PM, John Smith
On 15/12/2010 14:31, Lennart Augustsson wrote:
Yes, I think there should be a MonadFail distinct from MonadPlus. Some types, like IO, are not in MonadPlus, but have a special implementation of the fail method.
Personally, I think fail should just be removed, but that would break existing code. The fail method was introduced for the wrong reasons (better error messages was the excuse).
Which other monads (other than MonadPlus subclasses) define fail?
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Hello,
I think that we should make both changes (make Applicative a
super-class of Monad, and remove the "fail" method from Monad). Code
will break but we can fix it.
By the way, just for reference, the proposal to have a separate
failure class and using it in the "do" notation, is how things used to
be back in Haskell 1.4 (one version before Haskell 98). For the
curious, take a look at page 21 of
http://haskell.org/definition/haskell-report-1.4.ps.gz
-Iavor
On Thu, Dec 16, 2010 at 3:57 PM, Lennart Augustsson
IO
On Thu, Dec 16, 2010 at 6:03 PM, John Smith
wrote: On 15/12/2010 14:31, Lennart Augustsson wrote:
Yes, I think there should be a MonadFail distinct from MonadPlus. Some types, like IO, are not in MonadPlus, but have a special implementation of the fail method.
Personally, I think fail should just be removed, but that would break existing code. The fail method was introduced for the wrong reasons (better error messages was the excuse).
Which other monads (other than MonadPlus subclasses) define fail?
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Wed, 2010-12-15 at 13:51 +0200, John Smith wrote:
On 15/12/2010 11:39, Lennart Augustsson wrote:
Any refutable pattern match in do would force MonadFail (or MonadPlus if you prefer). So 1. (MonadFail m) => a -> m a, \ a -> return a 2. (MonadFail m) => m a, mfail "..." 3. (MonadFail m) => Maybe a -> m a, \ a -> case a of Nothing -> mfail "..."; Just x -> return x 4. (Monad m) => a -> b -> m a, \ a b -> return a 5. (Monad m) => (a, b) -> m a, \ (a, b) -> return a
As far as type inference and desugaring goes, it seems very little would have to be changed in an implementation.
Is there a need for a MonadFail, as distinct from mzero? fail always seems to be defined as error in ordinary monads, and as mzero in MonadPlus (or left at the default error).
Not all types can implement mplus to begin with even if they can have 'zero' type. For example technically Maybe breaks the laws while still having useful fail: (guard . even) =<< (Just 1 <|> Just 2) (guard . even) =<< Just 1 guard (even 1) guard False Nothing /= Just () Nothing <|> Just () guard False <|> guard True (guard (even 1)) <|> (guard (even 2)) ((guard . even) =<< Just 1) <|> ((guard . even) =<< Just 2) Regards

On Wed, Dec 15, 2010 at 06:25:30PM +0100, Maciej Piechotka wrote:
On Wed, 2010-12-15 at 13:51 +0200, John Smith wrote:
On 15/12/2010 11:39, Lennart Augustsson wrote:
Any refutable pattern match in do would force MonadFail (or MonadPlus if you prefer). So 1. (MonadFail m) => a -> m a, \ a -> return a 2. (MonadFail m) => m a, mfail "..." 3. (MonadFail m) => Maybe a -> m a, \ a -> case a of Nothing -> mfail "..."; Just x -> return x 4. (Monad m) => a -> b -> m a, \ a b -> return a 5. (Monad m) => (a, b) -> m a, \ (a, b) -> return a
As far as type inference and desugaring goes, it seems very little would have to be changed in an implementation.
Is there a need for a MonadFail, as distinct from mzero? fail always seems to be defined as error in ordinary monads, and as mzero in MonadPlus (or left at the default error).
Not all types can implement mplus to begin with even if they can have 'zero' type. For example technically Maybe breaks the laws while still having useful fail:
(guard . even) =<< (Just 1 <|> Just 2) (guard . even) =<< Just 1 guard (even 1) guard False Nothing /= Just () Nothing <|> Just () guard False <|> guard True (guard (even 1)) <|> (guard (even 2)) ((guard . even) =<< Just 1) <|> ((guard . even) =<< Just 2)
But that depends on what laws you choose for MonadPlus. See http://www.haskell.org/haskellwiki/MonadPlus . -Brent

On 15 Dec 2010, at 17:48, Brent Yorgey wrote:
On Wed, Dec 15, 2010 at 06:25:30PM +0100, Maciej Piechotka wrote:
On Wed, 2010-12-15 at 13:51 +0200, John Smith wrote:
On 15/12/2010 11:39, Lennart Augustsson wrote:
Any refutable pattern match in do would force MonadFail (or MonadPlus if you prefer). So
[..]
As far as type inference and desugaring goes, it seems very little would have to be changed in an implementation.
Is there a need for a MonadFail, as distinct from mzero? fail always seems to be defined as error in ordinary monads, and as mzero in MonadPlus (or left at the default error).
Not all types can implement mplus to begin with even if they can have 'zero' type. For example technically Maybe breaks the laws while still having useful fail:
[..]
But that depends on what laws you choose for MonadPlus. See http://www.haskell.org/haskellwiki/MonadPlus .
What has any of this to do with monads? Cheers Conor
participants (9)
-
Antoine Latter
-
Brent Yorgey
-
Conor McBride
-
Iavor Diatchki
-
John Smith
-
Jonathan Geddes
-
Lennart Augustsson
-
Maciej Piechotka
-
Tillmann Rendel