First of all, remember how fmap
is defined for applicatives:
fmap f x = pure f <*> x
This means that your example is the same as (fmap (+ 5) (* 3)) 4
. The fmap
function for functions is just composition, so your exact expression is the same as ((+ 5) . (* 3)) 4
.
Now, let's think about why the instance is written the way it is. What <*>
does is essentially apply a function in the functor to a value in the functor. Specializing to (->) r
, this means it applies a function returned by a function from r
to a value returned by a function from r
. A function that returns a function is just a function of two arguments. So the real question is this: how would you apply a function of two arguments (r
and a
, returning b
) to a value a
returned by a function from r
?
The first thing to note is that you have to return a value of type (->) r
which means the result also has to be a function from r
. For reference, here is the <*>
function:
f <*> g = x -> f x (g x)
Since we want to return a function taking a value of type r
, x :: r
. The function we return has to have a type r -> b
. How can we get a value of type b
? Well, we have a function f :: r -> a -> b
. Since r
is going to be the argument of the result function, we get that for free. So now we have a function from a -> b
. So, as long as we have some value of type a
, we can get a value of type b
. But how do we get a value of type a
? Well, we have another function g :: r -> a
. So we can take our value of type r
(the parameter x
) and use it to get a value of type a
.
So the final idea is simple: we use the parameter to first get a value of type a
by plugging it into g
. The parameter has type r
, g
has type r -> a
, so we have an a
. Then, we plug both the parameter and the new value into f
. We need both because f
has a type r -> a -> b
. Once we plug both an r
and an a
in, we have a b1
. Since the parameter is in a lambda, the result has a type r -> b
, which is what we want.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…