The expression:
(+) <$> (+3) <*> (*100)
will use the Functor
and Applicative
instances for a function in Haskell. Indeed, a -> b
is short for (->) a b
with (->)
a type constructor. So we can define instances as Functor (->) a
to work with a function. The Functor
[src] and Applicative
[src] instances are defined as:
instance Functor ((->) r) where
fmap = (.)
instance Applicative ((->) r) where
pure = const
(<*>) f g x = f x (g x)
liftA2 q f g x = q (f x) (g x)
The fmap
on a function thus acts as a "post-processor": it convers a function a -> b
into a function a -> c
if the first operand is a function b -> c
by applying that function on the result of the first function.
The Applicative
will take two functions f :: a -> b -> c
and g :: a -> b
and thus construct a function that maps x
on f x (g x)
.
If we thus look at the expression:
(+) <$> (+3) <*> (*100)
Then this is equivalent to:
(<*>) (fmap (+) (+3)) (*100)
The fmap
thus is equivalent to (.)
, so this is:
(<*>) ((+) . (+3)) (*100)
or:
(<*>) (\x -> ((x+3) +)) (*100)
which is equivalent to:
\y -> (\x -> ((x+3) +)) y ((*100) y)
We can simplify this to:
\y -> (y+3) +) (y*100)
and thus:
\y -> (y+3) + (y*100)
It thus produces a function will map y
to y + 3 + (y * 100)
. It will thus apply y
to both (+3)
and (*100)
and then adds these together because of the (+)
before the <$>
.
The rule in general is thus that:
g <$> f1 <*> f2 <*> … <*> fn
with fi :: a -> bi
and g :: b1 -> b2 -> … -> bn -> c
, then it creates a function that will map a variable x
to g (f1 x) (f2 x) … (fn x)
.