coldwa.st
All guidesProgrammingWebDataHaskellConceptsCabal & buildsToolchainCompilerPerformanceEditor & HLS

Haskell · syntax · lists

Haskell list comprehensions, explained

By ColdwastUpdated Jun 14, 20266 min read#haskell#lists#syntax
Source code in a terminal editor
List comprehensions build a new list from existing ones, in a single readable expression.

One of the first things that makes Haskell feel elegant is the list comprehension: a concise way to build a new list from existing ones, mirroring the mathematical set-builder notation you may remember from school. This guide explains the syntax, multiple generators, guards, and how comprehensions relate to map and filter.

The basic syntax

A list comprehension has the shape [ output | generator, condition ]. Read the bar as "such that":

-- the squares of 1 to 10
squares = [x*x | x <- [1..10]]
-- [1,4,9,16,25,36,49,64,81,100]

Here x <- [1..10] is a generator: it draws each x from the list [1..10], and the part before the bar, x*x, is the output expression applied to every element.

Adding guards (filtering)

Lines of source code on a dark screen
Lines of code on a screen — a guard keeps only the elements that satisfy a condition.

A guard is a boolean condition that keeps only the elements satisfying it:

-- even numbers from 1 to 20, doubled
result = [x*2 | x <- [1..20], even x]
-- [4,8,12,16,20,24,28,32,36,40]

You can combine several guards, separated by commas; all must hold for an element to be included.

Multiple generators

With more than one generator, the comprehension iterates over every combination (like nested loops), in order:

-- all pairs from two small lists
pairs = [(x, y) | x <- [1,2,3], y <- ['a','b']]
-- [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')]

The rightmost generator varies fastest. You can also let a later generator depend on an earlier one — for example drawing y from [x..10].

How they relate to map and filter

A comprehension is essentially map and filter in one readable form. These are equivalent:

[x*2 | x <- xs, even x]
map (*2) (filter even xs)

Use whichever reads better for the case. Comprehensions shine when you have multiple generators or several guards; map/filter compose nicely in point-free pipelines. Because Haskell is lazy, a comprehension over an infinite list like [1..] only computes the elements you actually consume.

FAQ

What is a generator in a list comprehension? The x <- xs part: it draws each value of x from the list xs to feed the output expression.

What is a guard? A boolean condition (like even x) placed after a comma; only elements for which it's true are kept.

Can I use multiple generators? Yes — the comprehension produces every combination, with the rightmost generator varying fastest, like nested loops.

Are comprehensions just map and filter? Conceptually yes for the simple cases; comprehensions are often more readable with multiple generators or guards, while map/filter compose well in pipelines.

For the language behind the syntax, see what Haskell is; comprehensions are one of the features that make it concise and expressive.

Independent, community-maintained guide. coldwa.st is a programming-resources site; this article is new, original explanatory writing about Haskell list comprehensions, not affiliated with the language's maintainers. Code reflects standard Haskell/GHC behaviour — verify against current GHC docs.