Consider the type signature of <*>
:
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Compare this to the type signature for ordinary function application, $
:
($) :: (a -> b) -> a -> b
Notice that they are extremely similar! Indeed, the <*>
operator effectively generalizes application so that it can be overloaded based on the types involved. This is easy to see when using the simplest Applicative
, Identity
:
ghci> Identity (+) <*> Identity 1 <*> Identity 2
Identity 3
This can also be seen with slightly more complicated applicative functors, such as Maybe
:
ghci> Just (+) <*> Just 1 <*> Just 2
Just 3
ghci> Just (+) <*> Nothing <*> Just 2
Nothing
For (->) r
, the Applicative
instance performs a sort of function composition, which produces a new function that accepts a sort of “context” and threads it to all of the values to produce the function and its arguments:
ghci> ((\_ -> (+)) <*> (+ 3) <*> (* 100)) 5
508
In the above example, I have only used <*>
, so I’ve explicitly written out the first argument as ignoring its argument and always producing (+)
. However, Applicative
typeclass also includes the pure
function, which has the same purpose of “lifting” a pure value into an applicative functor:
ghci> (pure (+) <*> (+ 3) <*> (* 100)) 5
508
In practice, though, you will rarely see pure x <*> y
because it is precisely equivalent to x <$> y
by the Applicative
laws, since <$>
is just an infix synonym for fmap
. Therefore, we have the common idiom:
ghci> ((+) <$> (+ 3) <*> (* 100)) 5
508
More generally, if you see any expression that looks like this:
f <$> a <*> b
…you can read it more or less like the ordinary function application f a b
, except in the context of a particular Applicative
instance’s idioms. In fact, an original formulation of Applicative
proposed the idea of “idiom brackets”, which would add the following as syntactic sugar for the above expression:
(| f a b |)
However, Haskellers seem to be satisfied enough with the infix operators that the benefits of adding the additional syntax has not been deemed worth the cost, so <$>
and <*>
remain necessary.