Difference between revisions of "MonadPlus reform proposal"

From HaskellWiki
Jump to navigation Jump to search
(Question unbiased MonadPlus instance for Maybe)
 
(8 intermediate revisions by 7 users not shown)
Line 7: Line 7:
 
=== MonadZero ===
 
=== MonadZero ===
   
  +
<haskell>
class Monad m => MonadZero m where
 
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 ===
   
  +
<haskell>
class MonadZero m => MonadPlus m where
 
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 b = b
 
  +
<haskell>
mplus a mzero = a
 
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 ===
   
  +
<haskell>
class MonadZero m => MonadOr m where
 
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 b = b
 
  +
<haskell>
morelse a mzero = a
 
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 ==
 
== Instances of both ==
Line 39: Line 54:
 
Some types could be made instances of both. For instance:
 
Some types could be made instances of both. For instance:
   
  +
<haskell>
instance MonadOr [] where
+
instance MonadOr [] where
 
morelse [] b = b
 
morelse [] b = b
 
morelse a b = a
 
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 = Just a
  +
mplus Nothing (Just a) = Just a
  +
mplus _ _ = Nothing
  +
  +
instance MonadOr Maybe where
 
morelse (Just a) _ = Just a
 
morelse _ b = b
  +
</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 ==
  +
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:Proposals]] [[Category:Monad]]

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