对于任何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
。
“布尔”听起来像sumbools有2个元素。但是,仅是这种情况sumbool True True
。sumbool False False
并sumbool False True
分别具有0和1个元素。
同样A B : Prop
,OCaml提取sum A B
比的更冗长sumbool A B
。我不认为应该是一个明确的理由:我们假设提取知道的类型A
和B
是Prop
,因此它可以使用相同的简化为sumbool
在这种情况下。
许多时候,好像Coq的定义相同功能的3倍:对Type
,Set
和Prop
。为此,它会为感应类型的所有感应方案(_rect
,_rec
和_ind
)。在这里,对于不交,我们有sum
,sumbool
和or
。这使要记住的功能增加了3倍。
在某种程度上,我认为这仅仅sumbool
是出于与目的不同的目的sum
,并且使用唯一的名称和符号来突出并记录这一事实。
Asum
只是一个通用的求和类型,但是asumbool
打算用作类似布尔值的结果,其中“ true”和“ false”值带有证据。因此,当您看到类似以下的库函数时:
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
定义,使用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 *).
更新:在发表评论之前,请注意,提取中的这种差异并不是真正的“优化”。它不是像勒柯克只见上面-在这种特殊情况下-对Prop
在Ssumbool
可以被优化掉,但随后未能在执行相同的优化sum
,因为编译器是不够聪明。这是因为整个Coq逻辑都是基于这样的思想:在Prop
宇宙中,证明值可以并且将被擦除,但是在Set
宇宙中,“证明”值很重要,并且将在运行时反映出来。
进一步的更新:现在,您很可能会问(就像您在进一步的评论中所做的那样),为什么这不是提取级别的优化?为什么不在sum
Coq中使用单个类型,然后更改提取算法,以使它擦除编译时已知为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
,因为它的构造函数具有错误的arity。我们需要sum
使用删除的道具生成一个优化的类型:
type sumP =
| InlP
| InrP
let head_with_default' x l =
match le_lt_dec' (length l) O with
| InlP -> x
| InrP -> head l
这样可以。当然,如果有人尝试创建nat + (x == 0)
,也称为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
,例如:
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)
无法删除无用的类型术语prodP
,或者是否应将其优化为:
(((('d prodPS) sumPS) sumSP) ('d prodPS) sum)
实际上,Coq可以走这条路线,归纳地跟踪类型对Props
vs的依赖关系,Sets
并根据需要为程序中使用的所有变体生成多个提取。取而代之的是,它要求程序员在Coq级别上确定哪些证明重要(Set
)或不重要(Prop
),并且-过于频繁-要求类型,构造函数和函数的多个变体来处理(某些)组合。结果是提取将与Coq类型密切相关,而不是fooPPSPPSP
优化变种的色拉。(如果您尝试在任何非Coq代码中使用提取,则是一个很大的优势。)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句