term ::= ...
| term × term
积 Prod α β 写作 α × β。
Lean 标准库包含多种类似元组的类型。 在实践中,它们有四个方面的差异:
第一投影是类型还是命题
第二投影是类型还是命题
第二投影的类型是否依赖于第一投影的值
整个类型本身是命题还是类型
类型 | 第一投影 | 第二投影 | 依值? | 宇宙 |
|---|---|---|---|---|
|
| ❌️ |
| |
|
| ❌️ |
| |
|
| ✔ |
| |
|
| ✔ |
| |
|
| ✔ |
|
该表中的某些潜在行在库里并不存在:
不存在“第一投影是命题”的依值有序对,因为 证明无关性 会让它失去意义。
不存在把类型与命题组合起来的非依值有序对,因为这种情况在实践中很少见:把数据与无关的证明放在一起并不常见。
这些差异会带来非常不同的使用场景。
Prod 及其变体 PProd 与 MProd 只是把数据放在一起——它们是积。
由于第二投影依值,Sigma 具有和的特征:对于第一投影类型中的每个元素,第二投影都可能对应不同的类型。
Subtype 选出某个类型中满足给定谓词的值。
尽管它在语法上像一个有序对,但在实践中它被当作真正的子集。
And 是逻辑联结词,而 Exists 是量词。
本章记录的是这些类似元组的有序对,也就是 Prod 与 Sigma。
类型 α × β 是 Prod α β 的一种 记法,它包含有序对:第一个元素属于 α,第二个元素属于 β。
这些有序对写在圆括号中,并以逗号分隔。
更大的元组表示为嵌套元组,因此 α × β × γ 等价于 α × (β × γ),而 (x, y, z) 等价于 (x, (y, z))。
term ::= ...
| ([anonymous]term, term)
还存在变体 α ×' β(它是 PProd α β 的记法)以及 MProd,它们在 宇宙 层级方面有所不同:与 PSum 类似,PProd 允许 α 或 β 之一是命题,而 MProd 要求二者都是位于同一宇宙层级的类型。
一般来说,PProd 主要用于证明自动化和精译器的实现,因为它往往会引发无法解决的宇宙层级合一问题。
另一方面,MProd 在某些高级用例中可以简化宇宙层级问题。
作为单纯的有序对,Prod 的主要 API 由模式匹配以及第一、第二投影 Prod.fst 和 Prod.snd 提供。
Prod.map.{u₁, u₂, v₁, v₂} {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂ : Type v₂} (f : α₁ → α₂) (g : β₁ → β₂) : α₁ × β₁ → α₂ × β₂Prod.map.{u₁, u₂, v₁, v₂} {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂ : Type v₂} (f : α₁ → α₂) (g : β₁ → β₂) : α₁ × β₁ → α₂ × β₂
产品的字典顺序。
如果两个对的第一个元素是有序的,或者如果它们的第一个元素相等并且它们的第二个元素是有序的,则两个对按字典顺序排序。
依值有序对 也称为 依值和 或 Σ-类型, 是这样一种有序对:第二个项的类型可以依赖于第一个项的值。
它与存在量词以及 Subtype 关系密切。
不同于存在量化语句,依值有序对位于 Type 宇宙中,是与计算相关的数据。
不同于子类型,这里的第二个项也同样是与计算相关的数据。
与普通有序对一样,依值有序对也可以嵌套;这种嵌套是右结合的。
term ::= ...
| (ident : term) × termterm ::= ... | Σidentident* (: term)?, term
term ::= ... | Σ (identident* : term), term
依值有序对类型会绑定一个或多个变量,最终项中可以使用这些变量。
若只绑定一个变量,则它的类型就是有序对第一个元素的类型,而最终项则是第二个元素的类型。
若绑定多个变量,则类型会按右结合方式嵌套。
标识符也可以写成 _。
带括号的写法允许多个被绑定变量具有不同类型,而不带括号的写法要求它们都具有相同类型。
类型
Σ n k : Nat, Fin (n * k)等价于
Σ n : Nat, Σ k : Nat, Fin (n * k)以及
(n : Nat) × (k : Nat) × Fin (n * k)类型
Σ (n k : Nat) (i : Fin (n * k)) , Fin i.val等价于
Σ (n : Nat), Σ (k : Nat), Σ (i : Fin (n * k)) , Fin i.val以及
(n : Nat) × (k : Nat) × (i : Fin (n * k)) × Fin i.val
这两种标注风格不能在同一个 «termΣ_,_» : termΣ 类型中混用:
Σ n ki : Fin (n * k)) , Fin i.val依值有序对通常有两种用法:
它们可用于把某个具体的类型索引与该索引族中的值“打包”在一起,适用于事先不知道索引值的情况。
类型 Σ n, Fin n 就是一对值:一个自然数,以及另一个严格小于它的数。
这是依值有序对最常见的用法。
类型 Vector 会把一个已知长度与数组关联起来,它可以与该长度本身一起放入依值有序对中。
尽管从逻辑上说,这与直接使用 Array 等价,但为了弥补 API 之间的衔接空缺,这种构造有时是必要的。
def getNLinesRev : (n : Nat) → IO (Vector String n)
| 0 => pure #v[]
| n + 1 => do
let xs ← getNLinesRev n
return xs.push (← (← IO.getStdin).getLine)
def getNLines (n : Nat) : IO (Vector String n) := do
return (← getNLinesRev n).reverse
partial def getValues : IO (Σ n, Vector String n) := do
let stdin ← IO.getStdin
IO.println "How many lines to read?"
let howMany ← stdin.getLine
if let some howMany := howMany.trimAscii.copy.toNat? then
return ⟨howMany, (← getNLines howMany)⟩
else
IO.eprintln "Please enter a number."
getValues
def main : IO Unit := do
let values ← getValues
IO.println s!"Got {values.fst} values. They are:"
for x in values.snd do
IO.println x.trimAscii
向该程序提供如下标准输入时:
stdin4ApplesQuincePlumsRaspberries输出为:
stdoutHow many lines to read?Got 4 values. They are:RaspberriesPlumsQuinceApples
Sigma 可用于实现和类型。
第一投影中的 Bool 指示 Sum' 的第二投影值来自哪个类型。
def Sum' (α : Type) (β : Type) : Type :=
Σ (b : Bool),
match b with
| true => α
| false => β
两个注入构造子都会把一个标签(即 Bool)与指定类型的值配对。
为它们加上 match_pattern 标注后,它们既可用于普通项,也可用于模式。
variable {α β : Type}
@[match_pattern]
def Sum'.inl (x : α) : Sum' α β := ⟨true, x⟩
@[match_pattern]
def Sum'.inr (x : β) : Sum' α β := ⟨false, x⟩
def Sum'.swap : Sum' α β → Sum' β α
| .inl x => .inr x
| .inr y => .inl y
正如 Prod 有允许命题与类型一并出现的变体 PProd 一样,PSigma 也允许其投影是命题。
它与 PProd 有相同的缺点:更容易导致宇宙层级合一失败。
不过,在实现自定义证明自动化,或某些罕见的高级用例中,PSigma 可能是必要的。
term ::= ... | Σ'identident* (: term)? , term
term ::= ... | Σ' (identident* : term), term
Σ' 的嵌套规则以及其绑定结构规则,都与 «termΣ_,_» : termΣ 相同。
完全宇宙多态依赖对,其中第二个元素的类型取决于第一个元素的值,并且两种类型都允许是命题。类型 PSigma β 通常写作 Σ' a : α, β a 或 (a : α) ×' β a。
在实践中,这种通用性导致宇宙级约束难以解决,因此 PSigma 很少在手动编写的代码中使用。它通常仅用于构造任意类型对的自动化。
要将值与谓词对其成立的证明配对,请使用 Subtype。要证明存在满足谓词的值,请使用 Exists。由于证明无关,以命题作为其第一个组成部分的依赖对通常没有用处:依赖于特定证明是没有意义的,因为无论如何所有证明都是相等的。
构造子
PSigma.mk.{u, v}
构造完全宇宙多态的依值有序对。