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
>>> :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.
>>> [(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
]
>>> 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:
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.