Skip to content

Parallel List Comprehensions: zip and zipWith as a List Comprehension

The final type of list comprehension I will mention here does not exist in standard Haskell but can be enabled in GHC(i) using the ParallelListComp language extension. In GHCi, you enable this extension using

GHCi
>>> :set -XParallelListComp

In a Haskell source code file, you add the following pragma at the beginning of the file:

{-# LANGUAGE ParallelListComp #-}

With this, we can write the expression zip xs ys as [(x,y) | x <- xs | y <- ys] and the expression zipWith f xs ys as [f x y | x <- xs | y <- ys]. Note the vertical bar between x <- xs and y <- ys instead of a comma.

GHCi
>>> [(x,y) | x <- [1..3] | y <- [10,20,30]]
[(1,10),(2,20),(3,30)]
>>> [x + y | x <- [1..3] | y <- [10,20,30]]
[11,22,33]

So a parallel list comprehension does not combine every x from the first list with every y from the second list. Instead, it combines the first x from the first list with the first y from the second list, the second x from the first list with the second y from the second list, and so on, just as zip or zipWith would do.

Here's a more meaningful example. Remember our definition of Fibonacci numbers from earlier:

fibonaccis = 0 : 1 : zipWith (+) fibonaccis (tail fibonaccis)

Depending on your taste, you may find this definition using a parallel list comprehension more readable:

fibonaccis = 0 : 1 : [ x + y
                     | x <- fibonaccis
                     | y <- tail fibonaccis
                     ]
GHCi
>>> fibonaccis = 0 : 1 : [x + y | x <- fibonaccis | y <- tail fibonaccis]
>>> take 20 fibonaccis
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181]

Clearly, parallel list comprehensions are useful and one may wonder why they aren't part of standard Haskell. One possible theory is that they do not mirror any notation we know from mathematics. The other list comprehensions do. If we take the comprehension

[x * y | x <- [1..10], even x, y <- [x..10], odd (x + y)]

for example, then this looks a lot like set builder notation that you should have learned in an introductory math class:

\[ \{x \cdot y \mid x \in \{1, \ldots, 10\}, x \text{ is even}, y \in \{x, \ldots, 10\}, x + y \text{ is odd}\} \]

The only difference is that set builder notation describes a set—the order of the set elements does not matter—while list comprehensions describe lists, where the order of the list elements does matter.