Skip to content
Recursion and Pattern-Matching Functions
step 1/7

Reading — step 1 of 7

Learn

~2 min readRecursion, Modules, and Maps

Erlang has no loops. Recursion is THE iteration. Combined with multi-clause functions and pattern matching, it expresses what other languages would write as for loops. Programming Erlang treats this as foundational.

Multi-clause functions

A function definition has CLAUSES separated by ;, ending with .:

factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).
  • Clause 1 matches factorial(0) and returns 1
  • Clause 2 matches any positive N and recurses
  • Erlang tries clauses TOP-DOWN; first match wins
  • Final clause ends with ., others with ;

No separate if/case needed — pattern matching IS the dispatch.

Recursive list operations

The canonical Erlang style:

length([]) -> 0;
length([_ | T]) -> 1 + length(T).

sum([]) -> 0;
sum([H | T]) -> H + sum(T).

The [H | T] pattern destructures a list into HEAD (first element) and TAIL (rest). Recursion walks down the tail; the empty-list base case stops it.

Tail recursion — the accumulator pattern

Naive recursion grows the stack. Tail recursion uses an accumulator:

sum(L) -> sum(L, 0).

sum([], Acc) -> Acc;
sum([H | T], Acc) -> sum(T, Acc + H).    %% LAST call — tail position

The recursive call is the LAST thing the function does — Erlang's compiler turns this into a loop with no stack growth. Essential for processing large lists.

Convention: a public function (sum/1) calls a private helper (sum/2) that takes the accumulator.

Guards on recursion

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

Guards (when CONDITION) refine pattern matches. They run pure expressions only — no arbitrary function calls. Common guards: comparisons, type tests (is_atom, is_list, is_integer).

When to recurse vs use lists module

For most everyday list processing, prefer the lists module:

lists:sum([1, 2, 3])             %% 6
lists:foldl(fun erlang:'+'/2, 0, [1, 2, 3])    %% 6
lists:map(fun(X) -> X * 2 end, [1, 2, 3])
lists:filter(fun(X) -> X > 2 end, [1, 2, 3])
lists:reverse([1, 2, 3])

Write hand-rolled recursion when:

  • The shape doesn't fit standard library functions
  • You're learning the language
  • Performance demands a custom traversal

Common mistakes

  • Forgetting ; between clauses, . at the end — syntax errors. Each non-final clause ends with ;. The last with ..
  • Non-tail recursion on huge lists — stack overflow. Convert to accumulator pattern.
  • Reversing accumulators — tail-recursive functions building a list often produce it reversed. Final step: lists:reverse(Acc).
  • Function clause order — Erlang tries top-down; put more specific patterns first.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…