A Map of Mathematics · ch.07 · the crisis that built computers
🗺️ Chapter 7 · 20th Century, part I

The crisis that built
your computer

Hilbert wanted a guarantee that math was complete, consistent, and decidable — a spec for truth itself. The answers were no, can't-say-from-inside, and no. The wreckage became computer science.

1Hilbert's program

At the dawn of the 20th century, math had a confidence problem. Cantor had built a tower of infinities (Chapter 6), and then Russell had walked up and pointed at a crack in the foundation: the set of all sets that don't contain themselves — does it contain itself? Either answer contradicts the question. The cliffhanger we left you on.

One paradox in the basement is enough to make a careful person nervous about the whole building. If naïve set theory — the language everyone was using to define everything else — could produce a flat contradiction, what else was lurking? David Hilbert, the most influential mathematician alive, decided to settle the question once and for all. Not with hand-waving. With a program: a checklist that, if completed, would put mathematics on bedrock forever.

He wanted three things, and honestly they sound reasonable — the kind of acceptance criteria you'd write for a system you actually trusted:

  • Completeness. Write down a fixed set of axioms and rules. Then every true statement should be provable from them. No true sentence left stranded with no derivation.
  • Consistency. Those same axioms should never let you prove both a statement and its negation. And — the strong version — you should be able to prove that consistency using only safe, finite, finger-counting reasoning, so the proof itself isn't standing on the same shaky ground.
  • Decidability. There should be a mechanical procedure — Hilbert called it the Entscheidungsproblem, the "decision problem" — that takes any statement and, after finitely many steps, tells you yes-provable or no. An algorithm for truth.

Read that last one again. Hilbert was asking for an algorithm before anyone had defined what an algorithm was. To even prove it impossible, somebody would first have to nail down "mechanical procedure" with mathematical precision. That accidental homework assignment is where the computer comes from. Hold the thought.

"In mathematics there is no ignorabimus," Hilbert declared — no "we shall not know." Every problem has a solution; we just have to grind it out. It was a beautiful, optimistic spec. Over the next five years, three young men handed it back marked FAILED, in red ink, with proofs.

🏗️
A formal system is just: a fixed alphabet of symbols, a grammar for well-formed statements, a starting set of axioms, and mechanical rules for deriving new statements from old ones. A "proof" is a finite chain of rule-applications from axioms to a conclusion. Hilbert's bet was that you could build one of these that captured all of mathematics — and check it from the outside like a type-checker.

2The sentence that says no

In 1931, a 25-year-old named Kurt Gödel destroyed the first two items on the list with a single move so clever it still feels like a magic trick. Here's the whole thing in slow motion.

Start with an observation: arithmetic is expressive. It can talk about a lot. Gödel's first insight was that it can talk about itself — if you're willing to be sneaky. Every symbol in your formal system (0, +, =, the logical connectives, variables) gets assigned a number. A statement is a string of symbols, so it becomes a sequence of numbers, which you fold into one big number with a fixed recipe. A proof is a sequence of statements, so it too becomes a number. This is Gödel numbering, and the first widget lets you run the encoder yourself.

Once statements are numbers, statements about numbers are secretly statements about statements. The sentence "there is no number that encodes a valid proof of statement #N" is itself just an arithmetic claim about numbers — so it has its own Gödel number. You've smuggled provability into the language as a property of integers.

Now the diagonal trick — the same move Cantor used in Chapter 6 to out-count the integers, the one that's going to show up three times in this chapter. Gödel uses it to build a self-referential sentence. Call it G:

🔁
G says: "This statement has no proof in this system."  Not "is false" — that's the old liar's paradox and it goes nowhere useful. G talks about provability, which we just turned into honest arithmetic. So G is a perfectly legal sentence of arithmetic that happens to describe its own unprovability.

Now run the two cases, the way you'd trace a suspicious branch:

  • Suppose the system can prove G. Then G — which asserts it has no proof — is false. A system that proves false things is broken (inconsistent). Throw it out.
  • Suppose the system can't prove G. Then what G says is exactly true: it has no proof. So G is a true statement the system cannot reach.

For any consistent system rich enough to do basic arithmetic, the second case is the one you're stuck with. There is a true sentence sitting just outside its reach — and you can't patch it by adding G as a new axiom, because the trick just builds you a fresh G′ for the bigger system. This is Gödel's first incompleteness theorem. Completeness: dead. The dream of an axiom set that proves every truth is provably unbuildable.

The second theorem is the gut-punch. Among the true-but-unprovable statements is the sentence that asserts "this system is consistent." A system can't prove its own consistency from the inside — not unless it's already inconsistent, in which case it can prove anything and the proof is worthless. Hilbert wanted a safe, internal certificate of consistency. Gödel proved that certificate can never be issued. Item two: dead.

Interactive · Gödel numbering Pick a formula · watch it become a number · decode one back
statements are now numbers — so statements can talk about statements.

3What incompleteness does not mean

No theorem in history has been more abused at dinner parties. Gödel gets dragged in to justify everything from "science can't know anything" to "therefore, God." So before we go further, the guardrails — because the actual result is precise, and the precision is the whole point.

It does not mean math is broken. Nothing collapsed. Arithmetic still works; your bank balance is fine. The theorems say a specific formal system can't prove certain true things about itself from inside. That's a statement about the reach of a fixed proof-machine, not about whether math is reliable.

It does not mean "everything is relative" or "truth is subjective." The opposite, really — Gödel's G is objectively true. The result depends on there being a sharp fact of the matter about what's true. It limits provability-from-inside, not truth.

It is not a license for mysticism. "Gödel proved the mind transcends machines / the universe is unknowable / my crystals work" — no. The theorem has hypotheses, and they're narrow: the system must be consistent, and it must be rich enough to encode arithmetic. Plenty of useful formal systems dodge it entirely. The theory of real numbers as a geometric field is complete and decidable. So is Presburger arithmetic (addition, no multiplication). The incompleteness only bites once you have enough machinery to build self-reference — which, it turns out, is not much.

⚠️
The honest one-sentence version: any consistent formal system powerful enough to do ordinary arithmetic contains true statements it cannot prove, and cannot certify its own consistency. Consistency is fine. Provability-from-inside is what's bounded. That's it. Everything past that sentence is someone selling you something.

4What does "computable" even mean?

Two items down. One left: decidability. Is there a mechanical procedure that decides any statement? To even attack the question, somebody had to answer a more basic one that nobody had ever pinned down: what is a mechanical procedure? What does it mean to "compute" something? In 1936, two people answered, separately, from totally different directions.

Alan Turing's answer was a machine. Imagine the dumbest possible computer you can still describe precisely. It has an infinite paper tape divided into cells, each holding a symbol. A head sits over one cell and can read it, write a new symbol, and shuffle one step left or right. The machine has a handful of internal states, and a lookup table: given (current state, symbol under the head), the table says what to write, which way to move, and which state to go to next. That's the entire machine. No arithmetic unit, no memory hierarchy, no clever instructions — just a head crawling along a tape following a tiny rulebook. Turing's claim: anything a human computer could do by following rote rules, this machine can do too. The second widget is one of these machines. Run it.

Alonzo Church's answer was a notation. No machine, no tape — just functions. You start with variables and exactly one construction: λx. (body), an anonymous function of x. You build everything — numbers, booleans, pairs, recursion, all of it — by nesting and applying these lambdas, and the only rule is substitution: applying a function means plugging the argument in for the variable. This is the lambda calculus, and if it smells like the core of every functional language you've ever written, that's because it is the core. Scala's a => body is Church's λa. body wearing a hat.

A machine crawling a tape. A grammar of nested functions. They could not look more different. And yet — they define the exact same class of computable functions. Anything one can compute, so can the other. Add in Gödel's own "recursive functions," and Post's tag systems, and every other honest attempt anyone has made since: same class, every time.

🤝
When two wildly different definitions, built by different people for different reasons, turn out to carve reality at exactly the same joint — you haven't defined an arbitrary thing, you've discovered a real one. This is the Church–Turing thesis: "computable" is a single, robust, definition-independent notion. It's not a theorem you can prove (it's a claim about an informal idea), but a century of failed attempts to beat it have made it about as solid as physics.
Interactive · Turing machine Pick a program · run / step · watch the head crawl
state A steps: 0 ones: 0
statereadwritemovenext
how long can a tiny table run and still stop? — that question gets a chapter of its own (ch.13).

5The halting problem

Now we have a precise "computable," so we can finally ask Hilbert's third question as a real math problem — and answer it. Turing picked a specific job and asked whether any program could do it: given a program and its input, decide whether it eventually halts or runs forever. Call the hoped-for decider halts(P, x) — returns true if program P halts on input x, false if it loops. A perfect static analyzer. Every engineer wants this.

It can't exist. And the proof is the diagonal trick a third time — Cantor in Chapter 6, then Gödel two sections ago, now Turing. Watch the detonation. Suppose halts exists. Then I can write this little troublemaker:

Now the lethal question: does trouble halt when you feed it to itself? Trace it.

  • If trouble(trouble) halts, then by definition halts(trouble, trouble) returned true — so trouble took the while(true) branch and loops forever. It doesn't halt.
  • If trouble(trouble) loops, then halts returned false — so trouble took the return branch and halts immediately.

It halts if and only if it doesn't. A flat contradiction, same flavor as Gödel's G and Cantor's missing diagonal number. The assumption was the bug: no general halts can exist. Hilbert's decidability dream — an algorithm that decides any statement — dies right here, because deciding provability would let you decide halting. All three items: dead. The whole spec, rejected.

This isn't an engineering gap that a smarter team will close next quarter. It's a proof of impossibility, permanent. And it generalizes brutally. Rice's theorem says that every non-trivial question about what a program does — does it ever output 42? does it leak memory? is it equivalent to this other program? — is undecidable in general. Static analysis has a ceiling, forever. Your linter is heuristics all the way down, and it has to be. That's not laziness; it's a theorem.

🪓
The same self-referential cut — feed the analyzer to a version of itself with the answer negated — slices through Cantor's diagonal, Gödel's G, and Turing's halting problem. One move, three of the deepest results in mathematics. If you only remember one trick from this book, remember this one.

6Types to the rescue

Back up to the paradox that started all this. Russell didn't just point at the crack — he proposed a fix, decades before Gödel. His paradox came from a set being allowed to contain itself, mixing levels that should never touch. So Russell stacked everything into a hierarchy of types: basic objects at level 0, sets of objects at level 1, sets of those at level 2, and a hard rule — a thing can only contain things of a strictly lower level. "The set of all sets that don't contain themselves" is now a grammar error. It doesn't typecheck. The paradox can't even be written down.

Sit with that, because it's the move your compiler makes every single day. You can't form the bad expression — not because it's false, but because it's ill-typed. The error happens before the question is even askable.

For a long time types were a defensive patch — a fence around the paradox. Then in 1972, Per Martin-Löf turned the fence into a foundation. His intuitionistic type theory rebuilds all of mathematics on types: a number is a type, a function is a type, a proposition is a type, even "these two things are equal" is a type. No separate logic bolted on top — the logic is the type system. It's the lineage behind today's proof assistants, the ones we'll watch check real theorems in Chapter 13.

And here's the bargain at the heart of it — the same bargain you accept every time you reach for Scala over a dynamic language: you restrict what you're allowed to write, and in exchange you get a machine-checkable guarantee. Give up the freedom to mix levels, to call a function on the wrong thing, to forget a case — and the machine hands you a proof that whole classes of nonsense can't occur. Sound familiar? That's not an analogy to your compiler. As the next section shows, it's literally the same thing.

🧰
The deal a type system offers: fewer programs you can write, stronger promises about the ones you can. Russell took the deal to dodge a paradox; Martin-Löf took it to rebuild mathematics; you take it every time you pick a typed language. Same trade, three centuries of payoff.

7Proofs are programs

Now the punchline the whole chapter has been walking toward. In the 1930s–60s, Haskell Curry and William Howard noticed that two things mathematicians thought were unrelated — formal logic and typed programming — are the exact same structure, just with different vocabulary. This is the Curry–Howard correspondence, and once you see it you can't unsee it.

The dictionary is short and shocking:

  • A proposition (a thing that might be true) is a type.
  • A proof of that proposition is a program (a value) of that type.
  • Implication A ⟹ B is the function type A => B — a recipe turning evidence for A into evidence for B.
  • "And" is a pair (A, B); "or" is a sum Either[A, B].
  • Chaining implications — proving A⟹B and B⟹C to get A⟹C — is just composing functions.

So "proving a theorem" and "writing a program with a given type" are the same activity. To prove a proposition, you inhabit its type — you exhibit a value of it. If the type is empty (no possible value), the proposition is false, unprovable. The compiler that checks your types is, secretly, a proof checker. The third widget is the Rosetta stone — pick a proposition and watch it appear three ways: as plain logic, as a Scala type with a one-line proof, and as Haskell.

Interactive · Curry–Howard Rosetta Pick a proposition · read it three ways
🧠 logic — plain English
🟥 Scala — type + term

          
🟪 Haskell — type + term

          
the program type-checks ⇔ the proposition is provable — same artifact.

Hold this thought for the finale. If a proof is just a program of the right type, then a machine can check a proof by type-checking it — no human refereeing required. Chapter 13 industrializes exactly this idea: entire research papers, written as programs, verified by a compiler. The toy below is the seed of that whole enterprise.

A total function is a proof

Let's make Curry–Howard concrete with a real theorem: if A or B holds, then B or A holds — commutativity of "or." Its type is Either[A, B] => Either[B, A]. To prove the theorem, write a total function of that type. The pattern-match exhaustiveness check is the proof checker: cover both cases and the compiler signs off; forget one and it warns you your proof is incomplete.

8Check yourself

3 questions · instant feedback 0 / 3

What this unlocked

Hilbert asked for a guarantee and got three impossibility proofs. But look at the wreckage. To prove decidability impossible, Turing had to invent a precise model of computation — and the stored-program computer, the thing on your desk, is a Turing machine with a budget. To answer "what is computation," Church built the lambda calculus — which became Lisp, then ML, then the Haskell and Scala you write today; every functional language is Church's notation industrialized. Russell's types grew into the type systems that catch your bugs at compile time. And Curry–Howard fused logic and programming so tightly that proof assistants can now verify mathematics by type-checking it.

A crisis about the limits of truth handed us the limits of machines — and, almost as a side effect, the machines themselves. The logicians went looking for certainty and accidentally invented computer science. Not a bad failure.

📡
Up next · Chapter 8
The Information Age
Fisher referees a tea-tasting, von Neumann turns strategy into math, and Shannon weighs a message in bits. Math goes to work — and every WiFi packet still pays it royalties.