Skip to content

Haskell Lazy Evaluation Guide — Thunks, Strictness, and Performance

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Haskell Lazy Evaluation Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Haskell lazy evaluation (non-strict) wraps expressions in thunks that are evaluated only when their value is demanded -- with seq for selective strictness, BangPatterns for strict fields, and foldl' for space-efficient reduction.

How Laziness Works

-- Expression is not evaluated until needed
let x = 1 + 2 * 3  -- thunk created
let y = x + 5      -- another thunk
print y             -- both thunks evaluated now

-- Infinite lists work because of laziness
ones = 1 : ones
take 5 ones  -- [1,1,1,1,1] (only 5 elements created)

seq and Strict Evaluation

-- seq forces evaluation of first argument, returns second
-- seq a b evaluates a to WHNF, then returns b

strictSum :: Int -> Int -> Int
strictSum x y = x `seq` y `seq` x + y

-- Using $!
-- ($!) is strict application
f x = x * 2
f $! 3 + 4  -- evaluates 3+4 before calling f

BangPatterns

{-# LANGUAGE BangPatterns #-}

-- Strict function arguments
sumStrict :: [Int] -> Int
sumStrict = go 0
  where
    go !acc []     = acc
    go !acc (x:xs) = go (acc + x) xs

-- Strict record fields
data Point = Point
    { x :: !Double  -- always strict
    , y :: !Double
    }

-- Without bang, fields are lazy
data LazyPoint = LazyPoint
    { lx :: Double  -- may be thunk
    , ly :: Double
    }

Space Leaks

-- foldl causes space leak (builds thunks)
badSum :: [Int] -> Int
badSum = foldl (+) 0
-- Thunks: ((((0+1)+2)+3)+4) builds up

-- foldl' is strict (no thunks)
import Data.List (foldl')
goodSum :: [Int] -> Int
goodSum = foldl' (+) 0
-- Evaluates: 1 -> 3 -> 6 -> 10

-- Difference:
-- For [1..1000000], foldl overflows stack
-- foldl' runs in constant space

Common Mistakes

1. Space leaks with foldl

Always use foldl' from Data.List for strict accumulation. foldl causes thunk buildup on large lists.

2. Overusing seq

Adding seq everywhere doesn't help. Use it only where laziness causes space leaks or unexpected thunk retention.

3. Infinite loops in strict contexts

let x = 1:x works lazily. But print $ length x (strict) will loop forever. Be careful with infinite structures.

Practice Questions

1. What is a thunk? A deferred computation -- an expression that hasn't been evaluated yet. Lazy evaluation creates thunks.

2. What does seq do? seq a b evaluates a to Weak Head Normal Form, then returns b. Used to force evaluation at specific points.

3. When should you use foldl' instead of foldl? Always. foldl' (strict) runs in constant space. foldl (lazy) builds thunks that overflow the stack.

FAQ

{{< faq question="What is Weak Head Normal Form (WHNF)?" >}} An expression evaluated to its outermost constructor or lambda. Not all subexpressions are evaluated. seq evaluates to WHNF. {{< /faq >}}

{{< faq question="Can I opt out of laziness?" >}} Yes, partially. Use seq, BangPatterns, strict data types, or the Strict extension for module-wide strictness. {{< /faq >}}

{{< faq question="How does laziness help performance?" >}} It avoids unnecessary computation, enables modular code (separate generator from consumer), and allows infinite data structures. {{< /faq >}}

What's Next

Now learn about error handling.

Topic Description Link
Error Handling Exception Handling {{< ref "18-error-handling" >}}
Testing Testing Haskell code {{< ref "19-tests" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro