wiki

Tail call

also: tail call optimization, tco, tail recursion, tail position, tailcall

A call whose result is returned directly, with nothing left for the caller to do. The caller's frame can be reused for it, so a loop written as tail recursion runs in constant stack space. OCaml guarantees this, and the [@tailcall] attribute makes the compiler warn when a call marked with it is not one.

A call is in tail position when it is the last thing its function does. In x + sum xs the addition still has to happen after sum xs returns, so the frame holding x has to stay; moving the addition into an accumulator puts the recursive call in tail position.

The same sum twice, over ten million elements.

let rec sum = function [] -> 0 | x :: xs -> x + sum xs
let rec sum_acc acc = function [] -> acc | x :: xs -> sum_acc (acc + x) xs

ocamlopt 5.5.1 with an 8 MB stack.

sum_acc 0 xs -> 49999995000000
sum xs -> exception Stack_overflow

Whether a call is a tail call is easy to get wrong by reading, since a try around it or an argument evaluated after it takes it out of tail position. The attribute checks it:

Marking the non-tail call in sum.

let rec sum = function [] -> 0 | x :: xs -> x + (sum [@tailcall]) xs
Warning 51 [wrong-tailcall-expectation]: expected tailcall

Tail calls make CPS practical, since in CPS every call is a tail call. They do not make a program's memory use constant on their own: a tail-recursive loop that builds a growing accumulator, or in a lazy language a growing chain of thunks, still grows.

see also

referenced by

further reading

  • G. L. Steele Jr., “Debunking the ‘expensive procedure call’ myth, or, procedure call implementations considered harmful, or, LAMBDA: the ultimate GOTO”, ACM Annual Conference (1977).
  • W. D. Clinger, “Proper tail recursion and space efficiency”, PLDI (1998).