liveBook · Manning

Tail recursion

As explained in Clojure: The Essential Reference

Tail recursion is recursion in which the recursive call is the final operation of a computation, allowing control to continue without retaining pending caller work.

Tail recursion makes a recursive call the final operation of its current scope: no caller work remains after that call. In Clojure, `recur` turns this pattern into iteration without consuming a new stack frame on each step. An exit condition still determines when to return a result. section 3.4 section 3.4.1

Carry the result forward

```clojure (defn fib [n] (loop [a 1 b 0 cnt n] (if (zero? cnt) b (recur (+ a b) a (dec cnt))))) (map fib (range 10)) ;; (0 1 1 2 3 5 8 13 21 34) ``` The loop bindings hold the current state. Each `recur` supplies their next values; when `cnt` reaches zero, `b` is returned. Unlike a formulation that adds the results of two recursive `fib` calls, this one has no addition waiting after its recursive step. section 3.4.1

The `recur` contract is deliberately strict:

  • It jumps to the innermost `loop` or function recursion target. With `loop`, it updates local bindings without changing the function’s arguments. section 3.4.1
  • It must be in tail position and supply one value per target binding or parameter. The compiler rejects a `recur` that is not in tail position. section 3.4.1

The same bottom-up idea can guide collection algorithms: accumulate the powerset from the empty set, adding each new element to existing subsets, rather than building the answer while recursive calls return. `reduce` expresses that approach without an explicit `recur`. For mutually recursive functions, `trampoline` instead repeatedly invokes returned functions to avoid growing the call stack. section 13.1 section 2.4.3

How Clojure: The Essential Reference covers it

How deep it goes
Defines tail position, specifies `recur` targets and arity, and works through Fibonacci and performance comparisons.
Examples
Uses Fibonacci, a square-collecting loop, and a powerset to contrast recursive calls with accumulating iteration.
What it assumes
Builds on Clojure functions and conditionals while defining recursion targets, loop bindings, and `recur` argument requirements.
What you can do after
Build it from scratch with `loop` and `recur`, carrying state forward while keeping recursive calls in tail position.
How it connects
Links tail recursion to `loop`, `recur`, `trampoline`, and bottom-up accumulation with `reduce`.

Also explained in

Compare how 4 books explain tail recursion →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Clojure: The Essential Reference. The section links point to the parts of the book it draws on.