| ← Typing rules and safety | Index | Next: Exercises → |
Recall
Formal definitions cited in this section, gathered here for quick reference (full citations in the Bibliography):
- Definitional equality. “Two terms that reduce to the same value
are called definitionally equal. They are considered ‘the same’ by
Lean’s type checker” (TPIL4, “Dependent Types”). Brief:
rflsucceeds exactly when this holds. - Propositional equality. “In other situations … reductions
hold only propositionally, which is to say, they are equational
theorems that must be applied explicitly” (TPIL4, “Dependent
Types” / “Tactics”). Brief: an ordinary
Propthat must be proved; definitional equality implies propositional equality, but not conversely. - Proof irrelevance. “If
p : Propis any proposition, Lean’s kernel treats any two elementst1 t2 : pas being definitionally equal … This is known as proof irrelevance” (TPIL4, “Propositions and Proofs”).
Chapter 4 introduced rfl as “the proof that both sides compute to the
same thing,” using it freely without asking exactly what “compute to
the same thing” means, or whether it is the only notion of equality
Lean has. It is not. Lean has (at least) two, and confusing them is a
common source of real confusion once proofs get more intricate. They are
worth separating out clearly now.
Definitional equality
Two terms are definitionally equal ($a \equiv b$, sometimes written
$a \equiv_\beta b$ to stress that it is driven by reduction) if they reduce to
the same normal form by unfolding definitions, beta-reduction
(substituting a lambda’s argument into its body), and the built-in
computation rules of inductive types (a match on a constructor reduces
immediately). This is a judgment the type-checker computes, not a
proposition you prove. There is no term of type a ≡ b. Either Lean’s
kernel confirms it during elaboration (silently, whenever it needs to
check that two types or terms match) or it does not, and rfl is the tactic
that asks Lean to check exactly this and fail loudly if it does not hold.
example : 2 + 2 = 4 := rfl -- 2 + 2 reduces to 4 definitionally
example : 0 + 2 = 2 := rfl -- Nat.add recurses on its 2nd arg; 2 + 0 = 2 is the base case
-- example : 2 + 0 = 2 := rfl -- also rfl (0 + n needs induction, n + 0 doesn't)
One precise point worth noting: “reduce to the same normal form” does not mean
Lean necessarily unfolds a term all the way down before comparing.
Checking a ≡ b typically only reduces each side as far as its weak
head normal form (WHNF): far enough to see the outermost constructor
or function head, no further than needed. It then compares heads,
recursing into subterms only as required. This is exactly why “Nat.add
recurses on its second argument” is a fact about evaluation order. To
determine whether 0 + n reduces at all, Lean examines n’s outermost
shape (its WHNF). If n is an unknown variable rather than a known
zero/succ constructor, there is nothing to see, so no reduction fires
and the goal is stuck at 0 + n, exactly as Chapter 4 found.
Propositional equality
Propositional equality, written a = b (the Eq type from Chapter 3),
is an ordinary proposition, a Prop, that must be proved, the way
any other statement is proved. Definitional equality is only the easiest
possible case (rfl is a proof of a = b precisely by showing that
a ≡ b). But a = b can hold propositionally even when a and b are
not definitionally equal. For instance, Chapter 4’s
my_add_comm : ∀ a b, a + b = b + a proves a propositional equality that
is not witnessed by rfl (indeed, rfl fails on a + b = b + a for
variable a, b, precisely because Nat.add only reduces on its second
argument, so the two sides do not share a common reduct without the
induction Chapter 4 walked through).
This is the exact type-theoretic counterpart of a distinction every mathematician already makes without naming it: “$2+2$ and $4$ are the same symbol after simplification” versus “$a+b$ and $b+a$ denote the same number, which requires an argument (commutativity) to establish, not mere inspection.” Definitional equality is inspection. Propositional equality is (potentially) a theorem.
Why the distinction has real consequences
Two practical facts follow directly:
rflonly ever proves definitional equalities. Whenrflfails, that is not evidence the statement is false. It only indicates the two sides do not share a computed normal form without further argument. This is exactly the “failure is information” point from Chapter 4:rflfailing ona + b = b + ais the signal to reach forinduction, not evidence that the statement should be abandoned.rwworks up to propositional equality, but the resulting goal is checked up to definitional equality. Whenrw [h]rewrites a goal usingh : a = b, the new goal is a genuinely different term (withbsubstituted fora), and Lean must still confirm the surrounding structure of the goal stays well-typed after the substitution. This subtlety occasionally shows up as the “motive is not type correct” error mentioned in Chapter 4, which is exactly a case where propositional substitution runs into a definitional-equality check it cannot discharge automatically (common when the term being rewritten appears inside a dependent type’s index, as in Chapter 11’sPath).
A note on proof irrelevance
One more fact worth having, since it explains why this book never worries
about “which proof” fills a Prop-valued field: Lean treats all proofs of
the same proposition as interchangeable (this is called proof
irrelevance for Prop). If h1 h2 : a = b are two different proof
terms of the same propositional equality, h1 and h2 are
definitionally equal to each other, even if they were built by
completely different tactic scripts. This is why the question “is this the
same proof of assoc” never arises, throughout this book, when comparing two
Group structures on the same carrier with the same operation. The
proof component does not matter; only the data (op, id, inv) can
actually differ.
A note on structure eta
This is a companion fact, relied on silently whenever this book (or the reader) writes
⟨x.fst, x.snd⟩ = x or splits a goal about a structure-typed equality
into one goal per field. Lean’s kernel treats a term x : S (for S a
structure) as definitionally equal to
S.mk x.field1 x.field2 .... Rebuilding x field-by-field gives back
the same term, by η (eta) for structures, not just a term that is
provably equal to it. This is exactly what makes Chapter 8’s Mat2.mk.injEq-based
extensionality reasoning work, and Chapter 10’s congr 1, which splits a
DirectSum.mk _ _ = DirectSum.mk _ _ goal into two field-wise goals. Both
depend on Lean already knowing, definitionally, that every term of a
structure type is (eta-equal to) its constructor applied to its own
fields.
Read more: TPiL’s chapter on structures discusses eta for structures directly; the “Why bundle proofs with data at all?” discussion in Chapter 6, Section 6 is the payoff this definitional transparency is building toward.
References
Full citations in the Bibliography. Formal definitions are gathered in Recall, above.
- Pierce (Pierce2002), Ch. 3, 11–12 — operational semantics and reduction, and the general distinction between checking equality by computation versus by proof that this section specializes to Lean’s
rfl/=. - Martin-Löf (MartinLof1984) — the original source distinguishing definitional (judgmental) equality from propositional equality, the exact distinction this section works through.
- Theorem Proving in Lean 4 (TPIL4), “Dependent Types” — Lean’s own documentation on
rfland definitional equality, matching the presentation here.
| ← Typing rules and safety | Index | Next: Exercises → |