A proof is a program — natural deduction, BHK, and Curry–Howard, played hands-on in a Lean game.
dr Bartosz Naskręcki · Adam Mickiewicz University in Poznań · CCAI, Warsaw Univ. of Technology
▷ live lab: /lab-lambda
What, precisely, is a proof?
A first logic course decides formulas with truth tables: a tautology is true under every valuation.
A proof assistant cannot work this way. Its kernel does not evaluate a table — it checks a derivation, a syntactic object built from a fixed stock of inference rules.
Before Lean can help us with real mathematics, we must answer the question that opens this lecture.
Proofs = programs
The answer that makes machine-checked mathematics possible: a proof is a construction, and a construction is a typed program. This is the Curry–Howard correspondence.
every connective becomes a type former,
every inference rule becomes a term former,
every Lean tactic becomes a recipe that fills a hole in a proof term.
The same moves you learn here on toy propositions build the kernel-checked theorems of the Lecture 6 formalization.
Roadmap
Gentzen's natural deduction — intro / elim rules
The BHK meaning of each connective
The Curry–Howard dictionary
Lean tactics as term construction
The classical boundary: $A\lor\neg A$, $\neg\neg A\to A$, Peirce
Play it live in Emily Riehl's Lean game
We mark precisely where intuitionistic reasoning ends and classical logic begins.
Natural deduction — implication
Each connective gets introduction rules (how to prove it) and elimination rules (how to use it). Implication is subtle: introduction discharges a hypothesis $[A]$.
${\lor}E$ is case analysis. Negation is derived: $\neg A := A\to\bot$.
BHK — what a proof is
Read each connective as a recipe for constructions, not as a truth value.
a proof of $A\land B$ is a pair $\langle a,b\rangle$;
a proof of $A\lor B$ is a tagged proof $(i,p)$ — you must know which side holds;
a proof of $A\to B$ is a function turning proofs of $A$ into proofs of $B$;
there is no proof of $\bot$;
a proof of $\neg A = A\to\bot$ is a map you apply, never something you evaluate.
The tag clause is why there is no construction witnessing $A\lor\neg A$ — you would have to decide $A$.
Curry–Howard for the connectives
Logic
Type
Introduce
Eliminate
$A \to B$
function $A\to B$
$\lambda$ fun a => …
application $f\,a$
$A \land B$
product $A\times B$
$\langle a,b\rangle$
$\pi_1,\pi_2$
$A \lor B$
sum $A+B$
$\mathsf{inl},\mathsf{inr}$
case analysis
$\bot$
empty $\varnothing$
— (no intro)
False.elim
$\neg A$
$A\to\varnothing$
assume $A$, derive $\bot$
apply to a proof of $A$
$\top$
unit $\mathbf 1$
trivial
—
Provability becomes inhabitation: $A$ is provable iff the type $A$ has a closed term.
Worked example — $A\land B\to B\land A$
The proof term is $\lambda h.\,\langle h.2,\,h.1\rangle$. Term mode and tactic mode build the same kernel object:
-- term mode: the proof IS the program
theorem and_comm' {P Q : Prop} : P ∧ Q → Q ∧ P :=
fun h => ⟨h.2, h.1⟩
-- tactic mode: a recipe that fills the same hole
theorem and_comm'' {P Q : Prop} : P ∧ Q → Q ∧ P := by
intro h -- goal Q ∧ P, with h : P ∧ Q (→I / λ)
obtain ⟨p, q⟩ := h -- destructure the pair (∧E)
exact ⟨q, p⟩ -- build the swapped pair (∧I)
Negation, constructively
Three theorems people wrongly assume need classical logic. All are pure constructions ($\neg\neg A = (A\to\bot)\to\bot$):
$$ A\to\neg\neg A,\qquad \neg(A\land\neg A),\qquad \neg\neg\neg A\to\neg A. $$
theorem dni {P : Prop} : P → ¬¬P := fun p k => k p
theorem noncontra {P : Prop} : ¬(P ∧ ¬P) := fun h => h.2 h.1
theorem tne {P : Prop} : ¬¬¬P → ¬P := fun h p => h (fun k => k p)
The converse $\neg\neg A\to A$ is not available here — it is the classical boundary in disguise.
ch term \p k. k p
Algorithm W infers the principal type α → (α → β) → β — read β as $\bot$ and this is exactly $A\to\neg\neg A$: the program came first, the proposition is its type.
De Morgan — and its classical cousin
One equivalence is fully constructive; one implication is genuinely classical:
theorem deMorgan_or {P Q : Prop} : ¬(P ∨ Q) ↔ (¬P ∧ ¬Q) :=
⟨fun h => ⟨fun p => h (Or.inl p), fun q => h (Or.inr q)⟩,
fun ⟨np, nq⟩ h => h.elim np nq⟩
The missing direction would force you to pick a tag — decide which conjunct fails. BHK forbids that.
Tactics = proof-term construction
Tactic
Rule
Term action
intro h
${\to}I$ (discharge)
$\lambda h.\,?$
exact e
close
plug in $e$
apply f
${\to}E$ backward
build $f\,?$
constructor / ⟨a,b⟩
${\land}I$
pair
rcases / obtain
${\land}E,{\lor}E,{\bot}E$
project / match
left / right
${\lor}I_1$ / ${\lor}I_2$
$\mathsf{inl}$ / $\mathsf{inr}$
exfalso
${\bot}E$ (always valid)
False.elim ?
by_contra h
classical ($\neg\neg$-elim)
byContradiction
Only the last row leaves the intuitionistic fragment. Don't confuse exfalso with by_contra.
prove P -> Q -> P
The Lambda Lab speaks this table: intro, intro, exact p closes it, and qed prints the extracted proof term \p. \q. p with its principal type — the K combinator, live. Stuck? Type hint.
The classical boundary
Equivalent over intuitionistic logic — adding any one recovers full classical logic:
theorem peirce {P Q : Prop} : ((P → Q) → P) → P := by
intro h
by_contra hnp -- hnp : ¬P, goal : False
exact hnp (h (fun p => absurd p hnp))
Every step but by_contra is constructive — the classical content is isolated in exactly one move. Glivenko: $\vdash_c A$ iff $\vdash_i \neg\neg A$.
ch type ((P -> Q) -> P) -> P
The lab's inhabitation search answers not inhabited in intuitionistic STLC — a machine check that no constructive witness exists. Compare ch type (P -> Q -> R) -> (P -> Q) -> P -> R, which finds one instantly.
Run it yourself
Feel the "proof = program" identity: $\land$-elimination is projection. In the Lambda Lab, type:
reduce FST (PAIR a b)
It computes to a; likewise reduce SND (PAIR a b) gives b. Then clear the constructive core of Riehl's A Reintroduction to Proofs — TypeWorld → Function/Implication → Conjunction/Disjunction → Empty/Negation — without touching ClassicalWorld.
Recap & references
A proof is a construction; Curry–Howard makes it a typed term, and detour removal is $\beta$-reduction.
$\to$ a function, $\land$ a product, $\lor$ a sum, $\bot$ the empty type, $\neg A = A\to\bot$.
Every tactic builds a piece of the term; only by_contra crosses into classical logic.
Wadler, Propositions as Types (2015) · Sørensen–Urzyczyn, Lectures on the Curry–Howard Isomorphism · Howard (1969/80) · Riehl, A Reintroduction to Proofs · Theorem Proving in Lean 4