# MonadPlus reform proposal

### From HaskellWiki

(→Instances of both) |
|||

(8 intermediate revisions by 6 users not shown) | |||

Line 1: | Line 1: | ||

The [[MonadPlus]] class is ambiguous: while all instances satisfy '''Monoid''' and '''Left Zero''', some such as <tt>[]</tt> satisfy '''Left Distribution''', while others such as <tt>Maybe</tt> and <tt>IO</tt> satisfy '''Left Catch'''. |
The [[MonadPlus]] class is ambiguous: while all instances satisfy '''Monoid''' and '''Left Zero''', some such as <tt>[]</tt> satisfy '''Left Distribution''', while others such as <tt>Maybe</tt> and <tt>IO</tt> satisfy '''Left Catch'''. |
||

+ | |||

+ | == Proposal == |
||

It is proposed that MonadPlus be split like this: |
It is proposed that MonadPlus be split like this: |
||

Line 5: | Line 7: | ||

=== MonadZero === |
=== MonadZero === |
||

− | class Monad m => MonadZero m where |
+ | <haskell> |

− | mzero :: m a |
+ | class Monad m => MonadZero m where |

+ | mzero :: m a |
||

+ | </haskell> |
||

satisfying '''Left Zero''': |
satisfying '''Left Zero''': |
||

− | mzero >>= k = mzero |
+ | |

+ | <haskell> |
||

+ | mzero >>= k = mzero |
||

+ | </haskell> |
||

=== MonadPlus === |
=== MonadPlus === |
||

− | class MonadZero m => MonadPlus m where |
+ | <haskell> |

− | mplus :: m a -> m a -> m a |
+ | class MonadZero m => MonadPlus m where |

+ | mplus :: m a -> m a -> m a |
||

+ | </haskell> |
||

satisfying '''Monoid''' and '''Left Distribution''': |
satisfying '''Monoid''' and '''Left Distribution''': |
||

− | mplus mzero a = a |
+ | |

− | mplus a mzero = a |
+ | <haskell> |

− | mplus (mplus a b) c = mplus a (mplus b c) |
+ | mplus mzero b = b |

− | mplus a b >>= k = mplus (a >>= k) (b >>= k) |
+ | mplus a mzero = a |

+ | mplus (mplus a b) c = mplus a (mplus b c) |
||

+ | mplus a b >>= k = mplus (a >>= k) (b >>= k) |
||

+ | </haskell> |
||

=== MonadOr === |
=== MonadOr === |
||

− | class MonadZero m => MonadOr m where |
+ | <haskell> |

− | morelse :: m a -> m a -> m a |
+ | class MonadZero m => MonadOr m where |

+ | morelse :: m a -> m a -> m a |
||

+ | </haskell> |
||

satisfying '''Monoid''' and '''Left Catch''': |
satisfying '''Monoid''' and '''Left Catch''': |
||

− | morelse mzero a = a |
+ | |

− | morelse a mzero = a |
+ | <haskell> |

− | morelse (morelse a b) c = morelse a (morelse b c) |
+ | morelse mzero b = b |

− | morelse (return a) b = return a |
+ | morelse a mzero = a |

+ | morelse (morelse a b) c = morelse a (morelse b c) |
||

+ | morelse (return a) b = return a |
||

+ | </haskell> |
||

+ | |||

+ | == Instances of both == |
||

+ | |||

+ | Some types could be made instances of both. For instance: |
||

+ | |||

+ | <haskell> |
||

+ | instance MonadOr [] where |
||

+ | morelse [] b = b |
||

+ | morelse a b = a |
||

+ | </haskell> |
||

+ | |||

+ | The left-biased implementation of mplus for the Maybe monad should be used as an implementation of morelse, but it is also possible to give an unbiased mplus for Maybe: |
||

+ | |||

+ | <haskell> |
||

+ | instance MonadPlus Maybe where |
||

+ | mplus (Just a) Nothing = a |
||

+ | mplus Nothing (Just a) = a |
||

+ | mplus _ _ = Nothing |
||

+ | |||

+ | instance MonadOr Maybe where |
||

+ | morelse (Just a) _ = Just a |
||

+ | morelse _ b = b |
||

+ | </haskell> |
||

+ | |||

+ | == Discussion == |
||

+ | Given that Control.Applicative(Alternative) now defines a class which seems innately bound to '''Left Catch''', at least in spirit, it seems to make sense to clean up MonadPlus such that all instances obey '''Left Distribution'''? --sclv |
||

+ | |||

+ | I'd actually suggest almost the opposite, that MonadPlus be dispensed with and merged into Monad. The (controversial) fail method looks no different than an mzero, except the string argument; indeed, so far as I know <tt>fail s</tt> is just mzero for any MonadPlus. MonadPlus is also barely made use of; just guard and msum in the standard? To be concrete, I would make the following the default definitions (in Monad): |
||

+ | |||

+ | <haskell> |
||

+ | mzero = fail "something" |
||

+ | mplus a b = a |
||

+ | </haskell> |
||

+ | |||

+ | These are thus somewhat trivial by default, but having msum=head and guard=assert (roughly; more like <tt>(`assert` return ())</tt>) for less-flexible monads doesn't seem actually wrong and could be useful fallbacks. |
||

+ | |||

+ | I also question the claim that Maybe and IO should be thought of as "left catch". IO is not even in MonadPlus, and I don't see how it can be meaningfully in any way other than the above. Maybe does satisfy Left Catch, but it seems almost like that's only because it's such a simple monad (holding only one value). It is a useful observation that it fails Left Distribution, but that may only call for weaker Monad/Plus conditions. |
||

+ | |||

+ | The MonadOr idea is a solid one, but it seems to be taking the monad in a different direction. So if there's a good match in Control.Applicative or Parsec, that might be the best place to develop that idea. -- Galen |
||

+ | |||

+ | The default <hask>mplus</hask> doesn't satisfy <hask>mplus mzero b = b</hask>, so you lose Monoid which seems to be the only thing people actually agree on :) -- [[User:Benmachine|Benmachine]] |
||

+ | |||

+ | [[Category:Proposals]] [[Category:Monad]] |

## Revision as of 01:01, 18 May 2011

The MonadPlus class is ambiguous: while all instances satisfy **Monoid** and **Left Zero**, some such as `[]` satisfy **Left Distribution**, while others such as `Maybe` and `IO` satisfy **Left Catch**.

## Contents |

## 1 Proposal

It is proposed that MonadPlus be split like this:

### 1.1 MonadZero

class Monad m => MonadZero m where mzero :: m a

satisfying **Left Zero**:

mzero >>= k = mzero

### 1.2 MonadPlus

class MonadZero m => MonadPlus m where mplus :: m a -> m a -> m a

satisfying **Monoid** and **Left Distribution**:

mplus mzero b = b mplus a mzero = a mplus (mplus a b) c = mplus a (mplus b c) mplus a b >>= k = mplus (a >>= k) (b >>= k)

### 1.3 MonadOr

class MonadZero m => MonadOr m where morelse :: m a -> m a -> m a

satisfying **Monoid** and **Left Catch**:

morelse mzero b = b morelse a mzero = a morelse (morelse a b) c = morelse a (morelse b c) morelse (return a) b = return a

## 2 Instances of both

Some types could be made instances of both. For instance:

instance MonadOr [] where morelse [] b = b morelse a b = a

The left-biased implementation of mplus for the Maybe monad should be used as an implementation of morelse, but it is also possible to give an unbiased mplus for Maybe:

instance MonadPlus Maybe where mplus (Just a) Nothing = a mplus Nothing (Just a) = a mplus _ _ = Nothing instance MonadOr Maybe where morelse (Just a) _ = Just a morelse _ b = b

## 3 Discussion

Given that Control.Applicative(Alternative) now defines a class which seems innately bound to **Left Catch**, at least in spirit, it seems to make sense to clean up MonadPlus such that all instances obey **Left Distribution**? --sclv

I'd actually suggest almost the opposite, that MonadPlus be dispensed with and merged into Monad. The (controversial) fail method looks no different than an mzero, except the string argument; indeed, so far as I know `fail s` is just mzero for any MonadPlus. MonadPlus is also barely made use of; just guard and msum in the standard? To be concrete, I would make the following the default definitions (in Monad):

mzero = fail "something" mplus a b = a

These are thus somewhat trivial by default, but having msum=head and guard=assert (roughly; more like `(`assert` return ())`) for less-flexible monads doesn't seem actually wrong and could be useful fallbacks.

I also question the claim that Maybe and IO should be thought of as "left catch". IO is not even in MonadPlus, and I don't see how it can be meaningfully in any way other than the above. Maybe does satisfy Left Catch, but it seems almost like that's only because it's such a simple monad (holding only one value). It is a useful observation that it fails Left Distribution, but that may only call for weaker Monad/Plus conditions.

The MonadOr idea is a solid one, but it seems to be taking the monad in a different direction. So if there's a good match in Control.Applicative or Parsec, that might be the best place to develop that idea. -- Galen

The default