Use this file to discover all available pages before exploring further.
Lean 4 is simultaneously a functional programming language and an interactive theorem prover. At the heart of its logical system is the Curry-Howard correspondence: every proposition is a type, and every proof of that proposition is a term of that type. This elegant unification means that writing a proof is the same activity as writing a program — the kernel checks both with the same type checker.
Because proofs are erased at runtime, choosing between two proofs of the same proposition has no computational cost.
Prop is Sort 0 and Type u is Sort (u+1). Both are special cases of the universe Sort u. The type Prop satisfies impredicativity: a ∀ over Prop landing in Prop stays in Prop.
Lean 4’s logical connectives are defined as inductive types in Init.Prelude and Init.Core.
And (∧)
Or (∨)
Not (¬) and Iff (↔)
Exists (∃) and Forall (∀)
-- And is a structure with two fields-- structure And (a b : Prop) : Prop where-- intro ::-- left : a-- right : btheorem and_comm (p q : Prop) (h : p ∧ q) : q ∧ p := ⟨h.right, h.left⟩-- Explicit constructor syntaxtheorem and_intro (hp : p) (hq : q) : p ∧ q := And.intro hp hq
-- Or has two constructors: inl and inr-- inductive Or (a b : Prop) : Prop where-- | inl (h : a) : Or a b-- | inr (h : b) : Or a btheorem or_comm (p q : Prop) (h : p ∨ q) : q ∨ p := match h with | Or.inl hp => Or.inr hp | Or.inr hq => Or.inl hq
-- Not p is defined as p → False-- @[implicit_reducible] def Not (a : Prop) : Prop := a → False-- Iff has two fields: mp and mpr-- structure Iff (a b : Prop) : Prop where-- mp : a → b-- mpr : b → atheorem not_not_intro (p : Prop) (hp : p) : ¬¬p := fun hnotp => hnotp hptheorem iff_comm (p q : Prop) (h : p ↔ q) : q ↔ p := Iff.intro h.mpr h.mp
-- ∀ is built into the kernel as a dependent function type-- ∃ x, p x desugars to Exists (fun x => p x)theorem exists_intro : ∃ n : Nat, n > 0 := ⟨1, Nat.one_pos⟩theorem forall_imp (p q : α → Prop) (h : ∀ x, p x → q x) (hall : ∀ x, p x) : ∀ x, q x := fun x => h x (hall x)
The by keyword switches from term mode to tactic mode. Inside a by block, you issue a sequence of tactics that transform the proof goal.
-- Term-mode prooftheorem add_zero_term (n : Nat) : n + 0 = n := rfl-- Tactic-mode proof of the same theoremtheorem add_zero_tactic (n : Nat) : n + 0 = n := by rfl
When you open a by block in an IDE like VS Code with the Lean 4 extension, you see the tactic state: the local hypotheses above the ⊢ turnstile and the current goal below it.
Use intro to move universally-quantified variables and hypothesis antecedents from the goal into the local context:
theorem imp_trans (p q r : Prop) : (p → q) → (q → r) → p → r := by intro hpq hqr hp -- Goal: ⊢ r -- hpq : p → q, hqr : q → r, hp : p exact hqr (hpq hp)
theorem and_elim (p q r : Prop) (h : p ∧ q) (hpqr : p → q → r) : r := by obtain ⟨hp, hq⟩ := h exact hpqr hp hqtheorem or_elim_example (p q r : Prop) (h : p ∨ q) (hpr : p → r) (hqr : q → r) : r := by obtain hp | hq := h · exact hpr hp · exact hqr hq
Lean 4’s core logic is constructive (no law of excluded middle by default), but Classical reasoning is available by opening Classical or importing the relevant axioms.
open Classical intheorem double_neg (p : Prop) : ¬¬p ↔ p := Classical.not_not
Any theorem or definition that uses Classical.choice (which underlies Classical.em and Classical.byContradiction) will appear under the axioms Classical.choice, propext, and Quot.sound when you run #print axioms. This is expected and is not a sign of an error.
Propositional extensionality
propext is one of Lean 4’s core axioms. It states that if two propositions are logically equivalent, they are definitionally equal as types:
-- axiom propext {a b : Prop} : (a ↔ b) → a = bexample (p q : Prop) (h : p ↔ q) : p = q := propext h
This axiom is what allows simp and rw to rewrite propositions using ↔.