coq - sumbool 和 sum 的区别
问题描述
对于任何A B : Prop
,sum A B
和sumbool A B
是同构的, 通过以下,
Definition from_sumbool (A B : Prop) (x : sumbool A B) : sum A B :=
match x with
| left l => inl l
| right r => inr r
end.
Definition to_sumbool (A B : Prop) (x : sum A B) : sumbool A B :=
match x with
| inl l => left l
| inr r => right r
end.
那么为什么我们有sumbool
呢?似乎只是对 的限制sum
,其中A B
是Prop
而不是,Type
结果是Set
而不是Type
。
“bool”听起来像 sumbools 有 2 个元素。然而,这只是sumbool True True
. sumbool False False
并且sumbool False True
分别有 0 和 1 个元素。
同样,对于A B : Prop
的 OCaml 提取sum A B
比 的更详细sumbool A B
。我没有看到明确的原因:我们假设提取知道A
andB
的类型Prop
,因此它可以使用与本例相同的简化sumbool
。
通常,Coq 似乎定义了 3 次相同的函数:forType
和. 它对所有归纳类型 ( 和 ) 的归纳方案执行此操作。对于不相交的联合,我们有和。这使得要记住的功能增加了 3 倍。Set
Prop
_rect
_rec
_ind
sum
sumbool
or
解决方案
在某种程度上,我认为这只是sumbool
为了服务于与 不同的目的sum
,并且使用唯一的名称和符号来突出和记录这一事实。
Asum
只是一个通用的求和类型,但 asumbool
旨在用作类似布尔值的结果,其中“真”和“假”值带有证据。因此,当您看到类似以下的库函数时:
Definition le_lt_dec n m : {n <= m} + {m < n}.
很明显,这种定义的目的是构建一个类似布尔值的决策值,我们可以在计算中使用它,就像 一样leb : nat -> nat -> bool
,但在每个条件分支中也有可用的证据。
作为一个更实际的问题,该类型sumbool : Prop -> Prop -> Set
允许在编译/提取时擦除证据,这对于更通用的类型Prop
不会发生。sum
作为一个公认的愚蠢示例,如果我们有一个head
需要非零列表长度证据的函数:
Lemma nlt_0_r : forall n, ~(n < 0). Proof. intros n H. inversion H. Qed.
Definition head {A : Set} (l : list A) (E : 0 < length l) : A :=
match l return (0 < length l -> A) with
| x :: _ => fun _ => x
| nil => fun E1 => except (nlt_0_r _ E1)
end E.
我们想写一个head_with_default
定义,使用 a 可能很自然sumbool
:
Definition head_with_default {A : Set} (x : A) (l : list A) :=
match le_lt_dec (length l) 0 : {length l <= 0} + {0 < length l} with
| left _ => x
| right E => head l E
end.
我们也可以用普通sum
类型来写:
Definition le_lt_dec' (n m : nat) : (n <= m) + (m < n). Admitted.
Definition head_with_default' {A : Set} (x : A) (l : list A) :=
match le_lt_dec' (length l) 0 : (length l <= 0) + (0 < length l) with
| inl _ => x
| inr E => head l E
end.
如果我们提取这两个定义,我们可以看到证据已从sumbool
版本中删除,但仍保留在sum
版本中:
Extraction head_with_default.
(* let head_with_default x l = *)
(* match le_lt_dec (length l) O with *)
(* | Left -> x *)
(* | Right -> head l *)
Extraction head_with_default'.
(* let head_with_default' x l = *)
(* match le_lt_dec' (length l) O with *)
(* | Inl _ -> x *)
(* | Inr _ -> head l *).
更新: 跟进评论,请注意,这种提取差异并不是真正的“优化”。Coq 并没有看到——在这种特殊情况下—— Prop
s 中的 ssumbool
可以被优化掉,但sum
由于编译器不够智能,所以无法执行相同的优化。就是整个 Coq 逻辑都建立在这样的思想之上:在Prop
Universe 中,证明值可以并且将会被擦除,但在Set
Universe 中,“证明”值很重要,并且会在运行时反映出来。
进一步更新:现在,您可能会问(正如您在进一步的评论中所做的那样),为什么这不是提取级别的优化?为什么不在 Coq 中使用单个sum
类型,然后更改提取算法,以便它擦除在编译时已知为Prop
s 的所有类型。好吧,让我们试试吧。假设,使用上面的定义,我们写:
Inductive error := empty | missing.
Definition my_list := (inr 1 :: inr 2 :: inl missing :: inr 4 :: nil).
Definition sum_head := head_with_default' (inl empty) my_list.
提取看起来像这样:
type ('a, 'b) sum =
| Inl of 'a
| Inr of 'b
(** val my_list : (error, nat) sum list **)
let my_list = ...
(** val sum_head : (error, nat) sum **)
let sum_head =
head_with_default' (Inl Empty) my_list
现在,天真的提取head_with_default'
如上。如果我们想写出一个优化的版本,我们不能重用 type sum
,因为它的构造函数有错误的数量。我们需要sum
使用已擦除的 props 生成优化类型:
type sumP =
| InlP
| InrP
let head_with_default' x l =
match le_lt_dec' (length l) O with
| InlP -> x
| InrP -> head l
这工作正常。当然,如果有人试图创建 a nat + (x == 0)
,也称为 a sumor
:
Definition nat_or_zero (x : nat) : nat + (x = 0) :=
match x with
| O => inr eq_refl
| _ => inl x
end.
那么我们将需要该sum
类型的第三个版本:
type ('a) sumSP =
| InlSP of 'a
| InrSP
let nat_or_zero x = match x with
| O -> InrSP
| S _ -> InlSP x
sumPS
除非我们有充分的理由拒绝,否则我们将需要第四个版本(x==0) + nat
。
任何可能对sum
s 进行操作的函数,例如:
Fixpoint list_lefts {A B : Type } (l : list (A + B)) : list A :=
match l with
| nil => nil
| inr x :: l' => list_lefts l'
| inl x :: l' => x :: list_lefts l'
end.
还需要在多个版本中提取。至少对于A : Set
, 两者B : Set
和都B : Prop
可能有用:
(** val list_lefts : ('a1, 'a2) sum list -> 'a1 list **)
let rec list_lefts = function
| Nil -> Nil
| Cons (s, l') ->
(match s with
| Inl x -> Cons (x, (list_lefts l'))
| Inr _ -> list_lefts l')
(** val list_leftsSP : ('a1) sumSP list -> 'a1 list **)
let rec list_leftsSP = function
| Nil -> Nil
| Cons (s, l') ->
(match s with
| InlSP x -> Cons (x, (list_lefts l'))
| InrSP -> list_lefts l')
您可以争辩说其他两个没有用,但是如果有人不同意您的观点并试图申请list_lefts'
一个list ((x=0)+(x=1))
怎么办?显然,优化版本的第一次破解并不能消除__
:
(** val list_leftsP : sum' list -> __ list **)
let rec list_leftsP = function
| Nil -> Nil
| Cons (s, l') ->
(match s with
| InlP -> Cons (__, (list_lefts l'))
| InrP -> list_lefts l')
但这仅仅是因为我们没有提取优化版本list
:
type listP =
| NilP
| ConsP of listP
让我们写:
(** val list_leftsP : sumP list -> listP **)
let rec list_leftsP = function
| Nil -> NilP
| Cons (s, l') ->
(match s with
| InlP -> ConsP (list_leftsP l')
| InrP -> list_leftsP l')
这表明list_leftsP
(以及我遗漏的第四个变体)可能很有x=1
用,因为它执行计算给定的证明数量的非平凡计算l : list ((x=0) + (x=1))
。
现在我们准备定义:
Definition ugh {A B C D : Type} : A + B -> C + D ->
A*C + A*D + B*C + B*D := ...
并使用它的 16 个版本之一,如ughPPPS
,以及四个版本的子集prod
来表示其结果。但是,不清楚 ML 返回类型是否ughPPPS
应该是幼稚的:
(((prodP ('d prodPS) sum) prodP sum) ('d prodPS) sum)
无法删除无用的 type 条款prodP
,或者是否应该将其优化为:
(((('d prodPS) sumPS) sumSP) ('d prodPS) sum)
事实上,Coq 可以走这条路,归纳地追踪类型对Props
vs的依赖,Sets
并根据需要为程序中使用的所有变体生成多个提取。相反,它需要程序员在 Coq 级别决定哪些证明是重要的Set
(Prop
结果是提取将密切反映 Coq 类型,而不是成为fooPPSPPSP
优化变体的沙拉。(如果您尝试在任何非 Coq 代码中使用提取,这是一个很大的优势。)
推荐阅读
- python - 在没有列表的python中合并两个数字
- reactjs - React 测试库:fireEvent 更改不起作用
- c - 在 C 中,'int *' 是派生数据类型吗?
- java - Swing JFileChooser 只接受 ZIP 和目录
- reactjs - 以这种方式定义反应组件的原因是什么?
- azure - 工作人员使用 celery、redis 和 rabbitMQ 发回结果的 2 分钟延迟
- python - 将行向量添加到 3-D numpy 数组
- html - 将 flexbox 缩小到其子项的大小
- mongodb - 根据 MongoDB 中的条件获取下一个和上一个文档
- linux - 有什么办法可以让进程处于 D 状态或 Z 状态?