I had a couple of hours of fun today trying to understand what the arrow operator applicative does in Haskell. I am now trying to verify whether my understanding is correct. In short, I found that for the arrow operator applicative
(f <*> g <*> h <*> v) z = f z (g z) (h z) (v z)
Before I proceed, I am aware of this discussion but found it to be very convoluted and much more complicated than what I hope I derived today.
In order to understand what the applicative does I started from the definition of the arrow applicative in base
instance Applicative ((->) a) where
pure = const
(<*>) f g x = f x (g x)
and then proceeded to explore what the expressions
(f <*> g <*> h) z
and
(f <*> g <*> h <*> v) z
yield when expanded.
From the definition we get that
f <*> g = \x -> f x (g x)
Because (<*>)
is left associative, it follows that
f <*> g <*> h = (f <*> g) <*> h
= (\x -> f x (g x)) <*> h
= \y -> (\x -> f x (g x)) y (h y)
Therefore
(f <*> g <*> h) z = (\y -> (\x -> f x (g x)) y (h y)) z
= (\x -> f x (g x)) z (h z)
= (f z (g z)) (h z)
= f z (g z) (h z)
The last step is due to the fact that function application is left associative. Similarly
(f <*> g <*> h <*> v) z = f z (g z) (h z) (v z)
This, to me, provides a very clear intuitive idea of what the arrow applicative does. But is this correct?
To test the result I ran, for example, the following,
λ> ((\z g h v -> [z, g, h, v]) <*> (1+) <*> (2+) <*> (3+)) 4
[4,5,6,7]
which conforms to the result derived above.
Before doing the expansion above I found this applicative very difficult to understand, since extremely complicated behaviour can result from its use because of currying. In particular, in
(f <*> g <*> h <*> v) z = f z (g z) (h z) (v z)
functions can return other functions. Here is an example:
λ> ((\z g -> g) <*> pure (++) <*> pure "foo" <*> pure "bar") undefined
"foobar"
In this case z=undefined
is ignored by all functions, because pure x z = x
and the first function ignores z
by construction. Furthermore, the first function takes only two arguments but returns a function taking two arguments.