Distinction between typeclasses MonadPlus, Alternative, and Monoid?
alternative-functor, applicative, haskell, monadplus, monoids
Solution
`MonadPlus` and `Monoid` serve different purposes.
A `Monoid` is parameterized over a type of kind `*`.
class Monoid m where
mempty :: m
mappend :: m -> m -> m
and so it can be instantiated for almost any type for which there is an obvious operator that is associative and which has a unit.
However, `MonadPlus` not only specifies that you have a monoidal structure, but also that that structure is related to how the `Monad` works, and that that structure doesn't care about the value contained in the monad, this is (in part) indicated by the fact that `MonadPlus` takes an argument of kind `* -> *`.
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
In addition to the monoid laws, we have two potential sets of laws we can apply to `MonadPlus`. Sadly, the community disagrees as to what they should be.
At the least we know
mzero >>= k = mzero
but there are two other competing extensions, the left (sic) distribution law
mplus a b >>= k = mplus (a >>= k) (b >>= k)
and the left catch law
mplus (return a) b = return a
So any instance of `MonadPlus` should satisfy one or both of these additional laws.
So what about `Alternative`?
`Applicative` was defined after `Monad`, and logically belongs as a superclass of `Monad`, but largely due to the different pressures on the designers back in Haskell 98, even `Functor` wasn't a superclass of `Monad` until 2015. Now we finally have `Applicative` as a superclass of `Monad` in GHC (if not yet in a language standard.)
Effectively, `Alternative` is to `Applicative` what `MonadPlus` is to `Monad`.
For these we'd get
empty <*> m = empty
analogously to what we have with `MonadPlus` and there exist similar distributive and catch properties, at least one of which you should satisfy.
Unfortunately, even `empty <*> m = empty` law is too strong a claim. It doesn't hold for Backwards, for instance!
When we look at MonadPlus, the empty >>= f = empty law is nearly forced on us. The empty construction can't have any 'a's in it to call the function `f` with anyway.
However, since `Applicative` is not a superclass of `Monad` and `Alternative` is not a superclass of `MonadPlus`, we wind up defining both instances separately.
Moreover, even if `Applicative` was a superclass of `Monad`, you'd wind up needing the `MonadPlus` class anyway, because even if we did obey
empty <*> m = empty
that isn't strictly enough to prove that
empty >>= f = empty
So claiming that something is a `MonadPlus` is stronger than claiming it is `Alternative`.
Now, by convention, the `MonadPlus` and `Alternative` for a given type should agree, but the `Monoid` may be completely different.
For instance the `MonadPlus` and `Alternative` for `Maybe` do the obvious thing:
instance MonadPlus Maybe where
mzero = Nothing
mplus (Just a) _ = Just a
mplus _ mb = mb
but the `Monoid` instance lifts a semigroup into a `Monoid`. Sadly because there did not exist a `Semigroup` class at the time in Haskell 98, it does so by requiring a `Monoid`, but not using its unit. ಠ_ಠ
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
mappend (Just a) (Just b) = Just (mappend a b)
mappend Nothing x = x
mappend x Nothing = x
mappend Nothing Nothing = Nothing
TL;DR `MonadPlus` is a stronger claim than `Alternative`, which in turn is a stronger claim than `Monoid`, and while the `MonadPlus` and `Alternative` instances for a type should be related, the `Monoid` may be (and sometimes is) something completely different.
Problem
The standard-library Haskell typeclasses `MonadPlus`, `Alternative`, and `Monoid` each provide two methods with essentially the same semantics: - An empty value: `mzero`, `empty`, or `mempty`. - An operator `a -> a -> a` that joins values in the typeclass together: `mplus`, `<|>`, or `mappend`. All three specify these laws to which instances should adhere: ``` mempty `mappend` x = x x `mappend` mempty = x ``` Thus, it seems the three typeclasses are all providing the same methods. (`Alternative` also provides `some` and `many`, but their default definitions are usually sufficient, and so they're not too important in terms of this question.) So, my query is: why have these three extremely similar classes? Is there any real difference between them, besides their differing superclass constraints?