2

I am trying to write zipWith function using zip and list comprehension. I need to zip the two lists after the function is applied. However I don't know where to use the list comprehension.

I tried doing two list comprehensions for each list.

zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]

I am expecting the function to be identical to zipWith, however it is not loading and giving error:

Occurs check: cannot construct the infinite type:
        c ~ (b -> c, b -> c)
      Expected type: [c]
        Actual type: [(b -> c, b -> c)]
    • In the expression: zip [f x | x <- xs] [f y | y <- ys]
      In an equation for ‘zipWith'’:
          zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]

    • Relevant bindings include
        ys :: [b] (bound at tutorial5.hs:166:15)
        f :: a -> b -> c (bound at tutorial5.hs:166:10)
        zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
          (bound at tutorial5.hs:166:1)
    |
166 | zipWith' f xs ys = zip [f x | x <- xs] [f y | y <- ys]
Kale Joe
  • 163
  • 6
  • Why would you want to implement `zipWith` (the more general function) using `zip` (the more specific)? `zip = zipWith (,)`. – chepner Oct 17 '19 at 13:07

1 Answers1

3

Well, there are a few problems here. At the top level, think about the signature of zip :: [a] -> [b] -> [(a,b)]: there's no way that it can return a [c] where c is not a tuple, so you don't want zip to be the outer function call in your new zipWith. Your type error is arising from GHC noticing that it needs to force c to be a tuple of things with elements whose types contain c themselves (since f applied to anything will always have type b -> c).

Your list comprehensions are also basically the same thing as map f xs and map f ys. The second of these can't typecheck, since each element of ys is a b, and you can't apply f to a b (its first argument is an a).

You instead could start by zipping the input lists to get [(a,b)], and then using a list comprehension to run f on each pair:

zipWith' f xs ys = [f x y | (x,y) <- zip xs ys]

Or, with map and uncurry instead of a list comprehension:

zipWith' f xs ys = map (uncurry f) $ zip xs ys

Or, using the GHC parallel list comprehension extension (-XParallelListComp), explicitly designed to imitate zip:

zipWith' f xs ys = [f x y | x <- xs | y <- ys]

As mentioned above, you can't really do the zip last, since it'll produce tuples. You could do something like

zipWith' f xs ys = [fx y | (fx, y) <- zip [f x | x <- xs] ys]

Which applies f to the elements of the first list (in [f x | x <- xs], or alternatively map f xs), zips this list of partially applied functions with the list of second arguments, and then applies the partial functions to their corresponding second arguments in the outer comprehension, but it's a bit of a roundabout way to do this.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Lucy Maya Menon
  • 1,560
  • 8
  • 15