Reputation: 61
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.
Upvotes: 6
Views: 4493
Reputation: 78639
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)
.
Upvotes: 2
Reputation: 16251
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.
Upvotes: 5
Reputation: 102236
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]
Upvotes: 2
Reputation: 2801
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.
Upvotes: 5
Reputation: 137987
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]
Upvotes: 19