Laziness and infinite lists

Contents
Thunks and evaluation
Infinite structures
Short-circuiting
When laziness bites

Thunks and evaluation

An expression is not evaluated where it is written. It becomes a thunk, a description of work, and is forced only when a consumer needs the value:

x = 1 + 2 -- not computed yet y = x * 10 -- still a thunk y -- 30, now both are forced

A thunk is evaluated at most once: the result is stored, so the second use is free. That sharing is why laziness does not repeat work.

Infinite structures

Because nothing is computed until it is needed, an infinite list is a value like any other. The consumer decides how much exists:

ones = 1 : ones naturals = [1..] fibs = 0 : 1 : zipWith (+) fibs (tail fibs) take 5 ones -- [1,1,1,1,1] take 8 fibs -- [0,1,1,2,3,5,8,13]

The Fibonacci line defines the list in terms of itself, and it works because each element depends only on earlier ones. This is the most quoted example of lazy programming for a reason.

Short-circuiting

Boolean operators stop as soon as the answer is known, and folds over an infinite list stop when the combining function ignores the rest:

False && undefined -- False, the second operand never runs True || undefined -- True any (> 100) [1..] -- True, stops at 101 elem 3 [1..] -- True, stops at 3

This is why foldr can work on an infinite list while foldl cannot: the right fold sees the first element before the rest of the structure exists.

When laziness bites

The two surprises are memory and order of effects. A chain of lazy additions builds a tower of thunks, and a lazy read of a file may keep the handle open longer than expected:

-- builds a million thunks before the first result foldl (+) 0 [1..1000000] -- strict version, constant memory foldl' (+) 0 [1..1000000]

The next lesson turns that warning into a set of tools: strict folds, bang patterns, and the profiler that finds the leak.

Next: Strictness and space leaks

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: