Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is guard based on Alternative?

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?

like image 569
haskellHQ Avatar asked Jun 22 '17 10:06

haskellHQ


1 Answers

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.

like image 158
Zeta Avatar answered Sep 28 '22 13:09

Zeta