MonadPlus reform proposal
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.
It is proposed that MonadPlus be split like this:
class Monad m => MonadZero m where mzero :: m a
satisfying Left Zero:
mzero >>= k = mzero
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)
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 = Just a mplus Nothing (Just a) = Just a mplus _ _ = Nothing instance MonadOr Maybe where morelse (Just a) _ = Just a morelse _ b = b
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. -- GalenThe default