List and Comprehension Extensions

As of March 2020, School of Haskell has been switched to read-only mode.

These extensions enhance the abilities of Haskell’s list and comprehension syntaxes.

ParallelListComp

Available in: All recent GHC versions

The ParallelListComp extension allows you to zip multiple sub-comprehensions together. For example:

[ w + x + y + z | ((w, x), (y, z)) <- zip [ (w, x) | w <- ws, x <- xs ]
                                          [ (y, z) | y <- ys, z <- zs ] ]

can be reduced to

[ w + x + y + z | w <- ws, x <- xs | y <- ys, z <- zs ]

Try it out!

{-# LANGUAGE ParallelListComp #-}

main = print [ (w, x, y, z) | w <- [1 .. 3],
                              x <- [2 .. 4]
                            | y <- [3 .. 5],
                              z <- [4 .. 6] ]

TransformListComp

Available in: All recent GHC versions

The TransformListComp extension allows you to use SQL-like statements in list comprehensions.

then clauses

You can use then clauses to alter the list at that point in the comprehension. For example:

[ (x, y) | x <- xs,
           y <- ys,
           then reverse ]

A then clause takes the form:

then f

where:

f :: forall a. [a] -> [a]

then by clauses

You can use then by clauses to alter the list at that point in the comprehension in a way that uses some combination of the prior bindings in that comprehension. For example:

[ (x, y) | x <- xs,
           y <- ys,
           then sortWith by (x + y) ]

A then by clause takes the form:

then f by e

where, for some type t:

f :: forall a. (a -> t) -> [a] -> [a]
e :: t

then group using clauses

You can use then group using clauses to group up the list at that point in the comprehension. For example:

[ (x, y) | x <- xs,
           y <- ys,
           then group using permutations ]

A then group using clause takes the form:

then group using f

where:

f :: forall a. [a] -> [[a]]

Be aware that after the point of the then group using clause, all bindings before the clause now have a different type. Specifically, if a binding x had type t before the clause, then it now has type [t] after the clause.

then group by using clauses

You can use then group by using clauses to group up the list at that point in the comprehension in a way that uses some combination of the prior bindings in that comprehension. For example:

[ (x, y) | x <- xs,
           y <- ys,
           then group by (x + y) using groupWith ]

A then group by using clause takes the form:

then group by e using f

where, for some type t,

f :: forall a. (a -> t) -> [a] -> [[a]]
e :: t

A then group by using clause has the same effect on the types of existing bindings as a then group using clause does.

The the function

The function the (found in the GHC.Exts module in the base package) is sometimes useful with TransformListComp. It takes a list, checks it to make sure that all values in the list are equal to each other, then returns the single unique value that the list holds. This is useful when grouping, where you might want to include a variable in the output but, since you’ve used a grouping clause, it’s now a list that contains just one unique value.

For example:

1 = the [1, 1, 1]

Example

Try it out!

{-# LANGUAGE TransformListComp #-}
import GHC.Exts (groupWith, the)
import Data.List (permutations)

main = print $ [ (x, y, map the v) | x <- [1 .. 10],
                                     y <- [1 .. 10],
                                     let v = x + y,
                                     then group by v using groupWith,
                                     then take 10,
                                     then group using permutations,
                                     t <- concat v,
                                     then takeWhile by t < 3]

MonadComprehensions

Available in: GHC 7.2 or later

The MonadComprehensions extension generalizes list comprehensions, including ParallelListComp and TransformListComp, to work for any Monad (or MonadPlus, or, in the case of ParallelListComp, MonadZip). This removes all limitations from list comprehensions relative to do notation. For example:

[ (x, y) | x <- lookup e1 l,
           y <- lookup e2 l,
           then (\f -> maybe Nothing
                             (\x -> if f x == 2
                                       then Just x
                                       else Nothing))
                by (x * y) ]

MonadComprehensions automatically implies both ParallelListComp and TransformListComp, so you don’t have to activate them seperately.

The generalization of ParallelListComp uses the MonadZip type class, which is defined in the Control.Monad.Zip module in the base package; the use of guards requires the MonadPlus type class, which is defined in the Control.Monad module in the base package.

Try it out!

{-# LANGUAGE TransformListComp, MonadComprehensions #-}

l :: [(String, Int)]
l = [("a", 1), ("b", 2), ("c", 3)]

main = print $ [ (x, y) | x <- lookup "a" l,
                          y <- lookup "b" l,
                          then (\f ->
                                 maybe Nothing
                                       (\x -> if f x == 2
                                                 then Just x
                                                 else Nothing))
                               by (x * y) ]

OverloadedLists

Available in: GHC 7.8 or later

TODO

comments powered by Disqus