要陈述 0 + n = n 0 + n = n 0 + n = n 这样的数论事实,就需要表达相等。虽然相等似乎需要内核内置专门的规则,它其实是按第 8 章的方法生成的归纳类型;利用它的递归子,可以把关于 a a a 的事实转化为关于 b b b 的事实。本章声明这个类型,说明它与定义相等的区别,并证明本书第一个实质性的定理。
声明
在 Lean 中,相等的声明是
inductive Eq {A : Sort u} (a : A) : A -> Prop where
| refl : Eq a a
对 a , b : A a, b : A a , b : A ,类型 E q u A a b \Eq{u}\;A\;a\;b Eq u A a b 是“a a a 与 b b b 相等”这一命题;当 A A A 是 N a t \Nat Nat 时我们把它写作 a = b a = b a = b 。用第 8 章的术语说,A A A 和 a a a 是参数,b b b 是索引:
E q u : Π ( A : S o r t u ) . A → A → S o r t 0 , r e f l u : Π ( A : S o r t u ) . Π ( a : A ) . E q u 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}}. Eq u : Π ( A : Sort u ) . A → A → Sort 0 , refl u : Π ( A : Sort u ) . Π ( a : A ) . Eq u A a a .
唯一的构造子 r e f l u \refl{u} refl u 没有字段,它的索引项是 a a a :它只能证明两边是同一个 a a a 的等式。所以 E q \Eq{} Eq 像 V e c \mathsf{Vec} Vec 一样是归纳族,而不是单个类型。左边由参数固定;右边可变,而构造子把它钉在左边上。
为什么一个构造子就够了?因为这个声明并不定义 两个东西何时相等;它说的是相等的证明可以如何构造 ,而唯一的方式就是观察到某物等于它自己。其余一切,如对称性、传递性和替换,都由递归子导出,递归子说的是一旦有了这样的证明,能用它做什么。
递归子:传输
非形式地说,E q \Eq{} Eq 的递归子表示:若 a = b a = b a = b ,则凡对 a a a 成立者对 b b b 也成立。 这是莱布尼茨原理,其最常见的形式是
E q . n d r e c u , v : Π ( A : S o r t u ) . Π ( a : A ) . Π ( M : A → S o r t v ) . M a → Π ( b : A ) . E q u 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}}}}. Eq.ndrec u , v : Π ( A : Sort u ) . Π ( a : A ) . Π ( M : A → Sort v ) . M a → Π ( b : A ) . Eq u A a b → M b .
给定 A A A 的元素的一个性质 M M M 、证据 m : M a m : M\;a m : M a 和 a = b a = b a = b 的证明 h h h ,它返回 M b M\;b M b 的证据,我们说它沿着 h h h 传输 m m m 。它的计算规则说,沿着 r e f l \refl{} refl 传输什么也不做:
E q . n d r e c u , v A a M m b ( r e f l u A c ) → m . \EqRec{u,v}\;A\;a\;M\;m\;b\;(\refl{u}\;A\;c) \;\to\; m. Eq.ndrec u , v A a M m b ( refl u A c ) → m .
第 8 章的通用方法生成的版本略为一般,E q . n d r e c \EqRec{} Eq.ndrec 是由它构造出的一个定义。
由通用方法生成的完整依赖递归子
由于 b b b 是索引,motive 接受 b b b 以及 证明 h : E q u A a b h : \Eq{u}\;A\;a\;b h : Eq u A a b ,并且只有一个小前提,对应 r e f l u \refl{u} refl u ,没有字段也没有假设:
E q . r e c u , v : Π ( A : S o r t u ) . Π ( a : A ) . Π ( M : ( Π ( b : A ) . E q u A a b → S o r t v ) ) . M a ( r e f l u A a ) → Π ( b : A ) . Π ( h : E q u A a b ) . M b h E q . r e c u , v A a M m b ( r e f l u 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.rec u , v : Π ( A : Sort u ) . Π ( a : A ) . Π ( M : ( Π ( b : A ) . Eq u A a b → Sort v )) . M a ( refl u A a ) → Π ( b : A ) . Π ( h : Eq u A a b ) . M b h Eq.rec u , v A a M m b ( refl u A ′ c ) → ι m .
E q \Eq{} Eq 是子单元(一个构造子,没有字段),所以 v v v 是任意的:相等的证明可以用来构造数据,而不只是证明。非依赖的 E q . n d r e c u , v \EqRec{u,v} Eq.ndrec u , v 是 motive 忽略证明的特例:
E q . n d r e c u , v : = λ ( A : S o r t u ) . λ ( a : A ) . λ ( M : A → S o r t v ) . λ ( m : M a ) . λ ( b : A ) . λ ( h : E q u A a b ) . E q . r e c u , v A a ( λ ( b ′ : A ) . λ ( _ : E q u 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} Eq.ndrec u , v := λ ( A : Sort u ) . λ ( a : A ) . λ ( M : A → Sort v ) . λ ( m : M a ) . λ ( b : A ) . λ ( h : Eq u A a b ) . Eq.rec u , v A a ( λ ( b ′ : A ) . λ ( _ : Eq u A a b ′ ) . M b ′ ) m b h .
在良类型的可归约式中 b b b 和 c c c 都可转换为 a a a ,所以规则可以忽略它们。图 9.1 总结了这些常量。
图 9.1。相等的常量及其计算规则。E q . n d r e c u , v \EqRec{u,v} Eq.ndrec u , v 的规则由其定义经 δ、β 以及 E q . r e c u , v \EqRecDep{u,v} Eq.rec u , v 的规则推出。
E q u : Π ( A : S o r t u ) . A → A → S o r t 0 Lean: Eq r e f l u : Π ( A : S o r t u ) . Π ( a : A ) . E q u A a a Eq.refl E q . r e c u , v : Π ( A : S o r t u ) . Π ( a : A ) . Π ( M : ( Π ( b : A ) . E q u A a b → S o r t v ) ) . M a ( r e f l u A a ) → Π ( b : A ) . Π ( h : E q u A a b ) . M b h Eq.rec E q . n d r e c u , v : Π ( A : S o r t u ) . Π ( a : A ) . Π ( M : A → S o r t v ) . M a → Π ( b : A ) . E q u A a b → M b Eq.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 u refl u Eq.rec u , v Eq.ndrec u , v : : : : Π ( A : Sort u ) . A → A → Sort 0 Π ( A : Sort u ) . Π ( a : A ) . Eq u A a a Π ( A : Sort u ) . Π ( a : A ) . Π ( M : ( Π ( b : A ) . Eq u A a b → Sort v )) . M a ( refl u A a ) → Π ( b : A ) . Π ( h : Eq u A a b ) . M b h Π ( A : Sort u ) . Π ( a : A ) . Π ( M : A → Sort v ) . M a → Π ( b : A ) . Eq u A a b → M b Lean: Eq Eq.refl Eq.rec Eq.ndrec (definition)
E q . r e c u , v A a M m b ( r e f l u A c ) → ι m E q . n d r e c u , v A a M m b ( r e f l u 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} Eq.rec u , v A a M m b ( refl u A c ) Eq.ndrec u , v A a M m b ( refl u A c ) → ι → δ β ι m m
例如,设 P : N a t → S o r t 0 P : \Nat \to \Sort{0} P : Nat → Sort 0 ,a , b : N a t a, b : \Nat a , b : Nat ,h : a = b h : a = b h : a = b ,p a : P a \mathit{pa} : P\;a pa : P a 。取 motive M = P M = P M = P ,得到
E q . n d r e c 1 , 0 N a t a P p a b h : P b . \EqRec{1,0}\;\Nat\;a\;P\;\mathit{pa}\;b\;h \;:\; P\;b. Eq.ndrec 1 , 0 Nat a P pa b h : P b .
这一行就是重写 的逻辑内容:关于 a a a 的事实变成关于 b b b 的事实。第 10 章 的 rw 策略会自动构造这样的项。
在 Lean 中,Eq 的类型参数是隐式的 ,在声明中写作 {A},由繁饰器填入,所以用户写 Eq a b 或 a = b。我们的内核只看到完全显式的项,因此我们写出每一个参数;填入它们是内核之上那一层的工作。在脚本语言中,这些常量是 Eq、Eq.refl 以及依赖递归子 Eq.rec0 和 Eq.rec1;非依赖的 E q . n d r e c \EqRec{} Eq.ndrec 没有预先声明,但它只是一个两行的定义,见下面的演练场。由于没有宇宙多态,内核只在 A : S o r t 1 A : \Sort{1} A : Sort 1 处声明一次 Eq,这覆盖了 N a t \Nat Nat 和每个用户声明的数据类型;证明之间的相等则不可用。
定义相等与命题相等
现在有了两种相等概念,区分它们是理解证明助手行为的关键。
定义相等 ≡ \conv ≡ 由内核通过计算来判定。它是关于项的判断,不是类型;不涉及任何证明对象。
命题相等 E q \Eq{} Eq 是一个类型,它的证明是项,而找到一个证明可能需要真正的推理,例如归纳。
构造子 r e f l \refl{} refl 把二者联系起来:由 T-Conv,只要 a ≡ b a \conv b a ≡ b ,就有 r e f l u A a : E q u A a b \refl{u}\;A\;a : \Eq{u}\;A\;a\;b refl u A a : Eq u A a b 。所以每个定义相等都免费给出一个命题相等,但反之不成立,标准的例子是 n + 0 = n n + 0 = n n + 0 = n 和 0 + n = n 0 + n = n 0 + n = n 这两半。采用第 7 章 a d d \add add 的定义,并记 s s s 为它的步骤函数,
n + 0 → δ β N a t . r e c 1 ( λ ( _ : N a t ) . N a t ) n s z e r o → ι n , n + 0 \;\to_{\delta\beta}\; \NatRec{1}\;(\tLam{\_}{\Nat}{\Nat})\;n\;s\;\zero \;\iotared\; n, n + 0 → δ β Nat.rec 1 ( λ ( _ : Nat ) . Nat ) n s zero → ι n ,
所以 r e f l 1 N a t n : n + 0 = n \refl{1}\;\Nat\;n : n + 0 = n refl 1 Nat n : n + 0 = n ,Lean 用 rfl 证明它。相反,
0 + n → δ β N a t . r e c 1 ( λ ( _ : N a t ) . N a t ) 0 s n 0 + n \;\to_{\delta\beta}\; \NatRec{1}\;(\tLam{\_}{\Nat}{\Nat})\;0\;s\;n 0 + n → δ β Nat.rec 1 ( λ ( _ : Nat ) . Nat ) 0 s n
是卡住的 :n n n 是变量,而 ι 规则只在 z e r o \zero zero 和 s u c c \suc succ 上触发。所以 0 + n = n 0 + n = n 0 + n = n 不能用 r e f l \refl{} refl 证明,需要归纳。
归约浏览器展示了这两个计算,其中 n n n 是上下文中的变量。第一个到达 n n 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
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 由计算成立:递归子看到的是构造子 zero。
goal forall (n : Nat), Eq Nat (add n Nat.zero) n
intro n
rfl
qed
# 0 + n = n 则不然:递归子卡在变量 n 上。
goal forall (n : Nat), Eq Nat (add Nat.zero n) n
intro n
rfl
哪些等式由 rfl 成立,取决于函数是如何 定义的,而不是它计算什么。对第二个参数递归的加法使 n + 0 = n n + 0 = n n + 0 = n 成为定义相等、0 + n = n 0 + n = n 0 + n = n 成为命题相等;对第一个参数递归则会把二者对调。Lean 用户经常遇到这种情况:rfl 能否关闭目标,取决于相关定义中递归的方向。
示例:证明 0 + n = n
我们用归纳证明 Π ( n : N a t ) . 0 + n = n \tPi{n}{\Nat}{0 + n = n} Π ( n : Nat ) . 0 + n = n ,使用 N a t . r e c 0 \NatRec{0} Nat.rec 0 和 motive M 0 = λ ( n : N a t ) . 0 + n = n M_0 = \tLam{n}{\Nat}{0 + n = n} M 0 = λ ( n : Nat ) . 0 + n = n 。
基础情形。 我们需要 M 0 0 M_0\;0 M 0 0 ,它可转换为 0 + 0 = 0 0 + 0 = 0 0 + 0 = 0 。由于 0 + 0 0 + 0 0 + 0 计算为 0 0 0 ,项 r e f l 1 N a t 0 \refl{1}\;\Nat\;0 refl 1 Nat 0 即可。
归纳步骤。 给定 k : N a t k : \Nat k : Nat 和 i h : 0 + k = k \mathit{ih} : 0 + k = k ih : 0 + k = k ,我们需要 M 0 ( s u c c k ) M_0\;(\suc\;k) M 0 ( succ k ) ,即 0 + s u c c k = s u c c k 0 + \suc\;k = \suc\;k 0 + succ k = succ k 。由第二条 ι 规则,0 + s u c c k 0 + \suc\;k 0 + succ k 计算为 s u c c ( 0 + k ) \suc\;(0 + k) succ ( 0 + k ) ,所以只需证明 s u c c ( 0 + k ) = s u c c k \suc\;(0 + k) = \suc\;k succ ( 0 + k ) = succ k 。我们从 r e f l 1 N a t ( s u c c ( 0 + k ) ) \refl{1}\;\Nat\;(\suc\;(0 + k)) refl 1 Nat ( succ ( 0 + k )) 出发,它是 s u c c ( 0 + k ) = s u c c ( 0 + k ) \suc\;(0+k) = \suc\;(0+k) succ ( 0 + k ) = succ ( 0 + k ) 的证明,然后用 motive N = λ ( b : N a t ) . s u c c ( 0 + k ) = s u c c b N = \tLam{b}{\Nat}{\suc\;(0 + k) = \suc\;b} N = λ ( b : Nat ) . succ ( 0 + k ) = succ b 沿着 i h \mathit{ih} ih 传输它,这把第二个 0 + k 0 + k 0 + k 替换为 k k k :
s t e p = λ ( k : N a t ) . λ ( i h : 0 + k = k ) . E q . n d r e c 1 , 0 N a t ( 0 + k ) N ( r e f l 1 N a t ( s u c c ( 0 + k ) ) ) k i h . \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}}}. step = λ ( k : Nat ) . λ ( ih : 0 + k = k ) . Eq.ndrec 1 , 0 Nat ( 0 + k ) N ( refl 1 Nat ( succ ( 0 + k ))) k ih .
证明为
z e r o _ a d d = N a t . r e c 0 M 0 ( r e f l 1 N a t 0 ) s t e p : Π ( n : N a t ) . 0 + n = n . \mathit{zero\_add} = \NatRec{0}\;M_0\;(\refl{1}\;\Nat\;0)\;\mathit{step} \;:\; \tPi{n}{\Nat}{0 + n = n}. zero_add = Nat.rec 0 M 0 ( refl 1 Nat 0 ) step : Π ( n : Nat ) . 0 + n = n .
内核用算法 5.1 检查它:每个参数都对照 N a t . r e c 0 \NatRec{0} Nat.rec 0 的类型中以 M 0 M_0 M 0 实例化后的相应部分进行检查,每次比较都在规范化之后成功。下面是完整的项,一次性提交给内核,其中非依赖递归子先定义,如图 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
# 层级 (1, 0) 的 Eq.ndrec:沿着 a = b 把 M a 的证明传输到 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
手写这样的项很繁琐,而找到正确的 motive 才是难点:motive N N N 必须恰好在 0 + k 0 + k 0 + k 出现的两处之一提及 b b b 。策略(第 10 章 )把记账工作和 motive 的选择都自动化了。
内核的变化
从命题逻辑走到带相等的算术,内核改动很少:I n f e r \Infer Infer 增加了常量的情形,它在 Σ \Sigma Σ 中查找类型(T-Const);C h e c k \Check Check 没有变化;只有 W h n f \Whnf Whnf ,以及经由它的 I s D e f E q \IsDefEq IsDefEq ,学会了新的 δ 步骤和 ι 步骤。第 12 章 展示单个 W h n f \Whnf Whnf 如何处理每一种归纳类型,第 14 章 讨论归纳类型给元理论带来的变化。
练习
证明相等的对称性:用 E q . n d r e c \EqRec{} Eq.ndrec 构造一个类型为 Π ( a : N a t ) . Π ( b : N a t ) . a = b → b = a \tPi{a}{\Nat}{\tPi{b}{\Nat}{a = b \to b = a}} Π ( a : Nat ) . Π ( b : Nat ) . a = b → b = a 的项。
解答
用 motive λ ( x : N a t ) . x = a \tLam{x}{\Nat}{x = a} λ ( x : Nat ) . x = a 沿着 h : a = b h : a = b h : a = b 传输证明 r e f l a : a = a \refl{}\;a : a = a refl a : a = a ,这把 a = a a = a a = a 变成 b = a b = a b = a :
λ ( a : N a t ) . λ ( b : N a t ) . λ ( h : a = b ) . E q . n d r e c 1 , 0 N a t a ( λ ( x : N a t ) . x = a ) ( r e f l 1 N a t 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}}} λ ( a : Nat ) . λ ( b : Nat ) . λ ( h : a = b ) . Eq.ndrec 1 , 0 Nat a ( λ ( 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
(其中 ndrec 的定义同上面的演练场。)
解释为什么 E q \Eq{} Eq 虽是命题却能消去到每个宇宙,而有两个构造子的类型 Or 却不能。
解答
E q \Eq{} Eq 是子单元:一个构造子,没有字段,所以由其递归子定义的函数无法区分 a = b a = b a = b 的两个证明,因为构造任何这样的证明只有一种方式。Or 有两个构造子,所以它到 N a t \Nat Nat 的递归子可以对两个注入返回不同的数,使程序的值依赖于提供的是哪个证明。第 8 章精确地给出了这条规则。
要点回顾
E q \Eq{} Eq 是归纳族,参数为 A A A 和 a a a ,索引为 b b b ,只有一个没有字段的构造子 r e f l \refl{} refl 。
它的递归子沿着 a = b a = b a = b 的证明把关于 a a a 的事实传输为关于 b b b 的事实,而在 r e f l \refl{} refl 上什么也不做。这就是重写。
定义相等由计算判定;命题相等是一个类型,其证明可能需要归纳。r e f l \refl{} refl 把前者变成后者。
n + 0 = n n + 0 = n n + 0 = n 是定义相等,0 + n = n 0 + n = n 0 + n = n 不是,因为加法对第二个参数递归。