h5i

Chapter 9

Equality Is an Inductive Type Too

To state facts about numbers, such as 0+n=n0 + n = n, we need a way to express equality. Although it might seem to require special rules built into the kernel, equality is another inductive type generated by the recipe of Chapter 8. Its recursor lets us turn a fact about aa into a fact about bb. This chapter declares the type, explains how it differs from definitional equality, and proves the book's first substantial theorem.

The declaration

In Lean, equality is declared as

inductive Eq {A : Sort u} (a : A) : A -> Prop where
  | refl : Eq a a

For a,b:Aa, b : A, the type Equ  A  a  b\Eq{u}\;A\;a\;b is the proposition that aa and bb are equal; when AA is Nat\Nat we write it a=ba = b. In the terms of Chapter 8, AA and aa are parameters and bb is an index:

Equ:Π(A:Sort u). A→A→Sort 0,reflu:Π(A:Sort u). Π(a:A). Equ  A  a  a.\Eq{u} : \tPi{A}{\Sort{u}}{A \to A \to \Sort{0}}, \qquad \refl{u} : \tPi{A}{\Sort{u}}{\tPi{a}{A}{\Eq{u}\;A\;a\;a}}.

The only constructor reflu\refl{u} has no fields, and its index term is aa: it can only prove equations whose two sides are the same aa. So Eq\Eq{} is an inductive family like Vec\mathsf{Vec}, not a single type. The left side is fixed by the parameter; the right side varies, and the constructor pins it to the left side.

The recursor: transport

Informally, the recursor of Eq\Eq{} says: if a=ba = b, then whatever holds of aa holds of bb. This is Leibniz's principle, and in its most common form it reads

Eq.ndrecu,v:Π(A:Sort u). Π(a:A). Π(M:A→Sort v). M  a→Π(b:A). Equ  A  a  b→M  b.\EqRec{u,v} : \tPi{A}{\Sort{u}}{\tPi{a}{A}{\tPi{M}{A \to \Sort{v}}{M\;a \to \tPi{b}{A}{\Eq{u}\;A\;a\;b \to M\;b}}}}.

Given a property MM of elements of AA, evidence m:M  am : M\;a, and a proof hh that a=ba = b, it returns evidence of M  bM\;b. We say that it transports mm along hh. Its computation rule says that transporting along refl\refl{} does nothing:

Eq.ndrecu,v  A  a  M  m  b  (reflu  A  c)  →  m.\EqRec{u,v}\;A\;a\;M\;m\;b\;(\refl{u}\;A\;c) \;\to\; m.

The version the general recipe of Chapter 8 generates is slightly more general, and Eq.ndrec\EqRec{} is a definition built from it.

the full dependent recursor, as generated by the recipe

Because bb is an index, the motive takes bb and the proof h:Equ  A  a  bh : \Eq{u}\;A\;a\;b, and there is one minor premise, for reflu\refl{u}, with no fields and no hypotheses:

Eq.recu,v:Π(A:Sort u). Π(a:A). Π(M:(Π(b:A). Equ  A  a  b→Sort v)). M  a  (reflu  A  a)→Π(b:A). Π(h:Equ  A  a  b). M  b  hEq.recu,v  A  a  M  m  b  (reflu  A′  c)  →ι  m.\begin{array}{l} \EqRecDep{u,v} : \tPi{A}{\Sort{u}}{\tPi{a}{A}{\tPi{M}{(\tPi{b}{A}{\Eq{u}\;A\;a\;b \to \Sort{v}})}{{}}}} \\[2pt] \qquad\qquad M\;a\;(\refl{u}\;A\;a) \to \tPi{b}{A}{\tPi{h}{\Eq{u}\;A\;a\;b}{M\;b\;h}} \\[8pt] \EqRecDep{u,v}\;A\;a\;M\;m\;b\;(\refl{u}\;A'\;c) \;\iotared\; m. \end{array}

Eq\Eq{} is a subsingleton (one constructor, no fields), so vv is arbitrary: equality proofs can be used to build data, not only proofs. The non-dependent Eq.ndrecu,v\EqRec{u,v} is the special case whose motive ignores the proof:

Eq.ndrecu,v:=λ(A:Sort u). λ(a:A). λ(M:A→Sort v). λ(m:M  a). λ(b:A). λ(h:Equ  A  a  b). Eq.recu,v  A  a  (λ(b′:A). λ(_:Equ  A  a  b′). M  b′)  m  b  h.\begin{array}{l} \EqRec{u,v} \mathrel{:=} \tLam{A}{\Sort{u}}{\tLam{a}{A}{\tLam{M}{A \to \Sort{v}}{\tLam{m}{M\;a}{\tLam{b}{A}{\tLam{h}{\Eq{u}\;A\;a\;b}{{}}}}}}} \\[2pt] \qquad\qquad \EqRecDep{u,v}\;A\;a\;(\tLam{b'}{A}{\tLam{\_}{\Eq{u}\;A\;a\;b'}{M\;b'}})\;m\;b\;h. \end{array}

In a well-typed redex bb and cc are both convertible to aa, so the rules can ignore them. Figure 9.1 summarizes the constants.

Figure 9.1. The constants for equality and their computation rules. The rule for Eq.ndrecu,v\EqRec{u,v} follows from its definition by delta, beta, and the rule for Eq.recu,v\EqRecDep{u,v}.

Equ:Π(A:Sort u). A→A→Sort 0Lean: Eqreflu:Π(A:Sort u). Π(a:A). Equ  A  a  aEq.reflEq.recu,v:Π(A:Sort u). Π(a:A). Π(M:(Π(b:A). Equ  A  a  b→Sort v)). M  a  (reflu  A  a)→Π(b:A). Π(h:Equ  A  a  b). M  b  hEq.recEq.ndrecu,v:Π(A:Sort u). Π(a:A). Π(M:A→Sort v). M  a→Π(b:A). Equ  A  a  b→M  bEq.ndrec (definition)\begin{array}{lcll} \Eq{u} &:& \tPi{A}{\Sort{u}}{A \to A \to \Sort{0}} & \text{Lean: } \texttt{Eq} \\[2pt] \refl{u} &:& \tPi{A}{\Sort{u}}{\tPi{a}{A}{\Eq{u}\;A\;a\;a}} & \texttt{Eq.refl} \\[2pt] \EqRecDep{u,v} &:& \tPi{A}{\Sort{u}}{\tPi{a}{A}{\tPi{M}{(\tPi{b}{A}{\Eq{u}\;A\;a\;b \to \Sort{v}})}{M\;a\;(\refl{u}\;A\;a) \to \tPi{b}{A}{\tPi{h}{\Eq{u}\;A\;a\;b}{M\;b\;h}}}}} & \texttt{Eq.rec} \\[2pt] \EqRec{u,v} &:& \tPi{A}{\Sort{u}}{\tPi{a}{A}{\tPi{M}{A \to \Sort{v}}{M\;a \to \tPi{b}{A}{\Eq{u}\;A\;a\;b \to M\;b}}}} & \texttt{Eq.ndrec}\ \text{(definition)} \end{array}
Eq.recu,v  A  a  M  m  b  (reflu  A  c)→ιmEq.ndrecu,v  A  a  M  m  b  (reflu  A  c)→δβιm\begin{array}{rcl} \EqRecDep{u,v}\;A\;a\;M\;m\;b\;(\refl{u}\;A\;c) &\iotared& m \\[2pt] \EqRec{u,v}\;A\;a\;M\;m\;b\;(\refl{u}\;A\;c) &\to_{\delta\beta\iota}& m \end{array}

For example, suppose P:Nat→Sort 0P : \Nat \to \Sort{0}, a,b:Nata, b : \Nat, h:a=bh : a = b, and pa:P  a\mathit{pa} : P\;a. Choosing the motive M=PM = P gives

Eq.ndrec1,0  Nat  a  P  pa  b  h  :  P  b.\EqRec{1,0}\;\Nat\;a\;P\;\mathit{pa}\;b\;h \;:\; P\;b.

This one line is the logical content of rewriting: a fact about aa becomes a fact about bb. The rw tactic of Chapter 10 builds such terms automatically.

Definitional versus propositional equality

There are now two notions of equality, and keeping them apart is essential to understanding how a proof assistant behaves.

  • Definitional equality ≡\conv is decided by the kernel, by computation. It is a judgment about terms, not a type; no proof object is involved.
  • Propositional equality Eq\Eq{} is a type. Its proofs are terms, and finding one may require real reasoning, such as induction.

The constructor refl\refl{} connects them: by T-Conv, reflu  A  a:Equ  A  a  b\refl{u}\;A\;a : \Eq{u}\;A\;a\;b whenever a≡ba \conv b. So every definitional equality yields a propositional one for free. The converse fails, and the standard example is the two halves of n+0=nn + 0 = n and 0+n=n0 + n = n. With the definition of add\add from Chapter 7 and ss its step function,

n+0  →δβ  Nat.rec1  (λ(_:Nat). Nat)  n  s  zero  →ι  n,n + 0 \;\to_{\delta\beta}\; \NatRec{1}\;(\tLam{\_}{\Nat}{\Nat})\;n\;s\;\zero \;\iotared\; n,

so refl1  Nat  n:n+0=n\refl{1}\;\Nat\;n : n + 0 = n, and Lean proves this by rfl. In contrast,

0+n  →δβ  Nat.rec1  (λ(_:Nat). Nat)  0  s  n0 + n \;\to_{\delta\beta}\; \NatRec{1}\;(\tLam{\_}{\Nat}{\Nat})\;0\;s\;n

is stuck: nn is a variable, and the iota-rules fire only on zero\zero and succ\suc. So 0+n=n0 + n = n is not provable by refl\refl{} and needs induction.

The reduction explorer shows both computations with nn as a variable in the context. The first reaches nn; the second stops with the recursor waiting for a constructor it will never get.

def add : Nat -> Nat -> Nat := fun (m : Nat) => fun (n : Nat) => Nat.rec1 (fun (_n : Nat) => Nat) m (fun (k : Nat) => fun (r : Nat) => Nat.succ r) n
def add : Nat -> Nat -> Nat := fun (m : Nat) => fun (n : Nat) => Nat.rec1 (fun (_n : Nat) => Nat) m (fun (k : Nat) => fun (r : Nat) => Nat.succ r) n
def add : Nat -> Nat -> Nat := fun (m : Nat) => fun (n : Nat) => Nat.rec1 (fun (_n : Nat) => Nat) m (fun (k : Nat) => fun (r : Nat) => Nat.succ r) n

# n + 0 = n holds by computation: the recursor sees the constructor zero.
goal forall (n : Nat), Eq Nat (add n Nat.zero) n
intro n
rfl
qed

# 0 + n = n does not: the recursor is stuck on the variable n.
goal forall (n : Nat), Eq Nat (add Nat.zero n) n
intro n
rfl

Example: proving 0 + n = n

We prove Π(n:Nat). 0+n=n\tPi{n}{\Nat}{0 + n = n} by induction, using Nat.rec0\NatRec{0} with the motive M0=λ(n:Nat). 0+n=nM_0 = \tLam{n}{\Nat}{0 + n = n}.

Base. We need M0  0M_0\;0, which is convertible to 0+0=00 + 0 = 0. Since 0+00 + 0 computes to 00, the term refl1  Nat  0\refl{1}\;\Nat\;0 works.

Step. Given k:Natk : \Nat and ih:0+k=k\mathit{ih} : 0 + k = k, we need M0  (succ  k)M_0\;(\suc\;k), that is, 0+succ  k=succ  k0 + \suc\;k = \suc\;k. By the second iota-rule, 0+succ  k0 + \suc\;k computes to succ  (0+k)\suc\;(0 + k), so it suffices to prove succ  (0+k)=succ  k\suc\;(0 + k) = \suc\;k. We start from refl1  Nat  (succ  (0+k))\refl{1}\;\Nat\;(\suc\;(0 + k)), a proof of succ  (0+k)=succ  (0+k)\suc\;(0+k) = \suc\;(0+k), and transport it along ih\mathit{ih} with the motive N=λ(b:Nat). succ  (0+k)=succ  bN = \tLam{b}{\Nat}{\suc\;(0 + k) = \suc\;b}, which replaces the second 0+k0 + k by kk:

step=λ(k:Nat). λ(ih:0+k=k). Eq.ndrec1,0  Nat  (0+k)  N  (refl1  Nat  (succ  (0+k)))  k  ih.\mathit{step} = \tLam{k}{\Nat}{\tLam{\mathit{ih}}{0 + k = k}{ \EqRec{1,0}\;\Nat\;(0 + k)\;N\;(\refl{1}\;\Nat\;(\suc\;(0 + k)))\;k\;\mathit{ih}}}.

The proof is

zero_add=Nat.rec0  M0  (refl1  Nat  0)  step  :  Π(n:Nat). 0+n=n.\mathit{zero\_add} = \NatRec{0}\;M_0\;(\refl{1}\;\Nat\;0)\;\mathit{step} \;:\; \tPi{n}{\Nat}{0 + n = n}.

The kernel checks it with Algorithm 5.1. Each argument is checked against the corresponding part of the type of Nat.rec0\NatRec{0}, instantiated with M0M_0, and every comparison succeeds after normalization. Here is the complete term, submitted to the kernel in one piece. The non-dependent recursor is defined first, as in Figure 9.1.

def add : Nat -> Nat -> Nat := fun (m : Nat) => fun (n : Nat) => Nat.rec1 (fun (_n : Nat) => Nat) m (fun (k : Nat) => fun (r : Nat) => Nat.succ r) n

# Eq.ndrec at levels (1, 0): transport a proof of M a along a = b to M b.
def ndrec : forall (A : Sort 1), forall (a : A), forall (M : A -> Sort 0), M a -> forall (b : A), Eq A a b -> M b := fun (A : Sort 1) => fun (a : A) => fun (M : A -> Sort 0) => fun (m : M a) => fun (b : A) => fun (h : Eq A a b) => Eq.rec0 A a (fun (b' : A) => fun (_h : Eq A a b') => M b') m b h

goal forall (n : Nat), Eq Nat (add Nat.zero n) n
exact Nat.rec0 (fun (n : Nat) => Eq Nat (add Nat.zero n) n) (Eq.refl Nat Nat.zero) (fun (k : Nat) => fun (ih : Eq Nat (add Nat.zero k) k) => ndrec Nat (add Nat.zero k) (fun (b : Nat) => Eq Nat (Nat.succ (add Nat.zero k)) (Nat.succ b)) (Eq.refl Nat (Nat.succ (add Nat.zero k))) k ih)
qed

Writing such terms by hand is tedious, and finding the right motive is the hard part: the motive NN had to mention bb in exactly one of the two places where 0+k0 + k occurs. Tactics (Chapter 10) automate both the bookkeeping and the choice of motive.

Changes to the kernel

Little changed to get from propositional logic to arithmetic with equality. Infer\Infer gained a case for constants, which looks up the type in Σ\Sigma (T-Const). Check\Check is unchanged. Only Whnf\Whnf, and through it IsDefEq\IsDefEq, learned the new delta- and iota-steps. Chapter 12 shows how a single Whnf\Whnf handles every inductive type, and Chapter 14 discusses what inductive types change in the metatheory.

Exercises

Prove symmetry of equality: build a term of type Π(a:Nat). Π(b:Nat). a=b→b=a\tPi{a}{\Nat}{\tPi{b}{\Nat}{a = b \to b = a}} using Eq.ndrec\EqRec{}.

Solution

Transport the proof refl  a:a=a\refl{}\;a : a = a along h:a=bh : a = b with the motive λ(x:Nat). x=a\tLam{x}{\Nat}{x = a}, which turns a=aa = a into b=ab = a: λ(a:Nat). λ(b:Nat). λ(h:a=b). Eq.ndrec1,0  Nat  a  (λ(x:Nat). x=a)  (refl1  Nat  a)  b  h\tLam{a}{\Nat}{\tLam{b}{\Nat}{\tLam{h}{a = b}{\EqRec{1,0}\;\Nat\;a\;(\tLam{x}{\Nat}{x = a})\;(\refl{1}\;\Nat\;a)\;b\;h}}}.

goal forall (a : Nat), forall (b : Nat), Eq Nat a b -> Eq Nat b a
exact fun (a : Nat) => fun (b : Nat) => fun (h : Eq Nat a b) => ndrec Nat a (fun (x : Nat) => Eq Nat x a) (Eq.refl Nat a) b h
qed

(with ndrec defined as in the playground above).

Explain why Eq\Eq{} eliminates into every universe although it is a proposition, but a type Or with two constructors does not.

Solution

Eq\Eq{} is a subsingleton: one constructor with no fields. A function defined by its recursor cannot distinguish two proofs of a=ba = b, because there is only one way to build any such proof. Or has two constructors, so its recursor into Nat\Nat could return different numbers for the two injections, making the value of a program depend on which proof was supplied. Chapter 8 states the rule precisely.

What to remember

  • Eq\Eq{} is an inductive family with parameters AA and aa, index bb, and one constructor refl\refl{} with no fields.
  • Its recursor transports a fact about aa to a fact about bb along a proof of a=ba = b, and does nothing on refl\refl{}. This is rewriting.
  • Definitional equality is decided by computation; propositional equality is a type whose proofs may need induction. refl\refl{} turns the former into the latter.
  • n+0=nn + 0 = n is definitional and 0+n=n0 + n = n is not, because addition recurses on its second argument.