softshipper
softshipper

Reputation: 34071

What does the compiler mean?

I have following applicative expression and I know it is wrong:

Prelude> [Just (*2), Just (+9)] <*> [(Just 3),(Just 4), (Just 5)]

and the compiler complains:

<interactive>:2:2: error:
    * Couldn't match expected type `Maybe Integer -> b'
                  with actual type `Maybe (Integer -> Integer)'
    * Possible cause: `Just' is applied to too many arguments
      In the expression: Just (* 2)
      In the first argument of `(<*>)', namely `[Just (* 2), Just (+ 9)]'
      In the expression:
        [Just (* 2), Just (+ 9)] <*> [(Just 3), (Just 4), (Just 5)]
    * Relevant bindings include it :: [b] (bound at <interactive>:2:1)

What does the compiler is trying to say?

This error message:

* Couldn't match expected type `Maybe Integer -> b'
              with actual type `Maybe (Integer -> Integer)'

it means this part [Just (*2), Just (+9)] of the expression?

Let's look the signature of the function (<*>):

(<*>) :: f (a -> b) -> f a -> f b

substitute it with List and Maybe type constructor above:

(<*>) :: [] (Maybe Integer -> b) -> [] (Maybe Integer) -> [] b

Which type should the b have?

Upvotes: 2

Views: 109

Answers (4)

Redu
Redu

Reputation: 26161

In

Prelude> [Just (*2), Just (+9)] <*> [(Just 3),(Just 4), (Just 5)]

instruction there are two functors. The outer one is List and the inner one is Maybe So when you put the <*> applicative operation in between two lists the first list should contain some function type elements to become an applicative functor, such as;

Prelude> [(+1),(*2)] <*> [1,2]
[2,3,2,4]

However in your instruction the first list just contains some Maybe values. This doesn't qualify the list as an applicative functor. But the elements of the list are of type Maybe (a -> b). That means not the list itself but it's contents are applicative functors. So in order to turn our list into an applicative list, you are expected replace the applicative Maybe values with a function which takes an applicative Maybe (a -> b) and a Maybe a and gives a Maybe b In another words f (a -> b) -> f a -> f b and this happens to be <*> operator.

So if you rephrase your code as follows you will get the expected result;

Prelude> (<*>) <$> [Just (*2), Just (+9)] <*> [(Just 3),(Just 4),(Just 5)]
[Just 6,Just 8,Just 10,Just 12,Just 13,Just 14]

Upvotes: 0

melpomene
melpomene

Reputation: 85767

The compiler is referring to this part of your code (In the expression: Just (* 2)):

[Just (*2), Just (+9)] <*> [(Just 3),(Just 4), (Just 5)]
 ^^^^^^^^^

It's also saying that the actual type of Just (*2) is Maybe (Integer -> Integer), but the way you're using it requires a value of type Maybe Integer -> b (for some type b).

If you look at the type of

(<*>) :: f (a -> b) -> f a -> f b

you can see that the first argument must be some type constructor f applied to a function type a -> b. You have a list of values, so f is [].

Therefore the first argument must be a list of functions, but what you have is a list of Maybes. That's why this code is an error.

The rest of the error message comes from the second argument of <*>, [(Just 3),(Just 4), (Just 5)], which is a list of arguments (for the list of functions in the first argument). That is, the compiler knows it needs a value of type f a and you gave it [(Just 3),(Just 4), (Just 5)], so it deduces f = [], a = Maybe Integer.

So the type of the first argument f (a -> b) becomes [] (Maybe Integer -> b) (which is the same as [Maybe Integer -> b]). b here is completely free. You can use whatever result type you want and you'll get f b (i.e. [b] (a list of results)) back from <*>.

Upvotes: 3

luqui
luqui

Reputation: 60463

Let's substitute more carefully

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
[Just (*2), Just (+9)] :: [Maybe (Int -> Int)]   --wlog
[Just 3, Just 4, Just 5] :: [Maybe Int]

So f = [], thus:

(<*>) :: [a -> b] -> [a] -> [b]

But now we are to match [a -> b] against [Maybe (Int -> Int)] and that's impossible -- a function and a Maybe can't be the same. You are trying to do function application under two functors, not just one. I.e. you would need something of type

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

Fortunately, that's easy to make: liftA2 (<*>).

Or, if you want something fancier, you could operate instead on the composite functor Compose [] Maybe.

getCompose $ Compose [Just (*2), Just (+9)] <*> Compose [Just 3, Just 4, Just 5]

(But when Compose is cool is when you don't immediately getCompose the result, but use it as an important abstraction in whatever program you are writing.)

Upvotes: 3

Igor Drozdov
Igor Drozdov

Reputation: 15045

There's a wrong substitution here:

(<*>) :: [] (Maybe Integer -> b) -> [] (Maybe Integer) -> [] b

[Just (*2), Just (+9)] has the following type: Num a => [Maybe (a -> a)]

That's because List is f in (<*>) :: f (a -> b) -> f a -> f b.

According to the (<*>)'s type, you can have:

[(*2), (+9)] <*> [2, 3]

Or

Just (*2) <*> Just 2

and [] and Maybe will be Applicative contexts in these expressions correspondingly, but not both, using just (<*>)

The following esoteric expression will compile:

[(Just (*2) <*>), (Just (+9) <*>)] <*> [(Just 3), (Just 4), (Just 5)]

But I'm not sure it is what you're looking for.

Upvotes: 2

Related Questions