Foundations of mathematics
Type theory
A foundation for mathematics and computation where every term carries a type, used in proof assistants.
IntuitionEvery value has a label
In most programming languages, is an `int` and `"hello"` is a `string` — the compiler rejects `3 + "hello"` before the program ever runs. Type theory turns this everyday idea into a foundation for all of mathematics: instead of starting from sets and membership (), start from terms and types (, " has type "). Remarkably, once types are rich enough, a type can encode a mathematical proposition, and a term of that type becomes a proof — programs and proofs become the same kind of object.
UndergraduateSimply typed lambda calculus
Definition: Typing judgment
A typing judgment says "in context (a list of variable-type pairs ), term has type ". The core rules: a variable has whatever type its context assigns it; an abstraction has type whenever has type in the context extended with ; and application has type whenever and .
Martin-Löf dependent type theory generalizes this further: the result type of a function can depend on the value of its argument. The **-type** (dependent function type) collects functions sending each to a term of type — when doesn't mention , this collapses to the ordinary . Dually, the **-type** (dependent pair type) collects pairs with and — a pair whose second component's type depends on the first; when doesn't mention , this collapses to the ordinary product .
| Type constructor | Non-dependent special case | Curry–Howard reading |
|---|---|---|
| (when is constant) | ||
| (when is constant) | ||
| Univalence: | — | Equivalent types are identified |
If in the simply typed lambda calculus, then is strongly normalizing: every sequence of -reductions starting from terminates after finitely many steps.
Why is it true?
This is why simply typed lambda calculus, despite having function application and recursion-like nesting, is not Turing-complete — no well-typed term can loop forever, which is exactly what makes type-checkers in this fragment always terminate.
Proof
(Tait's reducibility method.) Define, by induction on the structure of types, a set of "reducible terms" for every type : for a base type , let be the set of all strongly normalizing terms; for a function type, let .
One first proves, by induction on , three technical properties together: (CR1) every term in is strongly normalizing; (CR2) is closed under -reduction (if and then ); (CR3) any "neutral" term (a variable or application, not an abstraction) all of whose one-step reducts already lie in must itself lie in . The base case is immediate from the definition of ; the function-type case unfolds the definition of and uses the induction hypothesis on the smaller types and .
Next, one shows every well-typed term is reducible under any substitution of reducible terms for its free variables: by induction on the typing derivation of , if assigns each variable in a term , then . The variable case is immediate. The application case follows directly from the definition of . The abstraction case needs the most care: for applied to any , the term reduces in one -step to , which is in by the induction hypothesis (applied to the extended substitution); property (CR3) — closure under "expansion" for terms whose reducts are all already reducible — then places itself in , so by definition.
Finally, apply this to the identity substitution: every variable is itself neutral with no reducts at all, so it is vacuously in by (CR3). So for any closed derivation , taking to map each to itself gives . By (CR1), , so : every well-typed term is strongly normalizing.
AdvancedThe Curry–Howard correspondence
Read as "if then ", read as " and ", and a proof of a proposition becomes a well-typed program of the matching type — this is propositions-as-types, proofs-as-programs. Checking a proof reduces to type-checking a term; constructing a proof reduces to writing a program of the right type.
Let be a propositional formula built from atoms, , and , and let be the simple type obtained by translating atoms to base types, to product , and to function type . Then is provable in intuitionistic propositional logic if and only if is inhabited: there is a closed term with .
Why is it true?
This is the precise statement behind "a proof assistant is just a type-checker": Coq, Agda, and Lean accept a machine-checked proof of a theorem exactly when they accept a term of the corresponding type, and type-checking (unlike theorem-proving in general) is a small, fast, mechanical, and highly trustworthy algorithm.
Proof
(Proofs become programs.) By induction on a natural-deduction derivation of . If the last rule is -introduction, deriving from a subproof of under hypothesis : by the induction hypothesis that subproof translates to a term with (the hypothesis becomes a free variable ); then is closed and has type . If the last rule is -elimination (modus ponens) from and : by the induction hypothesis we have terms and ; then . The -introduction and -elimination rules translate symmetrically to pairing and the two projections. Every rule of the proof system has a matching term-building rule, so induction on the derivation produces a well-typed closed term of type .
(Programs become proofs.) Conversely, by induction on a typing derivation . Each typing rule — variable, abstraction, application, pairing, projection — has exactly the same tree shape as a natural-deduction rule — hypothesis, -introduction, -elimination, -introduction, -elimination. So take the same derivation tree, erase all the terms, and read each type as the formula it translates from: this is precisely a valid intuitionistic proof of (where ).
Since the two translations (proof term, and term proof) are structural inverses of each other on derivation trees — each one undoes exactly what the other does, rule by rule — is provable exactly when is inhabited.
UndergraduateReal-World Applications and Worked Examples
Rich type systems catch entire classes of bugs before a program ever runs: Rust's ownership types prevent use-after-free and data races at compile time, and dependently typed languages can encode invariants like "this list has exactly elements" or "this index is within bounds" directly into types, making certain runtime errors unrepresentable. The deepest application is inside proof assistants themselves: Coq, Agda, and Lean have small, carefully audited kernels that do nothing but type-check terms — because of the Curry–Howard correspondence, accepting a term of a theorem's type is accepting a machine-checked proof, so trusting a mathematical result the size of the Feit–Thompson theorem or the four-color theorem reduces to trusting a few hundred lines of type-checking code, not the (much larger, much harder to audit) tactics that built the proof term.
Example: A -type that ordinary function types cannot express
Let be the type of length- lists of elements of type . Write down a term inhabiting — "for every length , a function on length- vectors" — and explain why simple types (without ) cannot express this specification at all.
Solution
The term works: for each natural number , it takes a length- vector and returns it unchanged — plainly well-typed, since is returned at type , for whichever was supplied.
What makes this a genuine use of rather than an ordinary function type is that the type of the second argument () mentions the value of the first argument (). In simply typed lambda calculus, a function's argument and result types are fixed once and for all — with chosen before any value of type is known. There is no way to write "give me a natural number , and depending on which number you gave me, I will demand a vector of that exact length" using only : you would need a single fixed type that somehow works for every simultaneously, which is not what means.
Concretely, if you tried to write this using ordinary function types, you could at best get for one FIXED placeholder length — useless, since it would reject vectors of any other length, or accept vectors of the wrong length. The -type is precisely the type constructor that lets the codomain "look back" at the specific value of received, which is exactly the dependency simple types cannot express.
Example: Curry–Howard for function composition
The formula expresses transitivity of implication. Under Curry–Howard, find the -term that proves it, and verify its type derivation step by step.
Solution
The term is — exactly function composition applied to .
Derive the type from the inside out. In context : since and , application gives . Since and , application gives .
Now discharge the abstractions from the inside out. has type in context . Then has type in context . Finally has type with no remaining free assumptions — a closed term.
So , meaning is a Curry–Howard proof of transitivity: "if implies , and implies , then implies " — the same term that a functional programmer would write to compose two functions.
ResearchResearch today
In simply typed lambda calculus, if and , what is the type of ?
Why can a proof assistant like Coq, Agda, or Lean trust a machine-checked proof thousands of lines long?
When does not actually depend on , what does reduce to?
The Univalence Axiom says that...
References
- The Univalent Foundations Program (2013). Homotopy Type Theory: Univalent Foundations of Mathematics
- Wikipedia contributors (2024). Curry–Howard correspondence
- Wikipedia contributors (2024). Intuitionistic type theory