Lean 语言参考手册

19.4. 命题等式🔗

命题等式是允许将两个项相等表述为命题的运算符。 定义等价会在必要时自动检查。 因此,为了使检查算法快速且易于理解,其表达能力受到限制。 另一方面,命题等式必须显式证明并显式使用——Lean 检查证明的有效性,而不是判断陈述是否为真。 作为交换,它的表达能力强得多:许多项在命题上相等,却不定义等价。

命题等式定义为归纳类型。 其唯一构造器 Eq.refl 要求等式两边的值相同;这隐含地使用了定义等价。 命题等式也可以看作模定义等价的最小自反关系。 除 Eq.refl 外,等式证明还由 propextQuot.sound 公理生成。

🔗归纳谓词
Eq.{u_1} {α : Sort u_1} : α α Prop
Eq.{u_1} {α : Sort u_1} : α α Prop

等式关系。它只有一条引入规则 Eq.refl。 使用 a = b 作为 Eq a b 的记法。 等式的一项基本性质是它构成等价关系。

variable (α : Type) (a b c d : α) variable (hab : a = b) (hcb : c = b) (hcd : c = d) example : a = d := Eq.trans (Eq.trans hab (Eq.symm hcb)) hcd

不过,等式远不只是一种等价关系。它还具有一项重要性质:每个断言都尊重这种等价,即可以替换相等的表达式而不改变真值。 也就是说,给定 h1 : a = bh2 : p a,可构造 p b 的证明,所用替换为 Eq.subst h1 h2。 示例:

example (α : Type) (a b : α) (p : α Prop) (h1 : a = b) (h2 : p a) : p b := Eq.subst h1 h2 example (α : Type) (a b : α) (p : α Prop) (h1 : a = b) (h2 : p a) : p b := h1 h2

第二种写法中的三角符号是建立在 Eq.substEq.symm 之上的宏,可输入 \t 得到它。 更多信息:等式

标识符中记法的约定:

  • 标识符中 = 的推荐拼写是 eq

Eq.refl.{u_1} {α : Sort u_1} (a : α) : a = a

Eq.refl a : a = a 是自反性,也是等式类型唯一的构造子。另见通常优先使用的 rfl

语法命题等式
term ::= ...
    | term = term

命题等式通常用中缀运算符 = 表示。

🔗定义
rfl.{u} {α : Sort u} {a : α} : a = a
rfl.{u} {α : Sort u} {a : α} : a = a

rfl : a = a 是等式类型唯一的构造子。它与 Eq.refl 相同,只不过隐式而非显式地接受 a

这一定理比初看上去更强,因为尽管其陈述是 a = a,Lean 也会接受与该类型定义相等的任何类型。例如,在 Lean 中,2 + 2 = 4 可用 rfl 证明,因为等式两边在定义等价意义下相同。

🔗定理
Eq.symm.{u} {α : Sort u} {a b : α} (h : a = b) : b = a
Eq.symm.{u} {α : Sort u} {a b : α} (h : a = b) : b = a

等式具有对称性:若 a = b,则 b = a

因为它位于 Eq 命名空间中,若有变量 h : a = b,则可用 h.symm 作为 Eq.symm h 的简写来证明 b = a

更多信息:等式

🔗定理
Eq.trans.{u} {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b = c) : a = c
Eq.trans.{u} {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b = c) : a = c

等式具有传递性:若 a = bb = c,则 a = c

因为它位于 Eq 命名空间中,若有变量或表达式 h₁ : a = bh₂ : b = c,则可用 h₁.trans h₂ : a = c 作为 Eq.trans h₁ h₂ 的简写。

更多信息:等式

🔗定理
Eq.subst.{u} {α : Sort u} {motive : α Prop} {a b : α} (h₁ : a = b) (h₂ : motive a) : motive b
Eq.subst.{u} {α : Sort u} {motive : α Prop} {a b : α} (h₁ : a = b) (h₂ : motive a) : motive b

等式的替换原理。若 a = bP a 成立,则 P b 也成立。这里依惯例用名称 motive 表示 P;若无法正确推断它,可用例如 Eq.subst (motive := fun x => x < 5) 显式指定。

这一定理是 rw 策略的底层机制;该策略本质上是一种精巧算法,用于寻找合适的 motive 实参,从而有效应用本定理,将目标或假设中出现的 a 替换为 b

更多信息:等式

🔗定义
cast.{u} {α β : Sort u} (h : α = β) (a : α) : β
cast.{u} {α β : Sort u} (h : α = β) (a : α) : β

沿类型等式进行强制转换。若 h : α = β 是类型等式且 a : α,则直接写 a : β 通常无法通过类型检查;此函数可绕过这一限制,把 a 嵌入类型 β,写作 cast h a : β

最好尽可能避免使用此函数,因为含有强制转换的项更难推理;但当类型并非定义相等时,有时没有更好的做法。

更多信息:等式

🔗定理
congr.{u, v} {α : Sort u} {β : Sort v} {f₁ f₂ : α β} {a₁ a₂ : α} (h₁ : f₁ = f₂) (h₂ : a₁ = a₂) : f₁ a₁ = f₂ a₂
congr.{u, v} {α : Sort u} {β : Sort v} {f₁ f₂ : α β} {a₁ a₂ : α} (h₁ : f₁ = f₂) (h₂ : a₁ = a₂) : f₁ a₁ = f₂ a₂

函数与实参两方面的同余性。若 f₁ = f₂a₁ = a₂,则 f₁ a₁ = f₂ a₂。这仅适用于非依赖函数;在依赖情形下,定理陈述更为复杂。

更多信息:等式

🔗定理
congrFun.{u, v} {α : Sort u} {β : α Sort v} {f g : (x : α) β x} (h : f = g) (a : α) : f a = g a
congrFun.{u, v} {α : Sort u} {β : α Sort v} {f g : (x : α) β x} (h : f = g) (a : α) : f a = g a

函数应用中函数部分的同余性:若 f = g,则 f a = g a

🔗定理
congrArg.{u, v} {α : Sort u} {β : Sort v} {a₁ a₂ : α} (f : α β) (h : a₁ = a₂) : f a₁ = f a₂
congrArg.{u, v} {α : Sort u} {β : Sort v} {a₁ a₂ : α} (f : α β) (h : a₁ = a₂) : f a₁ = f a₂

函数实参的同余性:若 a₁ = a₂,则 f a₁ = f a₂,其中 f 为任意非依赖函数。这比初看上去更强,因为还可以用 lambda 表达式作为 f,证明 <something containing a₁> = <something containing a₂>congrsimp 等策略在子项内部应用等式时,会在内部使用此函数。

更多信息:等式

🔗定义
Eq.mp.{u} {α β : Sort u} (h : α = β) (a : α) : β
Eq.mp.{u} {α β : Sort u} (h : α = β) (a : α) : β

h : α = β 是类型等式的证明,则 h.mp : α β 是由它诱导的“强制转换”运算,把 α 的元素映射到 β 的元素。

可对 h 归纳来证明结果元素的定理,因为 rfl.mp 定义上就是恒等函数。

🔗定义
Eq.mpr.{u} {α β : Sort u} (h : α = β) (b : β) : α
Eq.mpr.{u} {α β : Sort u} (h : α = β) (b : β) : α

h : α = β 是类型等式的证明,则 h.mpr : β α 是由它诱导的反向“强制转换”运算,把 β 的元素映射到 α 的元素。

可对 h 归纳来证明结果元素的定理,因为 rfl.mpr 定义上就是恒等函数。

语法强制转换
term ::= ...
    | term  term

当项的类型包含等式一侧作为子项时,可以使用 运算符进行重写。 如果等式两侧都出现在项的类型中,则将左侧重写为右侧。

19.4.1. 等式证明的唯一性🔗

由于定义证明无关性,命题等式证明是唯一的:两个数学对象不可能以不同方式相等。

theorem Eq.unique {α : Sort u} (x y : α) (p1 p2 : x = y) : p1 = p2 := α:Sort ux:αy:αp1:x = yp2:x = yp1 = p2 All goals completed! 🐙

Streicher 的 K 公理 (Streicher, 1993)Thomas Streicher, 1993. Investigations into Intensional Type Theory. Habilitation, Ludwig-Maximilians-Universität München及其计算规则同样是定义证明无关性的结果。 K 公理是与 Eq.unique 逻辑等价的原则,实现为命题等式的另一种递归器

def K {α : Sort u} {motive : {x : α} x = x Sort v} (d : {x : α} motive (Eq.refl x)) (x : α) (z : x = x) : motive z := d example {α : Sort u} {a : α} {motive : {x : α} x = x Sort u} {d : {x : α} motive (Eq.refl x)} : K (motive := motive) d a rfl = d := α:Sort ua:αmotive:{x : α} x = x Sort ud:{x : α} motive K (fun {x} => d) a = d All goals completed! 🐙

19.4.2. 异构等式🔗

异构等式命题等式的一种形式,不要求等式两项具有相同类型。 不过,使用它的 rfl 版本证明两项相等时,仍要求类型和项都定义等价。 换言之,它允许表述更多陈述。

异构等式在实践中通常不如普通命题等式方便。 不要求等式两侧类型相同所带来的灵活性,也意味着它有更少的有用性质。 它常因依赖模式匹配而出现:当准确反映相应控制流所需的普通等式假设不满足类型要求时,split 策略和函数归纳会向上下文加入异构等式假设。 在这些情况下,内置自动化只能使用异构等式。

🔗归纳谓词
HEq.{u} {α : Sort u} : α {β : Sort u} β Prop
HEq.{u} {α : Sort u} : α {β : Sort u} β Prop

异构等式。a b 断言 ab 具有相同类型,并且沿该等式强制转换 a 会得到 b,反之亦然。

应尽可能避免使用此类型。异构等式不具有 Eq 的全部性质,因为仅假定 ab 的类型相等,通常不足以证明所需定理。一个重要的公知反例是 congr 的类似命题:若 f gx y,并且 f xg y 都类型正确,也不能推出 f x g y。(若改为 f = g,则可以推出。)不过,若 ab 类型相同,则 a = ba b 等价。

标识符中记法的约定:

  • 标识符中 的推荐拼写是 heq

HEq.refl.{u} {α : Sort u} (a : α) : a  a

异构等式的自反性。

语法异构等式
term ::= ...
    | term  term

异构等式 HEq x y 可写作 x y

🔗定义
HEq.rfl.{u} {α : Sort u} {a : α} : a a
HEq.rfl.{u} {α : Sort u} {a : α} : a a

隐式接受实参的 HEq.refl 版本。

异构等式

类型 Vector α nArray α 的包装器,其中包含数组大小为 n 的证明。 Vector 的追加满足结合律,但无法直接用普通命题等式表述这一事实:

variable {xs : Vector α l₁} {ys : Vector α l₂} {zs : Vector α l₃} set_option linter.unusedVariables false theorem Vector.append_associative : xs ++ (ys ++ zs) = Type mismatch xs ++ ys ++ zs has type Vector α (l₁ + l₂ + l₃) but is expected to have type Vector α (l₁ + (l₂ + l₃))(xs ++ ys) ++ zs := sorry All goals completed! 🐙

问题在于自然数加法的结合律在命题上成立,但不定义等价:

Type mismatch
  xs ++ ys ++ zs
has type
  Vector α (l₁ + l₂ + l₃)
but is expected to have type
  Vector α (l₁ + (l₂ + l₃))

一种解决方案是在陈述中使用自然数加法的结合律:

theorem declaration uses `sorry`Vector.append_associative' : xs ++ (ys ++ zs) = Nat.add_assoc _ _ _ ((xs ++ ys) ++ zs) := α:Type ul₁:Natl₂:Natl₃:Natxs:Vector α l₁ys:Vector α l₂zs:Vector α l₃xs ++ (ys ++ zs) = (xs ++ ys ++ zs) All goals completed! 🐙

不过,在某些情况下,这样的证明陈述很难处理。

另一种方案是使用异构等式:

theorem declaration uses `sorry`Vector.append_associative : HEq (xs ++ (ys ++ zs)) ((xs ++ ys) ++ zs) := α:Type ul₁:Natl₂:Natl₃:Natxs:Vector α l₁ys:Vector α l₂zs:Vector α l₃xs ++ (ys ++ zs) xs ++ ys ++ zs All goals completed! 🐙

此时,简化器可以重写等式两侧,而无需保持它们的类型。 不过,证明该定理最终仍需证明长度相匹配。

theorem Vector.append_associative : HEq (xs ++ (ys ++ zs)) ((xs ++ ys) ++ zs) := α:Type ul₁:Natl₂:Natl₃:Natxs:Vector α l₁ys:Vector α l₂zs:Vector α l₃xs ++ (ys ++ zs) xs ++ ys ++ zs α:Type ul₁:Natl₂:Natl₃:Natys:Vector α l₂zs:Vector α l₃toArray✝:Array αsize_toArray✝:toArray✝.size = l₁mk toArray✝ size_toArray✝ ++ (ys ++ zs) mk toArray✝ size_toArray✝ ++ ys ++ zs; α:Type ul₁:Natl₂:Natl₃:Natzs:Vector α l₃toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₁toArray✝:Array αsize_toArray✝:toArray✝.size = l₂mk toArray✝¹ size_toArray✝¹ ++ (mk toArray✝ size_toArray✝ ++ zs) mk toArray✝¹ size_toArray✝¹ ++ mk toArray✝ size_toArray✝ ++ zs; α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃mk toArray✝² size_toArray✝² ++ (mk toArray✝¹ size_toArray✝¹ ++ mk toArray✝ size_toArray✝) mk toArray✝² size_toArray✝² ++ mk toArray✝¹ size_toArray✝¹ ++ mk toArray✝ size_toArray✝ α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃mk (toArray✝² ++ (toArray✝¹ ++ toArray✝)) mk (toArray✝² ++ (toArray✝¹ ++ toArray✝)) α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃l₁ + (l₂ + l₃) = l₁ + l₂ + l₃α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃ α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃l₁ + (l₂ + l₃) = l₁ + l₂ + l₃ All goals completed! 🐙 α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃ α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃ = α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃((toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃)) = ((toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃) α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃ = All goals completed! 🐙 α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃((toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃)) = ((toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃) α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) (toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃ α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) (toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃ (toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) (toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃ (toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃h:(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃h:(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃)(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃α:Type ul₁:Natl₂:Natl₃:NattoArray✝²:Array αsize_toArray✝²:toArray✝.size = l₁toArray✝¹:Array αsize_toArray✝¹:toArray✝.size = l₂toArray✝:Array αsize_toArray✝:toArray✝.size = l₃h:(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + l₂ + l₃(toArray✝² ++ (toArray✝¹ ++ toArray✝)).size = l₁ + (l₂ + l₃) All goals completed! 🐙
🔗定义
HEq.elim.{u, v} {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : a b) (h₂ : p a) : p b
HEq.elim.{u, v} {α : Sort u} {a : α} {p : α Sort v} {b : α} (h₁ : a b) (h₂ : p a) : p b

HEq.ndrec 的变体。

🔗定义
HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} (m : motive a) {β : Sort u2} {b : β} (h : a b) : motive b
HEq.ndrec.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} (m : motive a) {β : Sort u2} {b : β} (h : a b) : motive b

HEq 的非依赖递归器。

🔗定义
HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : a b) (m : motive a) : motive b
HEq.ndrecOn.{u1, u2} {α : Sort u2} {a : α} {motive : {β : Sort u2} β Sort u1} {β : Sort u2} {b : β} (h : a b) (m : motive a) : motive b

HEq.ndrec 的变体。

🔗定理
HEq.subst.{u} {α β : Sort u} {a : α} {b : β} {p : (T : Sort u) T Prop} (h₁ : a b) (h₂ : p α a) : p β b
HEq.subst.{u} {α β : Sort u} {a : α} {b : β} {p : (T : Sort u) T Prop} (h₁ : a b) (h₂ : p α a) : p β b

使用异构等式进行替换。

🔗定理
eq_of_heq.{u} {α : Sort u} {a a' : α} (h : a a') : a = a'
eq_of_heq.{u} {α : Sort u} {a a' : α} (h : a a') : a = a'

若两个异构相等的项具有相同类型,则它们在命题上相等。

🔗定理
heq_of_eq.{u_1} {α✝ : Sort u_1} {a a' : α✝} (h : a = a') : a a'
heq_of_eq.{u_1} {α✝ : Sort u_1} {a a' : α✝} (h : a = a') : a a'

命题上相等的项也异构相等。

🔗定理
heq_of_eqRec_eq.{u} {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : h₁ a = b) : a b
heq_of_eqRec_eq.{u} {α β : Sort u} {a : α} {b : β} (h₁ : α = β) (h₂ : h₁ a = b) : a b

若使用 Eq.rec 把一项强制转换到另一类型后,它等于另一个项,则这两项异构相等。

🔗定理
eqRec_heq.{u, v} {α : Sort u} {φ : α Sort v} {a a' : α} (h : a = a') (p : φ a) : Eq.recOn h p p
eqRec_heq.{u, v} {α : Sort u} {φ : α Sort v} {a a' : α} (h : a = a') (p : φ a) : Eq.recOn h p p

φ 内使用 Eq.recOn 重写所得的项,与原项异构相等。

🔗定理
cast_heq.{u} {α β : Sort u} (h : α = β) (a : α) : cast h a a
cast_heq.{u} {α β : Sort u} (h : α = β) (a : α) : cast h a a

使用 cast 强制转换一项所得的结果,与原项异构相等。

🔗定理
heq_of_heq_of_eq.{u} {α β : Sort u} {a : α} {b b' : β} (h₁ : a b) (h₂ : b = b') : a b'
heq_of_heq_of_eq.{u} {α β : Sort u} {a : α} {b b' : β} (h₁ : a b) (h₂ : b = b') : a b'

异构等式可在前面复合命题等式。

🔗定理
type_eq_of_heq.{u} {α β : Sort u} {a : α} {b : β} (h : a b) : α = β
type_eq_of_heq.{u} {α β : Sort u} {a : α} {b : β} (h : a b) : α = β

若两项异构相等,则它们的类型在命题上相等。