In Haskell, the default implementation of the (<*>)
operator (it applies an applicative of functions a->b
to an applicative of a
resulting in an applicative of b
) in Control.Applicative is defined as such-
(<*>) :: f (a -> b) -> f a -> f b
(<*>) = liftA2 id
and I simply can't understand how it works.
liftA2
has the type liftA2 :: (a -> b -> c) -> f a -> f b -> f c
, meaning it takes a binary function, which id
is not. From my understanding, this means id
is somehow interpreted as some more complex type- but I'm not sure which or how it makes this definition work. If someone could explain perhaps what type is id
interpreted as (what type the a
in the id :: a -> a
definition stands for) and also walk through how it results in a function which takes an applicative of functions and an applicative of values and applies them I would be very thankful.
The most basic way of defining a function in Haskell is to ``declare'' what it does. For example, we can write: double :: Int -> Int double n = 2*n. Here, the first line specifies the type of the function and the second line tells us how the output of double depends on its input.
The ++ operator is the list concatenation operator which takes two lists as operands and "combines" them into a single list.
in goes along with let to name one or more local expressions in a pure function.
Let's say the type of id
is d -> d
, so all our type variables have different names. Now let's introduce two new type variables e
and t
and say that d = e -> t
. That makes the type of id
:
id :: (e -> t) -> e -> t
Now this fits the type of the first argument to liftA2
with a = e -> t
, b = e
and c = t
. So with those assignments, the type of liftA2
becomes:
liftA2 :: ((e -> t) -> e -> t) -> f (e -> t) -> f e -> f t
And if we apply the first argument, the remaining type becomes f (e -> t) -> f e -> f t
, which is exactly the type of <*>
(modulo renaming).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With