100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Guards and Clauses

How Erlang guards extend pattern matching with safe boolean conditions to express multi-way branching without if/else.

Core ConceptsBeginner7 min readJul 10, 2026
Analogies

Pattern Matching and Multiple Clauses

Pattern matching alone can select a clause based on the shape of an argument, such as matching {ok, Value} versus {error, Reason}, but many decisions depend on properties of a value rather than its shape — is this number positive, is this list empty, is this argument actually an integer at all. Guards extend pattern matching with a boolean test attached to a clause head using the when keyword, letting classify(N) when N > 0 -> positive fire only when both the pattern and the condition hold.

🏏

Cricket analogy: Pattern matching is like recognizing a bowler is left-arm versus right-arm just by their stance, but a guard is like also checking their economy rate is under 6 before selecting them for the death overs — shape plus condition together decide.

What Are Guards?

A guard is restricted to a small, deliberately limited set of built-in predicates and operators — comparisons like >, <, =:=, type tests like is_integer/1, is_atom/1, is_list/1, and simple arithmetic — precisely because guards must never fail with an exception or perform side effects; if a guard expression would raise an error, Erlang simply treats the guard as false and moves on to the next clause rather than crashing the process. This restriction is what lets the compiler and runtime evaluate guards safely and predictably no matter what garbage gets passed in.

🏏

Cricket analogy: It's like a third umpire's review that only checks a fixed set of camera angles and never asks the bowler's opinion — guards only use approved, side-effect-free checks like is_integer/1, never arbitrary calls that might error out.

erlang
-module(number_utils).
-export([classify/1, is_valid_age/1]).

classify(N) when N > 0 -> positive;
classify(N) when N < 0 -> negative;
classify(0) -> zero.

is_valid_age(Age) when is_integer(Age), Age >= 0, Age =< 150 ->
    true;
is_valid_age(_) ->
    false.

Guards may use andalso and orelse directly as well as commas and semicolons; is_integer(Age) andalso Age >= 0 inside a single clause behaves identically to writing is_integer(Age), Age >= 0, and reads more clearly to many Erlang programmers once a guard has several parts.

Guard Sequences and Multiple Conditions

Within a single guard, commas act as andalso — every comma-separated condition must be true — while semicolons act as orelse across whole guard sequences, so is_valid(X) when is_integer(X), X > 0; is_valid(X) when X =:= zero -> true reads as '(is_integer(X) and X>0) or (X=:=zero)'. Stacking multiple guarded clauses for the same function name, each with its own when condition, is how Erlang expresses multi-way branching without any if/else syntax at all.

🏏

Cricket analogy: It's like a fielding restriction rule stacking two conditions with AND — 'inside the circle AND within the first six overs' — while a semicolon between guard clauses is like an OR rule, such as 'powerplay overs OR final over' triggering a different field setting.

Common Guard Predicates and Pitfalls

A frequent beginner mistake is trying to call an ordinary user-defined function inside a guard — guards only permit a whitelisted set of BIFs, so classify(X) when my_helper(X) -> ... simply won't compile, and the fix is to compute the condition inside the function body instead, often via a case expression. If none of a function's clauses match — no pattern and guard combination succeeds — Erlang raises a function_clause error, which is why it's common practice to end a guard chain with a catch-all clause when full coverage is required.

🏏

Cricket analogy: It's like the third umpire being barred from calling the team's own analyst mid-review to ask an opinion — only pre-approved camera checks count, just as guards can't call my_helper(X); and if no clause matches at all, it's like the match ending with no result declared, the equivalent of a function_clause error.

A guard that raises an exception is simply treated as false, not as a crash — so is_valid_age(Age) when Age >= 0, Age =< 150 -> true given an atom instead of a number silently fails the guard and falls through to the next clause rather than halting the program, which can hide bugs if you're not expecting it.

  • Guards attach a boolean condition to a function clause using the when keyword.
  • Only a restricted set of BIFs and operators are allowed in guards: comparisons, type tests like is_integer/1, and simple arithmetic.
  • A guard can never raise an exception; an invalid guard expression simply evaluates to false.
  • Commas inside a guard behave like andalso; semicolons between guard sequences behave like orelse.
  • Multiple guarded clauses let you express multi-way branching without if/else syntax.
  • User-defined functions cannot be called inside a guard expression.
  • If no clause's pattern and guard combination matches, Erlang raises a function_clause error.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ErlangStudyNotes#GuardsAndClauses#Guards#Clauses#Pattern#Matching#StudyNotes#SkillVeris#ExamPrep