Security
Demand Management
Business Strategy
Workforce Optimization
Supply and Demand

Guards and demand

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

In Haskell, guards are part of how a function chooses between alternatives, while demand describes how much of an expression must actually be evaluated to continue execution. Those two ideas meet in an important way: a guard introduces just enough evaluation to decide whether that branch should be taken, but laziness still delays everything else.

What Guards Do

A guarded definition lets you write several conditional branches for one function.

haskell
signDescription :: Int -> String
signDescription n
| n < 0     = "negative" | n == 0    = "zero" | otherwise = "positive" ``` The guards are checked from top to bottom. The first one that evaluates to `True` determines the result. That much is straightforward. The subtler part is evaluation demand. To test `n < 0`, Haskell must evaluate `n` enough to compare it with `0`. If that succeeds and the guard is `True`, later guards are never examined. ## Demand in a Lazy Language Haskell is non-strict, which means expressions are not evaluated until something demands their value. Guards create one kind of demand because the runtime must know whether a boolean condition is `True` or `False`. This means a guarded function can force only part of its input. ```haskell firstIsZero :: [Int] -> String firstIsZero xs | head xs == 0 = "starts with zero" | otherwise    = "starts with non-zero" ``` To evaluate the first guard, Haskell must evaluate enough of `xs` to compute `head xs`. It does not need the whole list. Only the part demanded by the guard gets forced. ## Guards Do Not Make Everything Strict A common misconception is that using guards somehow makes the whole function eager. It does not. Only the expressions needed to test the guards and produce the selected branch are evaluated. ```haskell choose :: Int -> Int -> Int choose x y | x > 0     = x | otherwise = y ``` If `x` is positive, `y` is never evaluated at all. That is a normal consequence of laziness. The `otherwise` branch exists logically, but it is not demanded. This behavior is especially important when one branch is expensive, infinite, or undefined. ```haskell safeChoice :: Int safeChoice = choose 1 (error "never evaluated") ``` `safeChoice` evaluates successfully because the second argument is never demanded. ## Pattern Matching, Guards, and Order Pattern matching happens before guards attached to that pattern. That gives you a two-stage process: 1. choose the matching pattern 2. evaluate guards for that pattern in order ```haskell describe :: [Int] -> String describe [] = "empty" describe (x:xs) | x == 0    = "starts with zero" | otherwise = "non-empty, first not zero" ``` If the list is empty, the guarded second equation is never considered. If the list is non-empty, then the guard demands enough of `x` to check whether it is zero. This ordering matters when reasoning about bottoms, undefined values, or partial data structures. ## Guards and Bottom Values Because guards force their condition, a guard can trigger failure even if a later branch might look safe at first glance. ```haskell example :: Int -> String example n | n > 0     = "positive" | otherwise = "not positive" ``` If `n` is undefined, the program cannot decide whether `n > 0`, so evaluation fails before either string can be returned. The guard itself introduced the demand. That is a useful mental model: guards are lazy with respect to the overall program, but strict with respect to the boolean condition they must decide. ## Why This Matters Understanding guard demand helps you reason about performance and correctness. It explains why some code touches only the head of a list, why some branches avoid expensive work completely, and why undefined values sometimes crash sooner than expected. In a lazy language,what is evaluated?is often a more important question thanwhat code exists?Guards are one of the clearest places where that difference becomes visible. ## Common Pitfalls The biggest pitfall is assuming a guard evaluates the whole input. It evaluates only what the condition needs. Another mistake is forgetting the top-to-bottom order. Once a guard succeeds, later guards are ignored entirely. Developers also confuse pattern failure with guard failure. Pattern matching decides whether an equation applies at all, while guards refine the choice after the pattern already matched. Finally, do not assume laziness protects you from every undefined value. If a guard needs part of an expression, that part is demanded immediately. ## Summary - Guards choose between branches by testing boolean conditions in order. - In Haskell, those conditions create demand only for the data needed to evaluate them. - Guards do not make a function fully strict; unused branches can remain unevaluated. - Pattern matching happens before guards, and the first successful guard wins. - Understanding guard demand is essential for reasoning about laziness, partial values, and performance.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.