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 -> athat 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?
MonadPlusandMonoidserve different purposes.A
Monoidis 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,
MonadPlusnot only specifies that you have a monoidal structure, but also that that structure is related to how theMonadworks, and that that structure doesn't care about the value contained in the monad, this is (in part) indicated by the fact thatMonadPlustakes 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
MonadPlusshould satisfy one or both of these additional laws.So what about
Alternative?Applicativewas defined afterMonad, and logically belongs as a superclass ofMonad, but largely due to the different pressures on the designers back in Haskell 98, evenFunctorwasn't a superclass ofMonaduntil 2015. Now we finally haveApplicativeas a superclass ofMonadin GHC (if not yet in a language standard.)Effectively,
Alternativeis toApplicativewhatMonadPlusis toMonad.For these we'd get
analogously to what we have with
MonadPlusand there exist similar distributive and catch properties, at least one of which you should satisfy.Unfortunately, even
empty <*> m = emptylaw 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
fwith anyway.However, since
Applicativeis not a superclass ofMonadandAlternativeis not a superclass ofMonadPlus, we wind up defining both instances separately.Moreover, even if
Applicativewas a superclass ofMonad, you'd wind up needing theMonadPlusclass anyway, because even if we did obeythat isn't strictly enough to prove that
So claiming that something is a
MonadPlusis stronger than claiming it isAlternative.Now, by convention, the
MonadPlusandAlternativefor a given type should agree, but theMonoidmay be completely different.For instance the
MonadPlusandAlternativeforMaybedo the obvious thing:but the
Monoidinstance lifts a semigroup into aMonoid. Sadly because there did not exist aSemigroupclass at the time in Haskell 98, it does so by requiring aMonoid, but not using its unit. ಠ_ಠTL;DR
MonadPlusis a stronger claim thanAlternative, which in turn is a stronger claim thanMonoid, and while theMonadPlusandAlternativeinstances for a type should be related, theMonoidmay be (and sometimes is) something completely different.