Skip to the content.
← Reading failures Index Next: Worked example →

simp: simplify using known simplification lemmas

theorem simp_example (n : Nat) : n + 0 = n := by
  simp

simp automatically searches for known “simplification” lemmas and applies them, possibly many at once. This is convenient, but it hides which facts were used and why the proof works, which is bad for learning. This book avoids simp and rfl-as-a-shortcut wherever the point is to understand the proof. Instead, it uses explicit rw steps that name exactly which equality justifies each step, and induction with a fully spelled-out base case and inductive step. simp should be treated as a tool for later, independent proofs, once what it would have done by hand is already understood.

Read more: Chapter 12, “simp, now that you understand what it replaces” covers when it is the right efficient choice, once every step no longer needs to be spelled out.

constructor: build a structure/And/Iff by its constructor

theorem and_example (P Q : Prop) (hp : P) (hq : Q) : P  Q := by
  constructor
  · exact hp
  · exact hq

The · (focus dot) lets you address each remaining goal one at a time.

Mathematical reading. constructor invokes the introduction rule of the goal’s type (Chapter 3, Section 2’s $\wedge$-intro/$\vee$-intro, read more below). For a product/conjunction $P \wedge Q$ it splits the task into proving each factor separately. This reflects the universal property of the product, “a map into $P \times Q$ is a pair of maps,” so proving $P$ and proving $Q$ is enough. More generally, for any structure it reduces the goal to one subgoal per field. In a dual way, cases on $h : P \vee Q$ below invokes the elimination rule of a coproduct (Chapter 3, Section 5’s reading of $\vee$ as a coproduct): to prove anything from $P \sqcup Q$ it suffices to prove it from each summand, the case analysis $\iota_1$/$\iota_2$.

Read more: Chapter 3, Section 2 states the introduction/elimination rules for every connective by name, if “introduction rule”/”elimination rule” as general terms are new.

cases: case-split on an inductive value or hypothesis

theorem or_comm_ex {P Q : Prop} (h : P  Q) : Q  P := by
  cases h with
  | inl hp => exact Or.inr hp
  | inr hq => exact Or.inl hq

induction: proof by induction on a Nat (or other inductive type)

theorem add_zero_left (n : Nat) : 0 + n = n := by
  induction n with
  | zero => rfl
  | succ k ih =>
    show 0 + (k + 1) = k + 1
    rw [Nat.add_succ, ih]

This mirrors the mathematical principle of induction:

\[P(0) \;\land\; \big(\forall k,\ P(k) \to P(k+1)\big) \;\implies\; \forall n,\ P(n)\]

ih (induction hypothesis) is exactly $P(k)$, available to prove $P(k+1)$.

unfold: unfold a definition

def isZero (n : Nat) : Prop := n = 0

theorem isZero_zero : isZero 0 := by
  unfold isZero
  -- Goal after unfold: 0 = 0
  rfl

Mathematical reading. unfold isZero replaces the defined name with its definiens, exposing $\mathrm{isZero}(0) = (0 = 0)$. This uses a definitional equality: $\mathrm{isZero} := (n \mapsto n = 0)$ means the two are interchangeable by definition (like unwinding “$n$ is even” to “$\exists k,\ n = 2k$” in a proof). So the goal becomes the tautology $0 = 0$, closed by reflexivity.

← Reading failures Index Next: Worked example →
Try Lean
Lean playground · v1.4.18