Propositions as Types
Lean represents logical propositions as types. Understanding this representation is key to understanding how proofs work.
The Universe of Propositions
Lean has different "universes" for different kinds of types:
1-- Data types live in Type2#check Nat -- Nat : Type3#check String -- String : Type4#check List Nat -- List Nat : Type56-- Propositions live in Prop7#check True -- True : Prop8#check False -- False : Prop9#check 2 + 2 = 4 -- 2 + 2 = 4 : PropPropis special: it's the universe of things that are either true or false. Every proposition is a type, but a type specifically designed to express a logical claim.
Equality
The most common proposition is equality:
1-- Equality is a proposition2#check (5 = 5) -- 5 = 5 : Prop3#check (1 + 1 = 2) -- 1 + 1 = 2 : Prop4#check (1 + 1 = 3) -- 1 + 1 = 3 : Prop (still a valid Prop, just false)56-- Equality for any type7#check ("hello" = "hello") -- "hello" = "hello" : Prop8#check ([1,2] = [1,2]) -- [1, 2] = [1, 2] : PropNote that 1 + 1 = 3is a valid proposition—it's just false. You can state it, but you can't prove it.
True and False
1-- True: a proposition that's trivially provable2#check True -- True : Prop3example : True := trivial45-- False: a proposition with no proof6#check False -- False : Prop7-- example : False := ??? -- Impossible! No term has type FalseTrue is a type with exactly one term (trivial).False is a type with noterms—that's what makes it false.
Implication: Functions
The logical "implies" (→) is represented as a function type:
1-- "If A then B" is the type A → B2-- A proof is a function from proofs of A to proofs of B34example : True → True := fun _ => trivial56-- "If 1 = 1, then 2 = 2"7example : (1 = 1) → (2 = 2) := fun _ => rfl89-- Read: given a proof of 1 = 1, return a proof of 2 = 210-- The proof of 1 = 1 isn't even used—2 = 2 is independently trueTo prove an implication, you write a function. The function takes a proof of the hypothesis and produces a proof of the conclusion.
Constructors and Destructors
Every logical connective comes as a pair: a way to build a proof (the constructor, used when the connective is in the goal) and a way to take apart a proof (the destructor, used when the connective is in a hypothesis). Nearly every tactic you will learn is one of these two moves.
| Connective | Build (goal) | Use (hypothesis) |
|---|---|---|
| A → B | fun h => … / intro h | apply it: h a |
| A ∧ B | ⟨ha, hb⟩ / constructor | h.left, h.right / obtain ⟨ha, hb⟩ := h |
| A ∨ B | Or.inl / Or.inr / left, right | cases h — you must handle both |
| ¬A | intro h, then derive False | apply to a proof of A: absurd ha h |
| ∀ x, P x | intro x | specialise: h 5 |
| ∃ x, P x | ⟨w, proof⟩ / exact ⟨w, _⟩ | obtain ⟨x, hx⟩ := h |
| A ↔ B | ⟨mp, mpr⟩ / constructor | h.mp, h.mpr |
Conjunction: Products
"A and B" is represented as a pair:
1-- And is defined like a structure2-- structure And (a b : Prop) : Prop where3-- left : a4-- right : b56-- To prove A ∧ B, provide proofs of both7example : True ∧ True := ⟨trivial, trivial⟩89example : (1 = 1) ∧ (2 = 2) := ⟨rfl, rfl⟩1011-- Access parts with .left and .right (or .1 and .2)12example (h : True ∧ (1 = 1)) : True := h.left13example (h : True ∧ (1 = 1)) : 1 = 1 := h.rightDisjunction: Sum Types
"A or B" is represented as a sum (tagged union):
1-- Or has two constructors2-- inductive Or (a b : Prop) : Prop where3-- | inl : a → Or a b4-- | inr : b → Or a b56-- Prove left side7example : True ∨ False := Or.inl trivial89-- Prove right side10example : False ∨ True := Or.inr trivial1112-- Either side works13example : (1 = 1) ∨ (1 = 2) := Or.inl rflNegation
"Not A" is defined as "A implies False":
1-- ¬A is defined as A → False2#print Not -- def Not (a : Prop) : Prop := a → False34-- To prove ¬A, assume A and derive a contradiction5example : ¬False := fun h => h -- If False, then False (trivially)67-- 1 ≠ 2 means (1 = 2) → False8example : 1 ≠ 2 := by decide≠ is notation for ¬(... = ...), which expands to (... = ...) → False.Iff: If and Only If
A ↔ B is a structure holding two implications. It is not the same as A = B, though for propositions the two turn out to be equivalent (that is the axiom propext).
1-- structure Iff (a b : Prop) : Prop where2-- mp : a → b ("modus ponens" direction)3-- mpr : b → a ("reverse")45example (P Q : Prop) : (P ∧ Q) ↔ (Q ∧ P) :=6 ⟨fun h => ⟨h.2, h.1⟩, fun h => ⟨h.2, h.1⟩⟩78-- Using one: pick a direction9example (P Q : Prop) (h : P ↔ Q) (hp : P) : Q := h.mp hp10example (P Q : Prop) (h : P ↔ Q) (hq : Q) : P := h.mpr hq1112-- Iff is also usable by rw and simp, unlike a plain implication13example (P Q : Prop) (h : P ↔ Q) (hp : P) : Q := by rw [← h]; exact hp↔ over two separate lemmas when you can. It is a single name, and — crucially — rw and simp can rewrite with an Iffexactly as they rewrite with an equation. A one-directional implication cannot be used that way.Classical vs Constructive
Lean's core logic is constructive: to prove A ∨ B you must say which one holds. That means the law of excluded middle, P ∨ ¬P, is not automatic — it is an axiom you opt into.
1-- Not provable constructively; available as an axiom2example (P : Prop) : P ∨ ¬P := Classical.em P34-- Also classical: double negation elimination5example (P : Prop) (h : ¬¬P) : P := Classical.byContradiction fun hn => h hn67-- From a contradiction, anything8example (P Q : Prop) (h : P) (hn : ¬P) : Q := absurd h hn by_contra, push_neg, and tauto rely on it. You will see Classical.choice in the output of #print axiomsfor most real theorems, and that is completely normal — it is one of Lean's three standard axioms, not a red flag.Quantifiers
Universal: For All (∀)
1-- ∀ x : T, P x is a dependent function type2-- A proof is a function that works for any x34example : ∀ n : Nat, n = n := fun n => rfl56-- n + 0 = n holds by rfl: Nat.add recurses on its SECOND argument,7-- so n + 0 reduces to n by definition.8example : ∀ n : Nat, n + 0 = n := fun _ => rfl910-- 0 + n = n does NOT hold by rfl! Nothing can reduce until n is known.11-- example : ∀ n : Nat, 0 + n = n := fun n => rfl -- type mismatch12example : ∀ n : Nat, 0 + n = n := fun n => Nat.zero_add n1314-- Multiple quantifiers15example : ∀ a b : Nat, a + b = b + a := Nat.add_comm n + 0 = n is definitional (provable by rfl), while 0 + n = n needs a theorem (Nat.zero_add, proved by induction). The reason is purely the direction Nat.add recurses. Whenever rfl mysteriously fails on an arithmetic identity, check whether the variable is on the side that blocks reduction.Existential: There Exists (∃)
1-- ∃ x : T, P x is a dependent pair (witness + proof)2-- Provide a specific x and prove P holds for it34example : ∃ n : Nat, n > 5 := ⟨10, by decide⟩56example : ∃ n : Nat, n + n = 10 := ⟨5, rfl⟩78-- The witness and proof are bundled together9example : ∃ s : String, s.length = 5 := ⟨"hello", rfl⟩Deep Dive: Proof Irrelevance
In Prop, all proofs of the same proposition are considered equal. This is called "proof irrelevance."
1-- Two different proofs of the same thing2example (p q : 1 = 1) : p = q := rfl34-- This is why Prop is special: only truth matters, not how you proved itThis makes sense logically: if you know something is true, you don't carehowit was proven. The fact that it's true is what matters.
Summary Table
| Logic | Lean Type | To Prove |
|---|---|---|
| A implies B | A → B | Write a function |
| A and B | A ∧ B | Provide both proofs |
| A or B | A ∨ B | Provide one proof |
| Not A | ¬A (= A → False) | Derive contradiction |
| For all x, P(x) | ∀ x, P x | Function over all x |
| Exists x, P(x) | ∃ x, P x | Witness + proof |
Prove that if you have P and Q, then you can prove P ∧ Q.
1example (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q := by2 exact And.intro hp hq