Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the default definition of (<*>) in Haskell work?

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.

like image 880
ThinkRedstone Avatar asked Nov 02 '17 00:11

ThinkRedstone


People also ask

How are functions defined in Haskell?

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.

What does ++ mean in Haskell?

The ++ operator is the list concatenation operator which takes two lists as operands and "combines" them into a single list.

What does in do in Haskell?

in goes along with let to name one or more local expressions in a pure function.


1 Answers

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).

like image 83
sepp2k Avatar answered Oct 27 '22 00:10

sepp2k