简体   繁体   中英

Haskell: Using map in function composition

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.

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]

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.

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.

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

ie 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]

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 map (*2) . filter even it is [Integer] -> [Integer]

This would not be the case of the map (*2) . zipWith max [1,2] [4,5] 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) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM