The need for pure in Applicatives
I'm at the edge of my competency here, so don't take this for more than it is, but it was a bit too long for a comment.
There may be practical reasons to include pure
in the type class, but many Haskell abstractions are derived from theoretical foundations, and I believe that that's the case for Applicative
as well. As the documentation says, it's a strong lax monoidal functor (see https://cstheory.stackexchange.com/q/12412/56098 for a elaboration). I suppose that pure
serves as the identity, just like return
does for Monad
(which is a monoid in the category of endofunctors).
Consider pure
and liftA2
:
pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
If you squint a little, you may be able to imagine that liftA2
is a binary operation, which is also what the documentation states:
Lift a binary function to actions.
pure
, then, is the corresponding identity.
fmap
doesn't always cut it. Specifically, pure
is what lets you introduce f
(where f
is Applicative
) when you don't already have it. A good example is
sequence :: Applicative f => [f a] -> f [a]
It takes a list of "actions" producing values and turns it into an action producing a list of values. What happens when there are no actions in the list? The only sane result is an action that produces no values:
sequence [] = pure [] -- no way to express this with an fmap
-- for completeness
sequence ((:) x xs) = (:) <$> x <*> sequence xs
If you didn't have pure
, you'd be forced to require a nonempty list of actions. You could definitely make it work, but it's like talking about addition without mentioning 0 or multiplication without 1 (as others have said, because Applicative
s are monoidal). You will repeatedly run into edge cases that would be easily solved with pure
but instead have to be solved by weird restrictions on your inputs and other band-aids.