4.6.18 · D2Theory of Computation

Visual walkthrough — Halting problem — undecidability proof by diagonalization

2,295 words10 min readBack to topic

This page is the visual companion to the parent proof. Prerequisite ideas — Turing Machines, Decidable vs Recognizable Languages, Cantor's Diagonal Argument — are re-explained in pictures as they arise, so you can read straight through.


Step 1 — A program is just a string

WHAT. Before any logic, one plain fact: the source code of a program is a finite sequence of characters. That means a program can be stored, copied, and — crucially — handed to another program as ordinary input data.

WHY. The whole proof rests on feeding a program its own code. If code weren't just data, "run on " would be nonsense. So we must earn this idea first. (This is the same insight that makes the Universal Turing Machine possible — one machine reading another machine's description.)

PICTURE. On the left, a little program P drawn as a scroll of text. An arrow carries that same scroll into an input slot on the right. The program and the data are the identical object — only the role differs.

Figure — Halting problem — undecidability proof by diagonalization

Every one of these symbols is now defined. We will not use a new one before naming it.


Step 2 — Suppose the perfect predictor exists

WHAT. Assume — for the sake of argument — that somebody hands us a total program called . "Total" means: always halts and always answers correctly. Its job:

Reading the equation term by term: takes two arguments — the first is the program we are curious about, the second is the input that program would receive. It returns one of exactly two labels, or .

WHY. We want to prove cannot exist. The cleanest weapon against "X exists" is proof by contradiction: pretend it does, then derive an absurdity. The absurdity we manufacture will force the pretence to collapse.

PICTURE. A black box labelled with two input wires (P and w) and one output wire that lights up either green or red . A tick mark reminds us: it always answers — never gets stuck.

Figure — Halting problem — undecidability proof by diagonalization

Step 3 — The table of all behaviours (the grid we will attack)

WHAT. List every program down the rows: (there are infinitely many, but they can be numbered — that is what "listable" means). Across the columns, list the same programs used now as inputs. Cell records the answer to "does halt on ?" — an H or an L.

WHY. , if it exists, fills in this entire grid correctly. To break we only need to break one column against one row — the diagonal. Laying out the grid makes the target visible. (This is exactly the stage on which Cantor's Diagonal Argument plays out: a grid, and a diagonal you deliberately disagree with.)

PICTURE. A grid. The diagonal cells — "does halt on itself?" — are highlighted in orange. Those diagonal answers are the only ones we will touch.

Figure — Halting problem — undecidability proof by diagonalization

Step 4 — Build the troublemaker

WHAT. Using as a helper, define a new program that takes one program as input:

D(P):
    if H(P, P) == HALT:   # ask: does P halt on its own code?
        loop forever      # then D deliberately loops
    else:                 # H said P loops on itself
        halt              # then D deliberately halts

Term by term: calls — the diagonal question for . Then does the opposite of that prediction. Predicted HALT loops. Predicted LOOP halts.

WHY. is a machine for disagreeing with the diagonal. In grid language: if the diagonal cell says H, produces L there; if it says L, produces H. 's row is the flipped diagonal.

PICTURE. The orange diagonal from Step 3, and beneath it 's behaviour row with every cell flipped (H↔L). Arrows show each flip. agrees with no program at its own column.

Figure — Halting problem — undecidability proof by diagonalization

Step 5 — Feed its own code: run

WHAT. Since is a program, and programs are strings (Step 1), we may run on the input . We now trace and hit the trap.

WHY. was designed to disagree with the diagonal everywhere, including at its own cell. So we point at the one cell it cannot escape: itself. What does the table say cell should be? Whatever predicts. And was built to contradict exactly that.

PICTURE. A flowchart of the single run : it calls ; the two output branches (green HALT, red LOOP) each loop back to a contradiction stamp.

Figure — Halting problem — undecidability proof by diagonalization

Let us walk both possible answers of — there are only two, and we must cover each.


Step 6 — Case A: says HALT

WHAT. Suppose . By 's definition, this is claiming " halts on ". But now trace 's own code with this answer: the if H(P,P)==HALT branch is taken, so executes loop forever. Therefore loops — it does not halt.

WHY. We are testing whether 's prediction can be self-consistent. It cannot: predicted "halts", the machine actually loops. is wrong.

PICTURE. The green HALT wire out of ; following 's code it lands on loop forever; a red ✗ marks the clash between the prediction (halts) and the reality (loops).

Figure — Halting problem — undecidability proof by diagonalization

Step 7 — Case B: says LOOP

WHAT. Suppose instead , i.e. claims " loops on ". Trace : the if condition is false, so the else branch runs — executes halt. Therefore halts.

WHY. Again the prediction and the reality are opposite. predicted "loops", the machine actually halts. is wrong here too.

PICTURE. The red LOOP wire out of ; following 's code it lands on halt; a red ✗ marks the clash (predicted loops, actually halts).

Figure — Halting problem — undecidability proof by diagonalization

Step 8 — Collapse the two cases into one impossible sentence

WHAT. Chain Case A and Case B together. Case B says halts exactly when ; and Case A says is forced precisely when loops. Putting the whole loop of implications on one line:

Reading each link: the left is 's code (halting means the else branch ran, which needs to have said LOOP). The right is the meaning of (that label means runs forever). Stripping the middle:

That is a statement equivalent to its own negation — like "this sentence is false". No truth value fits.

WHY. A thing cannot both halt and not-halt. The only ingredient we ever assumed was . Everything after (, running ) was legal construction. So the assumption is the culprit.

PICTURE. A circular chain of three boxes — " halts" → " said LOOP" → " loops" → back to " halts" — forming a closed contradiction ring, with a red lightning bolt where halts meets loops.

Figure — Halting problem — undecidability proof by diagonalization

Step 9 — The degenerate case: "just simulate and watch" is not a decider

WHAT. A natural objection: "why bother with — just run on and see." Call this simulator . If halts, eventually notices and reports . But if loops forever, also runs forever and never reports anything.

WHY. This shows the boundary case matters. A decider must always halt with an answer. answers on the halting inputs but hangs on the looping ones — so is a recognizer (semi-decider), not a decider. is therefore recognizable but undecidable — a distinction lifted directly from Decidable vs Recognizable Languages.

PICTURE. Two timelines. Top: halts → simulator stops → outputs HALT ✓. Bottom: loops → simulator's clock spins forever → the LOOP answer never arrives (dashed, greyed out).

Figure — Halting problem — undecidability proof by diagonalization

The one-picture summary

WHAT. One figure that compresses all nine steps: code-is-data → assume → the grid → flip the diagonal into → run → both wires die → the impossible sentence.

PICTURE. A left-to-right pipeline of the whole argument, ending in the red contradiction ring; the flipped-diagonal grid sits above as the visual heart.

Figure — Halting problem — undecidability proof by diagonalization
Recall Feynman retelling of the whole walkthrough

Start with one small truth: a program is just text, so I can hand a program a copy of itself. Now pretend a magic box exists that always tells me, correctly, whether any program eventually stops. I imagine a giant table: rows are programs, columns are the same programs used as inputs, and each cell says "stops" or "runs forever". I only care about the diagonal — each program run on its own code. I build a sneaky program : it asks the box about a program running on itself, then does the opposite of the answer. Finally I show its own code. If the box says " stops", runs forever; if the box says " runs forever", stops. Either way the box is wrong — so " stops" and " runs forever" become the same thing, which is impossible. The only thing I ever guessed was the magic box, so the box can't exist. And if you object "just run it and watch" — that only works when the program does stop; if it loops, your watcher loops too and never says "runs forever", so watching is a recognizer, never a decider.


Recall

In cell of the grid, what question is being asked? ::: Does program halt when given its own code as input? What does do relative to 's prediction about on itself? ::: The exact opposite (predicted HALT → loops; predicted LOOP → halts). Which single assumption does the contradiction destroy? ::: That the total decider exists. Why is "simulate and watch" only a recognizer? ::: If the program loops forever the simulation also never halts, so it can never output LOOP. State the collapsed impossible sentence. ::: halts loops.