Skip to content
ALB Wiki

Lambda calculus

This is an old revision of this page — revision 7 (2026-10-20 03:27:00 UTC). It may differ from the current revision.

The lambda calculus (λ-calculus) is the smallest programming language that can compute anything — and one of the largest ideas in mathematics. It has exactly three kinds of expression and one rule of computation. It contains no numbers, no strings, no loops, no if, no names for anything — and it is exactly as powerful as every computer ever built. Alan Turing proved his universal machine equivalent to it; LISP grew directly out of it; every anonymous function, closure, and map you have ever used is its grandchild.

The story

A mathematics in crisis

In 1900, David Hilbert stood before the world's mathematicians and set the program for the century: put all of mathematics on complete, consistent, mechanical foundations. The dream had teeth — Whitehead and Russell's Principia Mathematica had already shown that vast tracts of math could be derived from pure logic, famously taking hundreds of pages to prove that 1 + 1 = 2. By 1928 Hilbert had sharpened the question into the Entscheidungsproblem: does there exist a procedure that can decide, in principle, whether any mathematical statement is provable?

Then the foundations cracked. Gödel's incompleteness theorems (1931) proved no such system could be both consistent and complete. What remained was the sharpest question left standing: what does "a procedure" even mean? Nobody could answer it — because nobody had a precise definition of computation itself.

Princeton, 1932–1936

Alonzo Church — a young Princeton logician of legendary precision, famous for lecturing in a monotone while erasing the blackboard with one hand and writing with the other — had been building a complete logical system of his own since 1932. At its heart was a notation for functions: where everyone else wrote "the function f(x)," Church wrote λx.f(x) — the function itself, unmarried from any name.

There was a problem. In 1935, his own graduate students — Stephen Kleene and J. Barkley Rosser — proved his grand logical system inconsistent. The logic died. But a fragment of it — the pure calculus of λ-functions — survived autopsy perfectly intact: consistent, elegant, and quietly Turing-powerful.

In April 1936 Church published "An Unsolvable Problem of Elementary Number Theory" and answered Hilbert's Entscheidungsproblem: no. There is no mechanical procedure that decides all mathematical truth — because he could now define "mechanical procedure" precisely, as λ-definability, and exhibit a problem beyond it.

Two definitions, one machine

Months later, a young Englishman published "On Computable Numbers" with a completely different answer-machine: an imaginary device scanning a tape. Alan Turing then did the extraordinary thing — he proved his machines and Church's λ-calculus exactly equivalent in power, packed his bags, and sailed to Princeton to take his PhD under Church himself.

The Church–Turing thesis — that "effectively calculable," "λ-definable," and "Turing-machine computable" are the same boundary, seen from three directions — remains the deepest unprovable axiom computing rests on. Every language since, FORTRAN to Haskell to AdaLogic BASIC, is a costume over one of those two skeletons.

After the war — the idea grows a body

The λ-calculus sat in logic journals for twenty years while the electronic computers were being born from von Neumann's architecture. Then, in 1958, John McCarthy needed a notation for reasoning about programs and reached for Church's: LISP carried lambda literally into its syntax, and the idea entered living code. From there the family tree never stopped branching:

From lambda to language
λ-calculus1936LISP1958Scheme1975ML1973Haskell1990every modernlambda
  • 1958 — LISP: lambda becomes a working keyword; functions become data
  • 1970s — Scheme purifies it; ML marries it to a real type system
  • 1970s–80s — the Curry–Howard correspondence is noticed: types are propositions, programs are proofs — the bridge to modern proof assistants
  • 1990 — Haskell: a mainstream language that is essentially typed λ-calculus wearing clothes
  • today — lambda in Python, => in JavaScript, closures in everything: the anonymous function is the most-borrowed idea in all of language design

The whole language in three rules

Here it is — genuinely all of it:

  1. A variable: x — a name that stands for an argument
  2. An abstraction: λx.M — "a function that takes x and gives M" (the λ binds x the way a parameter name is bound in any function)
  3. An application: M N — "feed N to M"

Every λ-term is one of those three. There are no numbers, no booleans, no strings, no built-in operations, no mutation, no printing. And yet: it computes everything.

The one rule of computation

The engine is β-reduction — application is substitution:

(λx. M) N   →   M with every free x replaced by N

That's the entire interpreter:

(λx.x) hello      →  hello                     -- identity, applied
(λx.λy.x) a b     →  (λy.a) b  →  a            -- a two-argument selector
(λf.λx.f(f x)) g 5 →  λx.g(g x) applied to 5  →  g(g(5))   -- "twice"

Two companion laws round out the system: α-conversion (bound names are arbitrary) and η-conversion (λx.f x is just f — a wrapper that does nothing is nothing).

Order doesn't matter — mostly

A term can have many redexes — which do you reduce first? The Church–Rosser theorem answers beautifully: it doesn't matter. Reduction paths always rejoin — the calculus is confluent. If a term has a normal form (a fully-reduced answer), every road leads there.

Mostly, because some terms have no answer:

Building a world from nothing

The magic trick: with only functions, you can manufacture everything a language needs. These are the Church encodings — data made of pure behavior.

Booleans — truth as choice

TRUE   =  λx.λy. x      -- "take two things, keep the first"
FALSE  =  λx.λy. y      -- "take two things, keep the second"
IF     =  λb.λt.λe. b t e   -- just *apply* the boolean to the branches!

IF TRUE a b reduces to a because TRUE is the function that picks its first argument. The boolean doesn't describe a choice — it is the choice. That's the calculus's whole aesthetic: a value is what it does.

Numbers — counting as iterating

Church numerals: the number n is "the function that applies f to x, n times."

0  =  λf.λx. x            -- apply f zero times
1  =  λf.λx. f x          -- apply once
2  =  λf.λx. f (f x)      -- twice
3  =  λf.λx. f (f (f x))  -- three times

Arithmetic falls out as function composition:

SUCC  =  λn.λf.λx. f (n f x)        -- "one more application"
PLUS  =  λm.λn.λf.λx. m f (n f x)   -- "apply f m times, then n times"
MULT  =  λm.λn. m (PLUS n) ZERO-ish -- i.e. (PLUS n) repeated m times

Check PLUS 1 2 mentally: λf.λx. 1 f (2 f x) = apply f once after twice = three times = 3. Arithmetic by counting applications. It works.

Pairs and everything else

PAIR   =  λa.λb.λp. p a b     -- a pair is "a function that takes a chooser"
FIRST  =  λp. p TRUE          -- feed it the first-picker
SECOND =  λp. p FALSE         -- feed it the second-picker

From pairs: lists, trees, records — the entire data-type zoo, built from functions that behave like containers.

Recursion without a name — the Y combinator

Nothing in λ-land can refer to itself by name — there are no names. Yet factorial needs recursion. The fix is the most famous one-liner in the field:

Y  =  λf. (λx. f (x x)) (λx. f (x x))

Y g reduces to g (Y g) — it hands any function its own fixpoint, recursion bootstrapped from pure self-application. Haskell Curry found it; Turing found a twin (Θ). Every letrec, every recursive PROCEDURE, every language's answer to "how does a function call itself" is Y wearing a friendlier mask.

The cheat sheet

You wantλ-termWhat it is
identityλx.xreturn the argument
TRUEλx.λy.xpick first
FALSEλx.λy.ypick second
NOTλb.λx.λy. b y xpick the other
ANDλa.λb. a b FALSEif a then b else false
pairλa.λb.λp. p a ba chooser-shaped box
nλf.λx. fⁿ x"do f n times"
successorλn.λf.λx. f (n f x)one more application
loops forever(λx.x x)(λx.x x)Ω — no normal form
recursionλf.(λx.f(x x))(λx.f(x x))Y — the fixpoint

Then types walk in

In 1940, Church published the simply typed λ-calculus — every term tagged with a type, ill-typed terms forbidden. Two consequences shaped the next eighty years:

  • Strong normalization — in the simply-typed calculus, every well-typed term halts. Ω can't be typed (you can't give x x a consistent type), so self-eating is legislated out of existence. Types as a proof of good behavior.
  • Curry–Howard — noticed gradually (Curry 1958, Howard 1969): types are logical propositions; programs are proofs. A → B is implication; a function of that type is a proof that "A implies B." This isomorphism — computation is deduction — is the foundation of modern proof assistants (Coq, Agda, Lean) and the reason "propositions as types" sits at the deepest point where programming and mathematics touch.

From there the tower kept rising: System F (Girard, Reynolds — polymorphism, the ancestor of generics everywhere), System Fω, dependent types — each a λ-calculus with a more expressive type language bolted on.

Why a working programmer should care

  • Every anonymous function — Python lambda, JavaScript =>, C++ [&] — is Church's notation in street clothes.
  • Every closure — a function carrying captured variables — is λ-abstraction plus a scope rule.
  • map, filter, reduce — functions as arguments — is application, unabstracted from its ivory tower.
  • Functional programming is simply the bet that β-reduction is a better programming model than assignment.
  • And the languages: LISP begot Scheme and the whole symbolic tradition; the typed branch became ML → Haskell → the type systems eating modern languages (Rust's ownership, TypeScript's shapes) — all of it refinement of Church's two fragments, typed and untyped.

Try it yourself

  1. Reduce (λx.x) (λy.y) — what do you get, and what does it mean?
  2. Show IF FALSE a b → b, step by step.
  3. What does (λf.λx.f(f x)) SUCC 0 evaluate to, and why is that 2?
  4. Reduce Ω once — convince yourself it loops.
  5. (Expert) Sketch why NOT (NOT TRUE) returns TRUE purely by substitution.

See also

References

  1. Church, Alonzo. "An Unsolvable Problem of Elementary Number Theory." American Journal of Mathematics 58(2), 1936.
  2. Church, Alonzo. The Calculi of Lambda-Conversion (Princeton UP, 1941).
  3. Barendregt, Henk. The Lambda Calculus: Its Syntax and Semantics (North-Holland, 1984).
Categories: History · Languages · Mathematics · Theory
This page was last edited 2026-10-20 03:27:00 UTC by ALB Team. What links here