Lecture 3: From Natural Deduction to Proofs in Lean
1. Aim of the lecture
In the previous lecture, we learned to read a proof state and used tactics such as intro, apply, have, exact, and cases. We now connect those commands to the rules of natural deduction. Tactics are not arbitrary moves: they build and use proofs according to the logical structure of propositions.
The lecture has five aims:
- revisit the idea of type inhabitation;
- introduce the Curry-Howard correspondence;
- read introduction and elimination rules in natural deduction;
- recognize those rules in Lean proofs;
2. Types and inhabitants
A type describes a class of possible objects. A type is inhabited when there is at least one term of that type. If t has type T, we write t : T and say that t inhabits T.
The idea is not limited to Lean. The informal type “moon of Jupiter” is inhabited because Io is a moon of Jupiter. Io is a concrete object satisfying the description expressed by the type NASA Science, Io: Facts.
#check 4 -- 4 : Nat
#check True -- True : Prop
#check True.intro -- True.intro : True
Nat is inhabited, for example, by 4. True is also inhabited: True.intro has exactly type True. This observation leads from the general relation between terms and types to the relation between proofs and propositions.
3. The Curry-Howard correspondence
Under the Curry-Howard correspondence, propositions and types are two ways of describing the same structure. A proposition specifies what must be proved; the corresponding type specifies what kind of term must be built. A proof of the proposition is a term inhabiting that type Wadler, Propositions as Types.
| Logic | Type theory |
|---|---|
| proposition | type |
| proof | term |
A → B | function from proofs of A to proofs of B |
introduction of → | function construction |
elimination of → | function application |
Checking a proof therefore means checking that the constructed term has the declared type. A derivation does not merely certify that a proposition is true: it specifies how to construct its proof Theorem Proving in Lean 4, Propositions and Proofs.
4. Natural deduction and the proof state
Natural deduction presents a proof as a sequence of local steps governed by rules. Each rule shows how to obtain a conclusion from premises. Introduction rules explain how to construct a proof with a particular main connective; elimination rules explain how to use an available proof containing that connective Logic and Proof, Natural Deduction for Propositional Logic.
A judgment has the form Γ ⊢ A. Here Γ is the context of currently available assumptions and A is the conclusion to derive. Lean's proof state has the same organization: the context appears above the turnstile and the goal after it.
Some rules introduce temporary assumptions. To prove A → B, for example, we temporarily assume A and construct B. Once the implication is complete, the assumption has been incorporated into a function from proofs of A to proofs of B.
5. Implication
Implication introduction, →I
To prove A → B, we introduce an assumption of type A and construct a proof of B. In Lean, intro performs precisely this step.
We want to prove that if it rains and it is cold, then it rains. We introduce the assumption that it rains and is cold; we obtain that it rains by taking the left component; we use exactly that fact.
-- Every example follows the same path: a natural-deduction rule, its reading
-- in natural language, a concrete instance, and a term built in the proof state.
-- Implication introduction, →I.
-- To prove `A → B`, we introduce the temporary assumption `A` and construct `B`.
-- Example: if it rains and it is cold, then it rains.
theorem
(Rains IsCold : Prop) :
Rains ∧ IsCold → Rains := Rains:PropIsCold:Prop⊢ Rains ∧ IsCold → Rains
Rains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsCold⊢ Rains
Rains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsColdhRains:Rains⊢ Rains
All goals completed! 🐙Implication elimination, →E
If we have A → B and A, we apply the first proof to the second and obtain B. This is modus ponens.
-- Implication elimination, →E: modus ponens.
-- From `A → B` and `A`, we obtain `B`.
-- Example: if it rains, I take the umbrella; it rains; therefore I take it.
theorem
(Rains TakeUmbrella : Prop)
(hRainsUmbrella : Rains → TakeUmbrella)
(hRains : Rains) :
TakeUmbrella := Rains:PropTakeUmbrella:ProphRainsUmbrella:Rains → TakeUmbrellahRains:Rains⊢ TakeUmbrella
Rains:PropTakeUmbrella:ProphRainsUmbrella:Rains → TakeUmbrellahRains:RainshTakeUmbrella:TakeUmbrella⊢ TakeUmbrella
All goals completed! 🐙hRainsUmbrella behaves as a function: it receives a term of type Rains and returns a term of type TakeUmbrella.
6. Conjunction
Conjunction introduction, ∧I
To prove A ∧ B, we must construct both components. Applying And.intro splits the goal into two cases: first A, then B.
-- Conjunction introduction, ∧I.
-- From a proof of A and a proof of B, we construct a proof of A ∧ B.
theorem
(Rains IsCold : Prop)
(hRains : Rains)
(hIsCold : IsCold) :
Rains ∧ IsCold := Rains:PropIsCold:ProphRains:RainshIsCold:IsCold⊢ Rains ∧ IsCold
Rains:PropIsCold:ProphRains:RainshIsCold:IsCold⊢ RainsRains:PropIsCold:ProphRains:RainshIsCold:IsCold⊢ IsCold
Rains:PropIsCold:ProphRains:RainshIsCold:IsCold⊢ Rains All goals completed! 🐙
Rains:PropIsCold:ProphRains:RainshIsCold:IsCold⊢ IsCold All goals completed! 🐙The bullets belong to distinct goals, corresponding to the two conjuncts.
Conjunction elimination, ∧Eₗ and ∧Eᵣ
A proof of A ∧ B contains a proof of A and a proof of B. We select the left component with .left and the right component with .right.
theorem lecture03_and_elim_left
(Rains IsCold : Prop) (hBoth : Rains ∧ IsCold) : Rains := Rains:PropIsCold:ProphBoth:Rains ∧ IsCold⊢ Rains
Rains:PropIsCold:ProphBoth:Rains ∧ IsColdhRains:Rains⊢ Rains
All goals completed! 🐙theorem lecture03_and_elim_right
(Rains IsCold : Prop) (hBoth : Rains ∧ IsCold) : IsCold := Rains:PropIsCold:ProphBoth:Rains ∧ IsCold⊢ IsCold
Rains:PropIsCold:ProphBoth:Rains ∧ IsColdhIsCold:IsCold⊢ IsCold
All goals completed! 🐙Introduction constructs a conjunction from its components; elimination travels in the opposite direction and retrieves a component.
7. Disjunction
Disjunction introduction, ∨Iₗ and ∨Iᵣ
To prove A ∨ B, it is enough to construct one side, but we must specify which one. Or.inl chooses the left side; Or.inr chooses the right side.
-- Left disjunction introduction, ∨Iₗ.
theorem
(Rains Snows : Prop)
(hRains : Rains) :
Rains ∨ Snows := Rains:PropSnows:ProphRains:Rains⊢ Rains ∨ Snows
Rains:PropSnows:ProphRains:Rains⊢ Rains
All goals completed! 🐙-- Right disjunction introduction, ∨Iᵣ.
theorem
(Rains Snows : Prop)
(hSnows : Snows) :
Rains ∨ Snows := Rains:PropSnows:ProphSnows:Snows⊢ Rains ∨ Snows
Rains:PropSnows:ProphSnows:Snows⊢ Snows
All goals completed! 🐙Disjunction elimination, ∨E
If we have A ∨ B, we do not know which side was proved. To obtain one conclusion C, we must derive it both in the A case and in the B case.
-- Disjunction elimination, ∨E.
-- From `A ∨ B`, `A → C`, and `B → C`, we obtain C by cases.
theorem
(Rains Snows TakeUmbrella : Prop)
(hRainsOrSnows : Rains ∨ Snows)
(hRainsUmbrella : Rains → TakeUmbrella)
(hSnowsUmbrella : Snows → TakeUmbrella) :
TakeUmbrella := Rains:PropSnows:PropTakeUmbrella:ProphRainsOrSnows:Rains ∨ SnowshRainsUmbrella:Rains → TakeUmbrellahSnowsUmbrella:Snows → TakeUmbrella⊢ TakeUmbrella
cases hRainsOrSnows with
Rains:PropSnows:PropTakeUmbrella:ProphRainsUmbrella:Rains → TakeUmbrellahSnowsUmbrella:Snows → TakeUmbrellahRains:Rains⊢ TakeUmbrella
Rains:PropSnows:PropTakeUmbrella:ProphRainsUmbrella:Rains → TakeUmbrellahSnowsUmbrella:Snows → TakeUmbrellahRains:RainshTakeUmbrella:TakeUmbrella⊢ TakeUmbrella
All goals completed! 🐙
Rains:PropSnows:PropTakeUmbrella:ProphRainsUmbrella:Rains → TakeUmbrellahSnowsUmbrella:Snows → TakeUmbrellahSnows:Snows⊢ TakeUmbrella
Rains:PropSnows:PropTakeUmbrella:ProphRainsUmbrella:Rains → TakeUmbrellahSnowsUmbrella:Snows → TakeUmbrellahSnows:SnowshTakeUmbrella:TakeUmbrella⊢ TakeUmbrella
All goals completed! 🐙cases eliminates the disjunction by making all possible forms explicit.
8. Negation, truth, and falsity
Negation
In Lean, ¬A is defined as A → False. A proof of ¬A is therefore a function taking every hypothetical proof of A to a contradiction.
To introduce ¬A, we introduce A and construct False.
-- Negation introduction, ¬I.
-- To prove `¬A`, we introduce A and derive False.
theorem
(DrinkCoffee StayAwake : Prop)
(hCoffeeAwake : DrinkCoffee → StayAwake)
(hNotAwake : ¬StayAwake) :
¬DrinkCoffee := DrinkCoffee:PropStayAwake:ProphCoffeeAwake:DrinkCoffee → StayAwakehNotAwake:¬StayAwake⊢ ¬DrinkCoffee
DrinkCoffee:PropStayAwake:ProphCoffeeAwake:DrinkCoffee → StayAwakehNotAwake:¬StayAwakehDrinkCoffee:DrinkCoffee⊢ False
DrinkCoffee:PropStayAwake:ProphCoffeeAwake:DrinkCoffee → StayAwakehNotAwake:¬StayAwakehDrinkCoffee:DrinkCoffeehStayAwake:StayAwake⊢ False
DrinkCoffee:PropStayAwake:ProphCoffeeAwake:DrinkCoffee → StayAwakehNotAwake:¬StayAwakehDrinkCoffee:DrinkCoffeehStayAwake:StayAwakehContradiction:False⊢ False
All goals completed! 🐙To eliminate a negation, we apply ¬A to A and obtain False.
theorem lecture03_not_elim
(LabOpen : Prop) (hOpen : LabOpen) (hNotOpen : ¬LabOpen) : False := LabOpen:ProphOpen:LabOpenhNotOpen:¬LabOpen⊢ False
LabOpen:ProphOpen:LabOpenhNotOpen:¬LabOpenhContradiction:False⊢ False
All goals completed! 🐙Truth and falsity
True has a canonical constructor and requires no assumptions.
-- Truth introduction, ⊤I.
theorem : True := ⊢ True
All goals completed! 🐙 -- Alternatively: trivialexact True.intro can also be replaced by trivial.
False has no constructors. If the context already contains a proof of False, we can eliminate falsity and obtain any proposition. This is the principle of ex falso quodlibet.
-- Falsity elimination, ⊥E.
theorem
(Rains : Prop)
(hContradiction : False) :
Rains := Rains:ProphContradiction:False⊢ Rains
Rains:ProphContradiction:False⊢ False -- Alternatively: exfalso
All goals completed! 🐙The rule does not let us prove anything arbitrarily: it applies only after a contradiction has been constructed.
9. Biconditional
A proof of A ↔ B contains two functions: one from A to B and one from B to A.
Biconditional introduction, ↔I
To prove that “it rains and it is cold” is equivalent to “it is cold and it rains,” we construct both directions.
-- Biconditional introduction, ↔I: construct both directions.
theorem
(Rains IsCold : Prop) :
Rains ∧ IsCold ↔ IsCold ∧ Rains := Rains:PropIsCold:Prop⊢ Rains ∧ IsCold ↔ IsCold ∧ Rains
Rains:PropIsCold:Prop⊢ Rains ∧ IsCold → IsCold ∧ RainsRains:PropIsCold:Prop⊢ IsCold ∧ Rains → Rains ∧ IsCold
Rains:PropIsCold:Prop⊢ Rains ∧ IsCold → IsCold ∧ Rains Rains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsCold⊢ IsCold ∧ Rains
Rains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsCold⊢ IsColdRains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsCold⊢ Rains
Rains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsCold⊢ IsCold All goals completed! 🐙
Rains:PropIsCold:ProphRainsAndIsCold:Rains ∧ IsCold⊢ Rains All goals completed! 🐙
Rains:PropIsCold:Prop⊢ IsCold ∧ Rains → Rains ∧ IsCold Rains:PropIsCold:ProphIsColdAndRains:IsCold ∧ Rains⊢ Rains ∧ IsCold
Rains:PropIsCold:ProphIsColdAndRains:IsCold ∧ Rains⊢ RainsRains:PropIsCold:ProphIsColdAndRains:IsCold ∧ Rains⊢ IsCold
Rains:PropIsCold:ProphIsColdAndRains:IsCold ∧ Rains⊢ Rains All goals completed! 🐙
Rains:PropIsCold:ProphIsColdAndRains:IsCold ∧ Rains⊢ IsCold All goals completed! 🐙Biconditional elimination, ↔Eₗ and ↔Eᵣ
Iff.mp extracts the left-to-right direction; Iff.mpr extracts the right-to-left direction.
-- Left-to-right biconditional elimination, ↔Eₗ.
theorem
(EvenNumber DivisibleByTwo : Prop)
(hEquivalence : EvenNumber ↔ DivisibleByTwo)
(hEvenNumber : EvenNumber) :
DivisibleByTwo := EvenNumber:PropDivisibleByTwo:ProphEquivalence:EvenNumber ↔ DivisibleByTwohEvenNumber:EvenNumber⊢ DivisibleByTwo
EvenNumber:PropDivisibleByTwo:ProphEquivalence:EvenNumber ↔ DivisibleByTwohEvenNumber:EvenNumberhDirection:EvenNumber → DivisibleByTwo⊢ DivisibleByTwo
EvenNumber:PropDivisibleByTwo:ProphEquivalence:EvenNumber ↔ DivisibleByTwohEvenNumber:EvenNumberhDirection:EvenNumber → DivisibleByTwohDivisibleByTwo:DivisibleByTwo⊢ DivisibleByTwo
All goals completed! 🐙-- Right-to-left biconditional elimination, ↔Eᵣ.
theorem
(EvenNumber DivisibleByTwo : Prop)
(hEquivalence : EvenNumber ↔ DivisibleByTwo)
(hDivisibleByTwo : DivisibleByTwo) :
EvenNumber := EvenNumber:PropDivisibleByTwo:ProphEquivalence:EvenNumber ↔ DivisibleByTwohDivisibleByTwo:DivisibleByTwo⊢ EvenNumber
EvenNumber:PropDivisibleByTwo:ProphEquivalence:EvenNumber ↔ DivisibleByTwohDivisibleByTwo:DivisibleByTwohDirection:DivisibleByTwo → EvenNumber⊢ EvenNumber
EvenNumber:PropDivisibleByTwo:ProphEquivalence:EvenNumber ↔ DivisibleByTwohDivisibleByTwo:DivisibleByTwohDirection:DivisibleByTwo → EvenNumberhEvenNumber:EvenNumber⊢ EvenNumber
All goals completed! 🐙After extracting the needed direction, we apply it as an ordinary implication.
10. Choosing a rule
When reading a proof state, ask a few questions. First: what is the main connective of the goal? It often suggests an introduction rule. Second: which compound proofs are already in the context? They suggest elimination rules.
| Lean tactic or term | Natural-deduction reading |
|---|---|
intro h | introduce an assumption |
apply C | apply a constructor or rule |
have h := ... | obtain an intermediate fact |
exact h | use exactly the required term |
cases h with | consider the possible cases |
h.left, h.right | eliminate a conjunction |
hAB hA | eliminate an implication |
This vocabulary describes the structure of the term Lean is constructing and makes the relationship between formal rule, natural-language argument, and proof script explicit.
11. Working strategy
When constructing a proof:
1. read the goal and assumptions; 2. identify the goal's main connective; 3. choose an introduction rule when the goal suggests one; 4. look for a useful elimination rule in the context; 5. name important intermediate results with have; 6. close the goal with exact when the required term is available; 7. verify that every case and subgoal has been solved.
Classroom.lean presents each rule separately. Exercises.lean begins with direct applications and then combines rules into complete arguments. Solutions.lean keeps the proof structure explicit so that each Lean step can be connected to its natural-deduction rule.
12. Sources and further reading
- Jeremy Avigad, Leonardo de Moura, Soonho Kong, and Sebastian Ullrich, Theorem Proving in Lean 4, “Propositions and Proofs”: https://lean-lang.org/theorem_proving_in_lean4/Propositions-and-Proofs/.
- Jeremy Avigad, Robert Y. Lewis, and Floris van Doorn, Logic and Proof, “Natural Deduction Rules”: https://leanprover.github.io/logic_and_proof_lean3/nd_quickref.html.
- Philip Wadler, “Propositions as Types,” Communications of the ACM 58(12), 2015: https://homepages.inf.ed.ac.uk/wadler/papers/propositions-as-types/propositions-as-types.pdf.
- NASA Science, “Io: Facts”: https://science.nasa.gov/jupiter/jupiter-moons/io/facts/.