The standard-library Haskell typeclasses MonadPlus
, Alternative
, and Monoid
each provide two methods with essentially the same semantics:
- An empty value:
mzero
,empty
, ormempty
. - An operator
a -> a -> a
that joins values in the typeclass together:mplus
,<|>
, ormappend
.
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?
MonadPlus
andMonoid
serve different purposes.A
Monoid
is parameterized over a type of kind*
.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 theMonad
works, and that that structure doesn't care about the value contained in the monad, this is (in part) indicated by the fact thatMonadPlus
takes an argument of kind* -> *
.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
but there are two other competing extensions, the left (sic) distribution law
and the left catch law
So any instance of
MonadPlus
should satisfy one or both of these additional laws.So what about
Alternative
?Applicative
was defined afterMonad
, and logically belongs as a superclass ofMonad
, but largely due to the different pressures on the designers back in Haskell 98, evenFunctor
wasn't a superclass ofMonad
until 2015. Now we finally haveApplicative
as a superclass ofMonad
in GHC (if not yet in a language standard.)Effectively,
Alternative
is toApplicative
whatMonadPlus
is toMonad
.For these we'd get
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 anyways.However, since
Applicative
is not a superclass ofMonad
andAlternative
is not a superclass ofMonadPlus
, we wind up defining both instances separately.Moreover, even if
Applicative
was a superclass ofMonad
, you'd wind up needing theMonadPlus
class anyways, because even if we did obeythat isn't strictly enough to prove that
So claiming that something is a
MonadPlus
is stronger than claiming it isAlternative
.Now, by convention, the
MonadPlus
andAlternative
for a given type should agree, but theMonoid
may be completely different.For instance the
MonadPlus
andAlternative
forMaybe
do the obvious thing:but the
Monoid
instance lifts a semigroup into aMonoid
. Sadly because there did not exist aSemigroup
class at the time in Haskell 98, it does so by requring aMonoid
, but not using its unit. ಠ_ಠTL;DR
MonadPlus
is a stronger claim thanAlternative
, which in turn is a stronger claim thanMonoid
, and while theMonadPlus
andAlternative
instances for a type should be related, theMonoid
may be (and sometimes is) something completely different.