haskellzipwith

How does the second parameter become a list of functions?


I am playing a bit with zipWith and encounter following:

Prelude Control.Applicative> :t zipWith id
zipWith id :: [b -> c] -> [b] -> [c]

Why does the compiler expect for the next argument a list of functions?

I tried to analyze, but could not conclude, why the next argument must be a list of functions.

How did the signature is getting apply, when I pass id to zipWith?


Solution

  • The type of zipWith is:

    zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    

    And the type of id is:

    id :: d -> d
    

    So if we now want to derive the type of zipWith id, we push the type of id :: d -> d into the type of the first argument of zipWith:

      d -> d
    ~ a -> (b -> c)
    

    So that means that: a ~ d and a ~ b -> c. So that means that the type of zipWith id is now:

       zipWith id :: [a] -> [b] -> [c]
    -> zipWith id :: [b -> c] -> [b] -> [c]
    

    How does this work: the first list has to contain a list of functions f :: b -> c, and the second list, a list of elements x :: b, and it thus calculates a list of elements f x :: c.

    For example:

    Prelude> zipWith id [(+1),(5/),(3*),(3-)] [1,4,2,5]
    [2.0,1.25,6.0,-2.0]
    

    since 1+1 is 2.0, 5/4 is 1.25, 3*2 is 6.0 and 3-5 is -2.0.

    So zipWith id will take two elements f and x, and apply id f x on these, or more verbose (id f) x. Since id f is f, it will thus calculate f x.

    We can thus conclude that zipWith is an elementwise mapping.