(r ->) applicative functor
<$>
is just another name for fmap
and its definition for (->) r
is (.)
(the composition operator):
intance Functor ((->) r) where
fmap f g = f . g
You can basically work out the implementation for <*>
just by looking at the types:
instance Applicative ((->) r) where
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
f <*> g = \x -> f x (g x)
You have a function from r
to a
to b
and a function from r
to a
. You want a funtion from r
to b
as a result. First thing you know is you return a function:
\x ->
Now you want to apply f
since it is the only item which may return a b
:
\x -> f _ _
Now the arguments for f
are of type r
and a
. r
is simply x
(since it alrady is of type r
and you can get an a
by applying g
to x
:
\x -> f x (g x)
Aaand you're done. Here's a link to the implementation in Haskell's Prelude.
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.
As a Haskell newbie myself, i'll try to explain the best way i can
The <$>
operator is the same as mapping a function on to another function.
When you do this:
(+) <$> (+3)
You are basically doing this:
fmap (+) (+3)
The above will call the Functor implementation of (->) r which is the following:
fmap f g = (\x -> f (g x))
So the result of fmap (+) (+3)
is (\x -> (+) (x + 3))
Note that the result of this expression has a type of a -> (a -> a)
Which is an applicative! That is why you can pass the result of (+) <$> (+3)
to the <*>
operator!
Why is it an applicative you might ask? Lets look the at the <*>
definition:
f (a -> b) -> f a -> f b
Notice that the first argument matches our returned function definition a -> (a -> a)
Now if we look at the <*>
operator implementation, it looks like this:
f <*> g = (\x -> f x (g x))
So when we put all those pieces together, we get this:
(+) <$> (+3) <*> (+5)
(\x -> (+) (x + 3)) <*> (+5)
(\y -> (\x -> (+) (x + 3)) y (y + 5))
(\y -> (+) (y + 3) (y + 5))