簡體   English   中英

coq Set或Type如何成為命題

[英]How can a coq Set or Type be a proposition

我正在閱讀關於Coq的教程。 它構造一個bool類型如下:

Coq < Inductive bool :  Set := true | false.
bool is defined
bool_rect is defined
bool_ind is defined
bool_rec is defined

然后它顯示了這些東西正在使用“檢查”。

Coq < Check bool_ind.
bool_ind
     : forall P : bool -> Prop, P true -> P false -> forall b : bool, P b

Coq < Check bool_rec.
bool_rec
     : forall P : bool -> Set, P true -> P false -> forall b : bool, P b

Coq < Check bool_rect.
bool_rect
     : forall P : bool -> Type, P true -> P false -> forall b : bool, P b

我理解bool_ind 它說,如果某些東西保持為true並且它保持為false ,那么它適用於bool所有b (因為那些是唯一的兩個)。

但我不明白bool_recbool_rect的表達是什么意思。 看來,如果P true (這是一Set用於bool_recTypebool_rect )被視為一個命題值。 我在這里錯過了什么?

你對bool_ind直覺bool_ind明顯,但是考慮為什么bool_ind意味着你所說的可能有助於澄清其他兩個。 我們知道

bool_ind : forall P : bool -> Prop,
             P true ->
             P false ->
             forall b : bool,
               P b

如果我們將其視為邏輯公式,我們會得到您所做的相同讀數:

  • 對於bool eans中的每個謂詞P
    • 如果P true ,則
    • 如果P false成立,那么
    • 對於每個布爾值b
      • P b成立。

但這不僅僅是一個邏輯公式,而是一種類型。 具體來說,它是(依賴)函數類型。 作為一個函數類型,它說(如果你允許我為未命名的參數和結果發明名稱的自由):

  • 給定值P : bool -> Prop
    • Pt : P true
    • Pf : P false ,和
    • b : bool
      • 我們可以構造一個值Pb : P b

(當然,這是一個curry函數,所以還有其他方法可以將類型分解為散文,但這對我們的目的來說是最清楚的。)

這里最重要的是,使Coq作為一個定理證明者而作為一種編程語言(反之亦然)的事情是Curry-Howard的對應關系 :類型是命題,值是這些命題的證明。 例如,簡單函數類型->對應於蘊涵,依賴函數類型forall對應於通用量化。 (符號很有啟發性:-))所以在Coq中,為了證明φ→ψ,我們必須構造一個φ -> ψ類型的值:一個取φ型值的函數(換句話說,一個證明命題φ)並用它來構造ψ類型的值(命題ψ的證明)。

在Coq中,我們可以通過這種方式考慮所有類型,無論這些類型是否存在於SetTypeProp (所以當你說“似乎P true(這是bool rec的設置和bool_rect的類型)被視為命題值時,”你是對的!)例如,讓我們考慮一下我們是怎么做的我們自己實現bool_ind 我們首先列出函數的所有參數及其返回類型:

Definition bool_ind' (P  : bool -> Prop)
                     (Pt : P true)
                     (Pf : P false)
                     (b  : bool)
                     : P b :=

到現在為止還挺好。 在這一點上,我們想要返回P b類型的東西,但我們不知道b是什么。 所以,在這些情況下,我們總是模式匹配:

  match b with

現在有兩個案例。 首先, b可能是true 在這種情況下,我們必須要返回P true類型為P true東西,幸運的是我們有這樣的值: Pt

    | true  => Pt

false案件類似:

    | false => Pf
  end.

請注意,當我們實現bool_ind' ,它看起來並不“非常有用”,而是非常“程序化”。 當然,由於Curry-Howard的通信,這些都是一樣的。 但請注意,完全相同的實現將足以滿足其他兩個功能:

Definition bool_rec' (P  : bool -> Set)
                     (Pt : P true)
                     (Pf : P false)
                     (b  : bool)
                     : P b :=
  match b with
    | true  => Pt
    | false => Pf
  end.

Definition bool_rect' (P  : bool -> Type)
                      (Pt : P true)
                      (Pf : P false)
                      (b  : bool)
                      : P b :=
  match b with
    | true  => Pt
    | false => Pf
  end.

看看這個計算定義揭示了另一種關於bool_indbool_recbool_rect :它們封裝了你需要知道的東西來討論bool 每個值 但不管怎樣,我們正在打包那些信息:如果我知道某些事情是true ,而某些事情是false ,那么我知道所有的bool

bool_{ind,rec,rect}函數的定義通過我們在布爾值上編寫函數的常規方式進行抽象:有一個參數對應於true分支,一個參數對應於false分支。 或者,換句話說:這些函數只是if語句。 在非依賴類型的語言中,它們可以具有更簡單的類型forall S : Set, S -> S -> bool -> S

Definition bool_simple_rec (S : Set) (St : P) (Sf : P) (b : bool) : S :=
  match b with
    | true  => St
    | false => Sf
  end.

但是,因為類型可以依賴於值,所以我們必須通過各種類型來處理b 如果事實證明我們不希望這樣,我們可以使用我們更通用的功能並告訴:

Definition bool_simple_rec' (S : Set) : S -> S -> bool -> S :=
  bool_rec (fun _ => S).

沒有人說我們的P : bool -> Set必須使用 bool

對於遞歸類型,所有這些函數都更有趣。 例如,Coq具有以下類型的自然數:

Inductive nat : Set :=  O : nat | S : nat -> nat.

我們有

nat_ind : forall P : nat -> Prop,
            P O ->
            (forall n' : nat, P n' -> P (S n')) ->
            forall n : nat,
              P n

連同相應的nat_recnat_rect (為讀者練習:直接實現這些功能。)

乍一看,這只是數學歸納的原理。 但是,它也是我們在nat上編寫遞歸函數的方式; 他們是一回事。 通常, nat遞歸函數如下所示:

fix f n => match n with
             | O    => ...
             | S n' => ... f n' ...
           end

O (基本情況)之后的匹配臂只是PO類型的值。 S n'之后的匹配臂(遞歸情況)是傳遞給類型forall n' : nat, P n' -> P (S n')的函數forall n' : nat, P n' -> P (S n')n'是相同的,並且P n'值是遞歸調用f n'

考慮_rec_ind函數之間的等價性的另一種方法,然后 - 我認為在無限類型上比在bool上更清楚 - 它與數學ind (在Prop中發生)和(和)之間的等價性相同結構) rec ursion(發生在SetType )。


讓我們充滿熱情並使用這些功能。 我們將定義一個簡單的函數,將布爾值轉換為自然數,我們將直接和bool_rec一起bool_rec 編寫此函數的最簡單方法是使用模式匹配:

Definition bool_to_nat_match (b : bool) : nat :=
  match b with
    | true  => 1
    | false => 0
  end.

另一種定義是

Definition bool_to_nat_rec : bool -> nat :=
  bool_rec (fun _ => nat) 1 0.

這兩個功能是一樣的:

Goal bool_to_nat_match = bool_to_nat_rec.
Proof. reflexivity. Qed.

(注意:這些函數在語法上是相同的 。這比簡單地做同樣的事情更強大。)

在這里, P : bool -> Setfun _ => nat ; 它給我們返回類型,它不依賴於參數。 我們的Pt : P true1 ,當我們給出true時計算的東西; 同樣,我們的Pf : P false0

如果我們想要使用依賴項,我們必須編寫一個有用的數據類型。 怎么樣

Inductive has_if (A : Type) : bool -> Type :=
  | has   : A -> has_if A true
  | lacks : has_if A false.

根據這個定義, has_if A trueA同構,而has_if A false是與unit同構的。 然后我們可以有一個函數,當且僅當它被傳遞為true時才保留它的第一個參數。

Definition keep_if_match' (A : Type) (a : A) (b : bool) : has_if A b :=
  match b with
    | true  => has A a
    | false => lacks A
  end.

另一種定義是

Definition keep_if_rect (A : Type) (a : A) : forall b : bool, has_if A b :=
  bool_rect (has_if A) (has A a) (lacks A).

他們又是一樣的:

Goal keep_if_match = keep_if_rect.
Proof. reflexivity. Qed.

在這里,函數的返回類型取決於參數b ,所以我們的P : bool -> Type實際上做了什么。

這是一個更有趣的例子,使用自然數和長度索引列表。 如果你還沒有看到長度索引列表,也稱為矢量,它們就像他們在錫上所說的那樣; vec A nn A的列表。

Inductive vec (A : Type) : nat -> Type :=
  | vnil  : vec A O
  | vcons : forall n, A -> vec A n -> vec A (S n).
Arguments vnil  {A}.
Arguments vcons {A n} _ _.

Arguments機制處理隱式參數。)現在,我們想要生成一個特定元素的n副本的列表,因此我們可以使用fixpoint編寫它:

Fixpoint vreplicate_fix {A : Type} (n : nat) (a : A) : vec A n :=
  match n with
    | O    => vnil
    | S n' => vcons a (vreplicate_fix n' a)
  end.

或者,我們可以使用nat_rect

Definition vreplicate_rect {A : Type} (n : nat) (a : A) : vec A n :=
  nat_rect (vec A) vnil (fun n' v => vcons a v) n.

請注意,由於nat_rect捕獲遞歸模式,因此vreplicate_rect本身不是固定點。 需要注意的一點是nat_rect的第三個參數:

fun n' v => vcons a v

v在概念上是對vreplicate_rect n' a的遞歸調用的結果; nat_rect抽象出遞歸模式,因此我們不需要直接調用它。 n'vreplicate_fixn'確實相同,但現在看來我們不需要明確提及它。 它為什么傳入? 如果我們寫出我們的類型,那就很明顯了:

fun (n' : nat) (v : vec A n') => vcons a v : vec A (S n')

我們需要n'所以我們知道v有什么類型,結果是什么類型的結果。

讓我們看看這些功能在起作用:

Eval simpl in vreplicate_fix  0 tt.
Eval simpl in vreplicate_rect 0 tt.
  (* both => = vnil : vec unit 0 *)

Eval simpl in vreplicate_fix  3 true.
Eval simpl in vreplicate_rect 3 true.
  (* both => = vcons true (vcons true (vcons true vnil)) : vec bool 3 *)

事實上,他們是一樣的:

(* Note: these two functions do the same thing, but are not syntactically
   equal; the former is a fixpoint, the latter is a function which returns a
   fixpoint.  This sort of equality is all you generally need in practice. *)
Goal forall (A : Type) (a : A) (n : nat),
       vreplicate_fix n a = vreplicate_rect n a.
Proof. induction n; [|simpl; rewrite IHn]; reflexivity. Qed.

上面,我提出重新實現nat_rect和朋友的練習。 這是答案:

Fixpoint nat_rect' (P         : nat -> Type)
                   (base_case : P 0)
                   (recurse   : forall n', P n' -> P (S n'))
                   (n         : nat)
                   : P n :=
  match n with
    | O    => base_case
    | S n' => recurse n' (nat_rect' P base_case recurse n')
  end.

這有希望清楚地表明 nat_rect 如何抽象遞歸模式,以及為什么它足夠通用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM