Why is guard
based on Alternative
?
guard :: Alternative f => Bool -> f ()
-- guard b is pure () if b is True,
-- and empty if b is False.
I ask because guard
only uses the empty
from Alternative
. It doesn't use <|>
from Alternative
at all. So why bother using Alternative
in the first place?
I guess this is because there is some unstated idea behind Alternative
's empty
that matches perfectly with what we're trying to accomplish with guard
(stop on False
, continue on True
). If this is the case, please enlighten me about this unstated idea.
Yet at the same time, if feels that we're just ignoring <|>
. It feels almost as if guard
is not "fully capturing" what Alternative
is all about. I hope that makes sense. To make it more concrete: Why didn't they invent another type class called something like Stoppable
(or Abortable
) and used that instead of Alternative
?
TL;DR: Historical reasons. It was envisioned like this in MonadPlus
, which got its Applicative
variant Alternative
later, and no one has proposed to split Alternative
into AZero
and AChoice
or similar.
Alternative
is a relatively new idea, just like Applicative
. Back when guard
was first envisioned, it was based on MonadPlus
, a Monad
that should support choice and failure, just like Alternative
. Its original type was thus
guard :: MonadPlus m => Bool -> m ()
That was specified in the Haskell 98 report, where MonadPlus
was already noted. Haskell 1.0 didn't use monads at all, by the way. When Applicative
finally got a superclass of Monad
, Alternative
got a superclass of MonadPlus
, and mzero = empty
and mplus = (<|>)
.
Well, now we know why guard
uses Alternative
. Because it was based on MonadPlus
beforehand. So why is MonadPlus
defined like this?
One would have to write a mail to SPJ or someone else from the committee to get their rationale from 1998, because just one year later, Erik Meijer and Graham Hutton wrote their "Monadic Parsing in Haskell" paper. If you have a look at the paper, you'll notice that their MonadPlus
just works like you intend:
class Monad m => MonadZero m where zero :: m a class MonadZero m => MonadPlus m where (++) :: m a -> m a -> m a
So it's certainly possible to handle this "stoppable" the way you've described it. However, there is simply no base
class that currently defines empty
without Alternative
. There could be one, but it wasn't proposed yet.
Note that this is a recurring theme with Haskell classes. Monoid
contains mappend
and mempty
. After its conception, someone noticed that there are certain types where mappend
makes sense, but not mempty
. For example
newtype Min a = Min a
combine :: Ord a => Min a -> Min a -> Min a
combine (Min x) (Min y) = Min (min x y)
Here, mappend = combine
is clearly associative, whereas an empty Min
isn't possible if we just use Ord
, we would have to use Bounded
. That's why there is now Semigroup
, which isn't a base class of Monoid
yet, but gives us that associative operation.
To come back to your original question: guard
uses Alternative
, because Alternative
provides empty
, and empty
"stops" the evaluation in certain Alternative
's. There's no other class that contains that, yet.
But with a proposal, there might be at some point, although I'm not sure what's the community's opinion on splitting Alternative
is.
By the way, languages like PureScript split Alternative
, although they split it the other way round…
For more information about Alternative
and why I used Monoid
as another example, see Confused by the meaning of the 'Alternative' type class and its relationship to other type classes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With