Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 831
wajeeh Avatar asked May 06 '12 13:05

wajeeh


People also ask

How do I use a map in Haskell?

map is a function that takes two parameters: a function and a list of elements. The type signature of map is (a -> b) -> [a] -> [b] . The (a -> b) part is the function you pass to map , we will call it f . f takes one value and returns another that may be of a different type.

What does zipWith do Haskell?

As the name suggests zipWith function in Haskell is used to zip the elements pausing to the function. With the zipWith function, we can pass our vales, zipWith always followed by one operator it can be anything on the basis of which it will zip the value of the argument and produce the result for us.

What is function composition in Haskell?

Composing functions is a common and useful way to create new functions in Haskell. Haskell composition is based on the idea of function composition in mathematics. In mathematics, if you have two functions f(x) and g(x), you compute their composition as f(g(x)). The expression f(g(x)) first calls g and then calls f.


3 Answers

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]
like image 105
Don Stewart Avatar answered Nov 16 '22 23:11

Don Stewart


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.

like image 42
JJJ Avatar answered Nov 17 '22 00:11

JJJ


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.

like image 31
Theodore Norvell Avatar answered Nov 16 '22 23:11

Theodore Norvell