sumbool和sum之间的区别

时间:2018-09-01 20:39:46

标签: coq

对于任何A B : Propsum A Bsumbool 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 True的情况。 sumbool False Falsesumbool False True分别具有0和1个元素。

对于A B : Propsum A B的OCaml提取比sumbool A B的OCaml更冗长。我没有明确的原因:我们假设提取知道A的类型,而B的类型为Prop,因此可以使用与sumbool相同的简化形式在这种情况下。

通常,Coq定义了3次相同的功能:分别针对TypeSetProp。它适用于所有归纳类型(_rect_rec_ind)的归纳方案。对于不相交的联合,这里有sumsumboolor。这样可以记住的功能多了三倍。

1 个答案:

答案 0 :(得分:4)

在某种程度上,我认为sumbool的目的与sum的用途不同,并且使用唯一的名称和符号突出显示并记录了这一事实。

sum只是一个通用的求和类型,但是sumbool旨在用作类似布尔值的结果,其中“ 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 *).

更新:在发表评论之前,请注意,提取中的这种差异并不是真正的“优化”。就像Coq看到的那样-在这种特殊情况下-Prop中的sumbool s可以被优化掉了,但是却没有在sum中执行相同的优化,因为编译器没有还不够聪明。这是因为整个Coq逻辑都是基于这样的思想:在Prop Universe中,证明值可以并且将被删除,但是在Set Universe中,“证明”值很重要,并将在运行时反映出来。 / p>

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

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'的天真提取如上所述。如果我们想写出一个优化的版本,我们就不能重复使用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 : SetB : 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(以及我遗漏的第四个变体) 可能有用,因为它执行了计算{{ 1}}中的给定x=1

现在我们准备定义:

l : list ((x=0) + (x=1))

,并使用其16个版本之一(例如Definition ugh {A B C D : Type} : A + B -> C + D -> A*C + A*D + B*C + B*D := ... )和ughPPPS四个版本的子集来表示其结果。但是,尚不清楚prod的ML返回类型是否应该是幼稚的:

ughPPPS

无法删除类型为(((prodP ('d prodPS) sum) prodP sum) ('d prodPS) sum) 的无用术语,或者是否应将其优化为:

prodP

实际上,Coq可以走这条路线,归纳地跟踪类型对(((('d prodPS) sumPS) sumSP) ('d prodPS) sum) Props的依赖关系,并根据程序中使用的所有变体生成必要的多次提取。取而代之的是,它要求程序员在Coq级别上决定哪些证明很重要(Sets)或不重要(Set),并且-过于频繁-需要类型,构造函数和函数的多种变体处理(某些)组合。结果是,提取将紧密反映Coq类型,而不是优化变体的Prop色拉。 (如果您尝试在任何非Coq代码中使用提取,则是一个很大的优势。)

相关问题