Recursion
- A function defined in terms of itselfnot yet tested
- Base case, recursive case, and trustnot yet tested
- Trees, divide-and-conquer, and parsingnot yet tested
- Stack overflow and exponential redundant worknot yet tested
In 1936, three different mathematicians — Alonzo Church in Princeton, Alan Turing in Cambridge, Stephen Kleene working with Church — independently formalised what it means to compute. Church's lambda calculus, Turing's machines, the general recursive functions of Gödel, Herbrand, and Kleene — three formalisms that turned out, somewhat startlingly, to be exactly equivalent in expressive power. The result became known as the Church-Turing thesis: any function intuitively computable can be expressed in any of these systems. Of the three, the one closest to how working programmers actually think is recursion — the trick of defining a function in terms of itself. The factorial: n! = n × (n-1)!, with the base case 0! = 1. It feels strange the first time you see it work — the function calls itself, and somehow the result is finite, correct, and useful.
Recursion works when a problem can be decomposed into smaller instances of the same problem. The recipe is always the same: (1) base case — the smallest instance, where the answer is given directly; (2) recursive case — express the answer to the current instance in terms of the answer to a strictly smaller instance; (3) trust the recursion — assume the function works on smaller inputs and call it. The mathematics underneath is induction: a recursive definition is correct if the base case is correct and the recursive case correctly composes the smaller-input answer. Where recursion shines: tree and graph traversal, divide-and-conquer algorithms (Mergesort, Quicksort, FFT), parsing (recursive-descent parsers fall straight out of recursive grammars), functional programming idioms (map, filter, fold), and language design itself (Lisp, the second-oldest programming language, treats programs and data as recursively structured lists). Where recursion fails or needs care: deep recursion overflows the call stack (most languages limit stack depth to a few thousand frames; very deep recursions need tail-call optimization or explicit iteration); naive recursion can do exponential redundant work (Fibonacci computed naively does O(2^n) calls because F(n-1) and F(n-2) re-compute their subtrees; memoization or dynamic programming converts this to O(n)). Recursion's deeper significance: it is the simplest concrete demonstration that self-reference in computation is not paradoxical but constructive. The same trick that, applied carelessly, produces Russell's paradox in set theory and Gödel's incompleteness theorem produces, applied carefully, useful programs. The dividing line — between productive self-reference and paradoxical self-reference — is roughly whether the recursive case strictly reduces the problem.