Difference between revisions of "MonadPlus reform proposal"
Blaisorblade (talk | contribs) (Question unbiased MonadPlus instance for Maybe) |
|||
(10 intermediate revisions by 8 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 === |
||
+ | <haskell> |
||
− | class Monad m => MonadZero m where |
||
− | + | class Monad m => MonadZero m where |
|
⚫ | |||
+ | </haskell> |
||
satisfying '''Left Zero''': |
satisfying '''Left Zero''': |
||
+ | |||
⚫ | |||
+ | <haskell> |
||
⚫ | |||
+ | </haskell> |
||
=== MonadPlus === |
=== MonadPlus === |
||
+ | <haskell> |
||
− | class MonadZero m => MonadPlus m where |
||
− | + | class MonadZero m => MonadPlus m where |
|
+ | mplus :: m a -> m a -> m a |
||
+ | </haskell> |
||
satisfying '''Monoid''' and '''Left Distribution''': |
satisfying '''Monoid''' and '''Left Distribution''': |
||
+ | |||
⚫ | |||
+ | <haskell> |
||
⚫ | |||
− | + | 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) |
||
+ | </haskell> |
||
=== MonadOr === |
=== MonadOr === |
||
+ | <haskell> |
||
− | class MonadZero m => MonadOr m where |
||
− | + | class MonadZero m => MonadOr m where |
|
⚫ | |||
+ | </haskell> |
||
satisfying '''Monoid''' and '''Left Catch''': |
satisfying '''Monoid''' and '''Left Catch''': |
||
+ | |||
⚫ | |||
+ | <haskell> |
||
⚫ | |||
− | + | morelse mzero b = b |
|
− | + | 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 |
||
⚫ | |||
+ | </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" |
||
⚫ | |||
+ | </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]] |
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