Difference between revisions of "MonadPlus reform proposal"

From HaskellWiki
Jump to navigation Jump to search
(Question unbiased MonadPlus instance for Maybe)
 
(One intermediate revision by one other user not shown)
Line 64: Line 64:
 
<haskell>
 
<haskell>
 
instance MonadPlus Maybe where
 
instance MonadPlus Maybe where
mplus (Just a) Nothing = a
+
mplus (Just a) Nothing = Just a
mplus Nothing (Just a) = a
+
mplus Nothing (Just a) = Just a
 
mplus _ _ = Nothing
 
mplus _ _ = Nothing
   
Line 72: Line 72:
 
morelse _ b = b
 
morelse _ b = b
 
</haskell>
 
</haskell>
  +
  +
Question: But does this instance satisfy '''Left Distribution'''? If a = Just v1 and b = Just v2, '''Left Distribution''' implies that Nothing = mplus (k v1) (k v2), which isn't generally true — take for instance
  +
<haskell>
  +
v1 = 0
  +
v2 = 1
  +
f 0 = Just 0
  +
f 1 = Nothing
  +
</haskell>
  +
  +
Am I missing something? -- Blaisorblade
   
 
== Discussion ==
 
== Discussion ==

Latest revision as of 18:22, 23 June 2015

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.

Proposal

It is proposed that MonadPlus be split like this:

MonadZero

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

satisfying Left Zero:

mzero >>= k = mzero

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)

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

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

Question: But does this instance satisfy Left Distribution? If a = Just v1 and b = Just v2, Left Distribution implies that Nothing = mplus (k v1) (k v2), which isn't generally true — take for instance

v1 = 0
v2 = 1
f 0 = Just 0
f 1 = Nothing

Am I missing something? -- Blaisorblade

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 mplus doesn't satisfy mplus mzero b = b, so you lose Monoid which seems to be the only thing people actually agree on :) -- Benmachine