6

I am relatively new to Haskell so apologies if my question sounds stupid. I have been trying to understand how function composition works and I have come across a problem that I was wondering someone could help me with. I am using map in a function composition in the following two scenarios:

  • map (*2) . filter even [1,2,3,4]
  • map (*2) . zipWith max [1,2] [4,5]

Although both the filter and zipWith functions return a list, only the first composition works while the second composition throws the below error:

"Couldn't match expected type '[Int] -> [Int]' with actual type '[c0]'

Any suggestions would be greatly appreciated.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
wajeeh
  • 61
  • 1
  • 2
  • 1
    Do the answers to [this question](http://stackoverflow.com/questions/2834626/haskell-dot-operator) help? (Especially [this one](http://stackoverflow.com/a/2834661/1256624)) – huon May 06 '12 at 13:49
  • 1
    The first one actually produces the output `Couldn't match expected type a0 -> [b0]' with actual type [a1]'` – Edwin Dalorzo May 06 '12 at 13:50

5 Answers5

18

Recall the type of (.).

(.) :: (b -> c) -> (a -> b) -> a -> c

It takes three arguments: two functions and an initial value, and returns the result of the two functions composed.

Now, application of a function to its arguments binds tighter than the (.) operator. So your expression:

map (*2) . filter even [1,2,3,4]

is parsed as:

(.) (map (*2)) (filter even [1,2,3,4])

now, the first argument, map (*2) is ok. It has type (b -> c), where b and c is Num a => [a]. However, the second argument is a single list:

Prelude> :t filter even [1,2,3,4]
filter even [1,2,3,4] :: Integral a => [a]

and so the type checker will complain that you're passing a [a] as an argument when the (.) function needs a function.

And that's what we see:

Couldn't match expected type `a0 -> [b0]' with actual type `[a1]'
In the return type of a call of `filter'
In the second argument of `(.)', namely `filter even [1, 2, 3, 4]'
In the expression: map (* 2) . filter even [1, 2, 3, 4]

So... parenthesization!

Either use the $ operator to add a parenthesis:

map (*2) . filter even $ [1,2,3,4]

or use explicit parens, removing the composition of two functions

map (*2) (filter even [1,2,3,4])

or even:

(map (*2) . filter even) [1,2,3,4]
Don Stewart
  • 137,316
  • 36
  • 365
  • 468
  • 1
    I know that OP asked specifically about function composition, but this example is really best suited to straight up `$`, _i.e._, `map (*2) $ filter even [1,2,3,4]`. – apc May 07 '12 at 05:33
5

The result of zipWith max [1,2] [4,5] is a list, not a function. The (.) operator requires a function as its right operand. Hence the error on your second line. Probably what you want is

map (*2) (zipWith max [1,2] [4,5])

Your first example does not compile on WinHugs (Hugs mode); it has the same error. The following will work

(map (*2) . filter even) [1,2,3,4]

as it composes two functions and applies the resulting function to an argument.

Dan Burton
  • 53,238
  • 27
  • 117
  • 198
Theodore Norvell
  • 15,366
  • 6
  • 31
  • 45
5

The following forms are valid:

map (* 2) $ filter even [1, 2, 3, 4]
(map (* 2) . filter even) [1, 2, 3, 4]
map (* 2) $ zipWith max [1, 2] [4, 5]
(\xs -> map (* 2) . zipWith max xs) [1, 2] [4, 5]

but not the following:

map (* 2) . filter even [1, 2, 3, 4]
map (* 2) . zipWith max [1, 2] [4, 5]
(map (* 2) . zipWith max) [1, 2] [4, 5]

Why is that so? Well, take for example

map (* 2) . zipWith max [1, 2] [4, 5]

it is the same as

(map (* 2)) . (((zipWith max) [1, 2]) [4, 5])

(map (* 2)) has type [Int] -> [Int] (assuming defaulting for Int), (((zipWith max) [1, 2]) [4, 5]) has type [Int] and (.) has type (b -> c) -> (a -> b) -> a -> c or ([Int] -> [Int]) -> ([Int] -> [Int]) -> [Int] -> [Int] in this non-polymorphic case, so this is ill-typed. On the other hand ($) has type (a -> b) -> a -> b, or ([Int] -> [Int]) -> [Int] -> [Int] in this non-polymorphic case, so this:

(map (* 2)) $ (((zipWith max) [1, 2]) [4, 5])

is well-typed.

JJJ
  • 2,731
  • 1
  • 12
  • 23
2

Due to the low precedence of (.), Haskell parses

map (*2) . filter even [1,2,3,4]

as

map (*2) . (filter even [1,2,3,4])

i.e. compose map (*2) (a function) with the result of filter even [1,2,3,4] (a list), which makes no sense, and is a type error.

You can fix this using @Theodore's suggestions, or by using ($):

map (*2) . filter even $ [1,2,3,4]
huon
  • 94,605
  • 21
  • 231
  • 225
2

If you check the type of map it is: (a -> b) -> [a] -> [b]

So, it takes a function of a into b and then a list of a and returns a list of b. Right?

Now, you already provide a function of a into b by passing the parameter (*2). So, your partially applied map function end up being: [Integer] -> [Integer] meaning that you will receive a list of integers and return a list of integers.

Up to this point, you could compose (.) a function that has the same signature. If you check what is the type of filter even you would see that it is: [Integer] -> [Integer], as such a valid candidate for composition here.

This composition then, does not alter the final signature of the function, if you check the type of: map (*2) . filter even it is [Integer] -> [Integer]

This would not be the case of the map (*2) . zipWith max [1,2] [4,5] because the zipWith max does not have the same signature as the one expected by map (*2).

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205