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 Applicatives 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.