sumbool和sum之间的区别

V.塞梅里亚

对于任何A B : Propsum 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 BProp代替Type,结果是Set不是Type

“布尔”听起来像sumbools有2个元素。但是,仅是这种情况sumbool True Truesumbool False Falsesumbool False True分别具有0和1个元素。

同样A B : Prop,OCaml提取sum A B比的更冗长sumbool A B我不认为应该是一个明确的理由:我们假设提取知道的类型ABProp,因此它可以使用相同的简化为sumbool在这种情况下。

许多时候,好像Coq的定义相同功能的3倍:对TypeSetProp为此,它会为感应类型的所有感应方案(_rect_rec_ind)。在这里,对于不交,我们有sumsumboolor这使要记住的功能增加了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宇宙中,“证明”值很重要,并且将在运行时反映出来。

进一步的更新:现在,您很可能会问(就像您在进一步的评论中所做的那样),为什么这不是提取级别的优化?为什么不在sumCoq中使用单个类型,然后更改提取算法,以使它擦除编译时已知为Props的所有类型好吧,让我们尝试一下。假设使用上面的定义,我们写:

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 : SetB : 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可以走这条路线,归纳地跟踪类型对Propsvs的依赖关系,Sets并根据需要为程序中使用的所有变体生成多个提取。取而代之的是,它要求程序员在Coq级别上确定哪些证明重要(Set)或不重要(Prop),并且-过于频繁-要求类型,构造函数和函数的多个变体来处理(某些)组合。结果是提取将与Coq类型密切相关,而不是fooPPSPPSP优化变种色拉。(如果您尝试在任何非Coq代码中使用提取,则是一个很大的优势。)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章