← Fundamentals
Guided lesson 6 questions · Haskell only

Haskell lazy lists and finite demand

A list cell is built when some code asks for it. Follow what take asks for, and what is never built.

GHC builds each cell of a Haskell list only when some code asks for it. So a list without an end is safe while the code that reads it stops, and a function that needs the last cell never finishes.

Written for Haskell.

Before you begin

Why this matters

Haskell code uses lists without an end every day: for numbering, for repeating patterns, and for “every next value”. If you can follow which cells a program asks for, you can predict whether it finishes, spot the call that never returns, and see which lists stay in memory.

What this helps you reason about

  • Predict how much of a list a program builds, and what is never built.
  • Spot a call such as length or filter that never finishes on a list without an end.
  • Say when a named list is built once and kept in memory.

In Python or Java, a list holds all of its elements, so a list without an end would never finish building. Python generators and Java streams give values one at a time instead, as a separate kind of object. A Haskell list does both jobs. It is an ordinary list, and each of its cells is built only when some code asks for it. This lesson follows what take, takeWhile, and zip ask for, what is never built, and which functions never finish.

Already know this? The short version
  • A list cell is built when some code asks for it, and the rest stays a thunk; GHCi shows it as _ (unevaluated).
  • [1 ..], iterate f x, cycle xs, and repeat x have no end that a program reaches; take, takeWhile, head, null, and zip with a finite list stop reading, and a function that gives a cell with : before it asks for the rest makes such a list.
  • length, sum, last, reverse, sort, and show need the last cell and never finish on such a list, and filter never finishes once no later element passes; a named list is built at most once, and its built cells stay in memory while the name is still used.
  • The section “What goes wrong with lazy lists in real programs?” covers length on an infinite or huge list, a list kept alive by a name, filter where takeWhile was meant, and show on an infinite structure.

Go to the final check· Go to the practice tickets

Words in this lesson (5)
list
A sequence of values of one type, written [12, 7, 31]. It is a chain of cells: each cell holds one element and a link to the rest of the list, and [] is the empty list.
non-strict evaluation
The Haskell rule that an expression is evaluated only when its value is needed. A binding that nothing needs is never evaluated. GHC implements the rule as lazy evaluation: a named value is usually computed at most once and then shared.
thunk
GHC’s stored description of work that has not happened yet. When the value is needed, GHC runs the work and replaces the thunk with the result.
infinite list
A list in which every cell has a rest, such as cycle [1, 2]. Only the cells that some code asks for are ever built.
space leak
Memory a Haskell program keeps for longer than its results need, often because a thunk still refers to a large value.

Examples are focused snippets; imports, enclosing functions, or classes may be omitted. C examples use C11; Haskell examples use Haskell 2010. Expand the notes for language-specific depth.

01 / Cells on demand

When is a list cell built?

Arrays, lists, and vectors showed that a Haskell list is a chain of cells, each with one element and a link to the rest. Haskell bindings and recursive definitions showed that a binding is computed when its value is first needed. This is non-strict evaluation, and lists follow it one cell at a time: a cell is built only when some code asks for it.

[1 ..] counts up from 1 and, in practice, never ends. Writing it builds nothing. take 3 beads gives the first three elements of beads, so it asks for three cells. The rest of beads stays a thunk: work that has not happened yet. No code asks for it, so it is never built.

The stepper shows a list that is partly built in the same way: 1 : 2 : 3 : _ (unevaluated) is three cells, then a rest that is still a thunk.

Main.hs
beads :: [Int]
beads = [1 ..] -- counts up from 1

firstThree :: [Int]
firstThree = take 3 beads -- [1,2,3]
-- take builds three cells of beads; the rest is never built

A list cell is built when some code asks for it; a cell that no code asks for is never built.

Precisely: where [1 ..] ends

[1 ..] is enumFrom 1. For Integer, it has no end. For Int, it stops at maxBound, the largest Int, which is 9223372036854775807 on the stepper’s 64-bit machine, so no program walks to its end. The Haskell 2010 Report specifies non-strict semantics, not when cells are built; building each cell once and keeping it is what GHC does.

Experiment

Count the cells that take builds

print needs firstThree, firstThree needs three cells of beads, and each cell is built when take asks for it. The rest of beads stays a thunk, because no code asks for it.

Why it matters The number of cells built is decided by the code that reads the list, not by the list.

beads has no last element. Predict how many cells of beads exist after print firstThree, and whether head beads builds more. Then change take 3 to take 5 and predict again.

main :: IO ()
main = do
  let beads = [1 ..] :: [Int]
      firstThree = take 3 beads
  print firstThree
  print (head beads)
What to look for

Each stop shows one place, such as Main.main.firstThree, not a list of frames. The stops are line 2, line 5 with firstThree shown as _ (unevaluated), line 4, and line 3. On line 6, [1,2,3] has printed, and beads shows 1 : 2 : 3 : _ (unevaluated): three cells, then a rest that nobody has asked for. head beads reads the first cell, so nothing more is built, and 1 prints. After the edit, [1,2,3,4,5] prints, and beads shows 1 : 2 : 3 : 4 : 5 : ....; GHCi writes .... where it stops printing a long value.

Expected output

[1,2,3]
1

02 / Infinite lists

How can a list without an end be safe to use?

An infinite list is a list in which every cell has a rest. Four standard ways make one:

  • [1 ..] counts up from 1.
  • iterate f x is x, then f x, then f (f x), and so on. iterate (+ 2) 6 is 6, 8, 10, …; (+ 2) is + with its right side filled in, a function that adds 2, and (< 11) is the test “less than 11”.
  • cycle xs repeats the list xs: cycle ["red", "blue", "white"].
  • repeat x is x again and again.

An infinite list is safe when the code that reads it stops. take n stops after n elements. takeWhile test stops at the first element that fails the test. head reads one cell. zip pairs two lists and stops when either one ends, so zip [1 ..] numbers any list.

You can make an infinite list yourself. countFrom n = n : countFrom (n + 1) gives a cell with n before it asks for the rest. Nothing asks for the rest until some code reads that far. [1 ..] and iterate work the same way.

Main.hs
rows :: [Int]
rows = iterate (+ 2) 6 -- 6, 8, 10, …

colours :: [String]
colours = cycle ["red", "blue", "white"]

-- take 3 rows is [6,8,10]
-- takeWhile (< 11) rows is [6,8,10]
-- zip [1 ..] ["red", "blue"] is [(1,"red"),(2,"blue")]
-- head (repeat "gold") is "gold"

An infinite list is safe while the code that reads it stops: take, takeWhile, head, and zip with a finite list all stop.

Coming from Python: A Python generator gives values on request, but it can be read only once. A Haskell list keeps the cells it has built, and any code can read them again.

Precisely: cycle builds a loop

cycle xs copies the cells of xs once and links the last copy back to the first. The list never ends, but it uses as many cells as xs has, however far it is read. cycle [] has no cells to repeat: the program stops with Prelude.cycle: empty list.

Experiment

Number a repeating pattern with zip

zip builds a pair only when something asks for it, and it stops as soon as either list ends. take 4 asks for four pairs. In the edit, [1 .. 3] ends after three numbers, so zip asks colours for three cells and no more.

Why it matters An infinite list is safe beside a finite one: zip, take, and takeWhile decide where the reading stops.

zip [1 ..] colours pairs two lists that have no end. Predict what take 4 numbered prints, and how many cells of colours exist afterwards. Then change line 4 to numbered = zip [1 .. 3] colours :: [(Int, String)] and line 5 to print numbered, and predict again.

main :: IO ()
main = do
  let colours = cycle ["red", "blue", "white"]
      numbered = zip [1 ..] colours :: [(Int, String)]
  print (take 4 numbered)
  print (take 2 colours)
What to look for

After stops on lines 2, 5, 4, and 3, the run prints [(1,"red"),(2,"blue"),(3,"white"),(4,"red")]. On line 6, colours shows "red" : "blue" : "white" : "red" : "blue" : ....: five cells, although take asked for only four. cycle links its last cell back to its first, so the fourth cell is the first one again, and GHCi follows the loop until it stops printing. Then ["red","blue"] prints. After the edit, zip stops when [1 .. 3] ends: the run prints [(1,"red"),(2,"blue"),(3,"white")], and colours shows "red" : "blue" : "white" : _ (unevaluated).

Expected output

[(1,"red"),(2,"blue"),(3,"white"),(4,"red")]
["red","blue"]

03 / Whole-list functions

Which functions never finish on an infinite list?

Some functions need the last cell of a list: length, sum, last, reverse, maximum, and sort from Data.List. Printing the whole list with print or show needs it too. An infinite list has no last cell, so these functions never finish. There is no error message and no result. The program keeps reading cells.

filter test xs keeps the elements of xs that pass the test. On an infinite list, filter can never know that no later element passes, so it keeps reading. head (filter (> 100) rows) finishes, because 102 passes and head needs one element. With no element that passes, the same expression never finishes.

null xs asks only whether the list has a first cell, so null [1 ..] is False at once. When you need to know that a list is not empty, ask null, not length.

Main.hs
rows :: [Int]
rows = iterate (+ 2) 6

-- length (take 5 rows) is 5
-- length rows never finishes
-- head (filter (> 100) rows) is 102
-- head (filter (> 100) (cycle [1, 2, 3])) never finishes

A function that needs the last cell never finishes on an infinite list, and filter never finishes when no later element passes.

Precisely: cells and elements

A cell and the element in it are separate pieces of work. length asks for every cell but never for the elements, so length (take 5 rows) leaves the numbers 8 to 14 as thunks. sum needs the elements as well.

Experiment

Ask for the length of a list with no end

length counts cells. take 5 gives it five, so it finishes. On rows itself, length asks for the next cell for ever, because every cell has a rest. The program gives no error and no output: it is still counting.

Why it matters Before you call length, sum, last, or reverse, check that the list has an end, or give it one with take.

Predict what line 4 asks of the list rows: its cells, its numbers, or both. Then change line 4 to print (length rows) and predict what prints.

main :: IO ()
main = do
  let rows = iterate (+ 2) 6 :: [Int]
  print (length (take 5 rows))
  print (sum (take 5 rows))
What to look for

Line 4 prints 5. At the stop on line 5, rows shows 6 : _ (unevaluated) : _ (unevaluated) : _ (unevaluated) : _ (unevaluated) : ....: length built five cells but did not compute the numbers 8 to 14 inside them. sum needs the numbers, and the run prints 50. After the edit, no stop comes after line 3, nothing prints, and the run ends with Stopped at the 20 second execution limit.

Expected output

5
50

04 / Sharing

Is a named list built again for each use?

A list bound to a name is built at most once, like any binding. The first code that reads the list builds its cells. A later reader finds those cells already built, and builds only the cells it needs beyond them. iterate widen 6 written twice is two separate lists, so the work happens twice.

A where binding is shared in the same way within one application of its function. Below, both takes read the cells of one colours list.

The cost is memory. Cells that are built stay in memory as long as code still to run uses the name. A few cells cost nothing; a million cells of a name that is used again later stay for the whole time between the two uses.

Main.hs
bracelet :: Int -> [String]
bracelet size = take size colours ++ take 1 colours
  where
    colours = cycle ["red", "blue", "white"]

-- bracelet 4 is ["red","blue","white","red","red"]
-- take 1 colours reads a cell that is already built

A named list is built at most once: later uses read the cells already built, and those cells stay in memory while the name is still used.

Coming from Java: A Java Stream can be read only once, and a second read throws IllegalStateException. A Haskell list bound to a name can be read any number of times, and each of its cells is built once.

Experiment

Count how often a named list builds a cell

rows is a binding, so GHC builds its cells at most once. The second take reads the three cells that are already there and asks for one more. iterate widen 6 written a second time is a new list, with no cells built yet.

Why it matters Give a list a name when two parts of the code read it: each cell is then built once. Remember that the cells stay while the name is used.

rows is used by two print lines. Predict how many times the stepper stops in widen. Then change line 8 to print (take 4 (iterate widen 6)) and predict again.

widen :: Int -> Int
widen beads = beads + 2

main :: IO ()
main = do
  let rows = iterate widen 6
  print (take 3 rows)
  print (take 4 rows)
What to look for

For the first print, the stepper stops in widen twice, with beads = 6 and beads = 8: the second and third cells. The output appears in parts, [6, and then [6,8,, because print asks for each number just before it writes it. On line 8, rows shows 6 : 8 : 10 : _ (unevaluated). For the second print, widen runs once, with beads = 10, and [6,8,10,12] prints. After the edit, the second print runs widen three times again, with beads = 6, 8, and 10, and prints the same line.

Expected output

[6,8,10]
[6,8,10,12]

05 / In production

What goes wrong with lazy lists in real programs?

Four mistakes with lazy lists are common in real Haskell code.

length on an infinite or a huge list. if length items > 0 never finishes on an infinite list, and on a long list it reads every cell to answer a question about one. Use null items. To ask for at least three, ask for three: length (take 3 items) == 3.

A list kept alive by a name. print (sum marks) followed by print (length marks) keeps every cell of marks in memory between the two lines, because the second line still uses the name. With ten million cells, that is a space leak. Compute both results in one walk, or do not give the list a name. Reachability and garbage collection, a later lesson, measures this memory.

filter where takeWhile was meant. filter (< 15) rows prints [6,8,10,12,14 and never closes the bracket. takeWhile (< 15) rows stops at 16. This is right only when the list is sorted so that, once one element fails, every later one fails too.

show on an infinite structure. A record that holds an infinite list and uses deriving Show prints for ever when you print or log the record. Show a part of the list instead, such as take 5.

Main.hs
-- never finishes: filter keeps looking after 14
filter (< 15) rows

-- stops at 16, the first row that fails
takeWhile (< 15) rows

Ask null for one cell, walk a big list once, stop a sorted infinite list with takeWhile, and show only part of an infinite list.

Experiment

Stop a sorted list where the test first fails

takeWhile stops at the first element that fails its test. filter checks every element, and on a list with no end it never reaches a last one. Only you know that rows grows, so that no row after 16 can pass.

Why it matters On a sorted list without an end, use takeWhile for a test that stays failed once it fails. filter is for finite lists.

rows grows by 2 each time. Predict what prints. Then change takeWhile to filter and predict whether the program finishes.

main :: IO ()
main = do
  let rows = iterate (+ 2) 6 :: [Int]
  print (takeWhile (< 15) rows)
What to look for

The run prints [6,8,10,12,14] and finishes. takeWhile reached 16, which fails (< 15), and stopped there. After the edit, the output is [6,8,10,12,14, with no closing bracket. filter has found every row below 15, but it keeps reading rows to look for another. The run ends with Stopped at the 20 second execution limit.

Expected output

[6,8,10,12,14]

06 / Try it

Can you predict which cells are built?

Find which cells each line asks for before you predict.

Main.hs
main :: IO ()
main = do
  let sizes = cycle [4, 6] :: [Int]
      numbered = zip [1 ..] (take 3 sizes) :: [(Int, Int)]
      total = sum sizes
  print numbered
  print (takeWhile (< 6) sizes)
  print (null sizes)

What does the program print, and does it finish?

Reveal the reasoning

[(1,4),(2,6),(3,4)], then [4], then False, and the program finishes. take 3 sizes has three cells, so zip stops after three pairs, although [1 ..] has no end. takeWhile (< 6) stops at 6, the second element, and never reads further. null asks only whether sizes has a first cell. total = sum sizes would never finish, but no line needs total, so it is never computed.

Practice tickets

  1. HS-501 · The round list never appears for a repeating crochet pattern → Free
  2. HS-502 · Live juggling sessions show no running totals → Sign in · Pro
  3. HS-503 · The kite club calendar freezes on some holiday periods → Sign in · Pro

Keep exploring

Reachability and garbage collection →

Primary references