Chirlo
Chirlo

Reputation: 6130

Understanding the signature of <$>

I have the applicative <$> operator more or less figured out, but I can't understand the signature I'm getting with the following example:

ghci>  let f x y z = x + y + z  -- f::Num a => a -> a -> a -> a
ghci> f <$> Just 2 <*> Just 3 <*> Just 4 
Just 9

This result I understand, but when checking the following type:

ghci> :t (<$> f)
(<$> f) :: Num a => ((a -> a -> a) -> b) -> a -> b  --This makes no sense to me

That signature I would understand as : a function that takes a (a -> a- > a) -> b function and an a as parameters and returns a b. According to this reasoning , I should call this like :

 (<$>f) f 4

which would result in an Integer. Obviously this is not true, so can you please help me understand how to read the type of (<$> f)?

Upvotes: 1

Views: 123

Answers (2)

chi
chi

Reputation: 116174

<$> takes as a second argument something of type g b, where g is any applicative functor.

You are passing f :: Num a => a -> a -> a -> a as a second argument. Let's ignore the Num a context to keep things simple.

Hence, we look for g,b such that g b = a -> a -> a -> a.

Let's write the type of f in prefix form:

f :: (->) a ((->) a ((->) a a)) = g b

Hence, g = (->) a and b = ((->) a ((->) a a)). The latter is b = a -> a -> a in infix form.

It happens that (->) a is an applicative functor, so <$> f type checks. Note however that <$> is used on a completely different functor than the Maybe one you were using in your examples. Hence the confusion.

TL;DR: overloaded identifiers can shapeshift to many things adapting to their contexts, possibly in some unexpected way.

Upvotes: 1

sepp2k
sepp2k

Reputation: 370425

a function that takes a (a -> a- > a) -> b function and an a as parameters and returns a b.

This is correct.

According to this reasoning , I should call this like :

(<$>f) f 4

which would result in an Integer.

No, because f does not have type (a -> a -> a) -> b or one compatible with it. Instead it has type Num a => a -> a -> a -> a. That is, f takes three numbers and produces a number, whereas we're looking for a function that takes a function (of type a -> a -> a) as its first argument.

Upvotes: 3

Related Questions