证明包含向量的数据类型的可判定性

时间:2019-03-25 09:51:58

标签: recursion coq

我正在尝试使用一种表示通用代数上下文中的表达式的数据类型。在(数学和纸上)数学中表达这种现象的通常方法是,您拥有一组功能符号F和一个arity函数。表达式是一棵树,其中每个节点都用功能符号标记,并且其子节点数与其Arity一样多。在这个特定示例中,我还获得了一组原子变量,这些变量明确地作为术语注入。

很明显如何用Coq写下这些(我在底部有一段代码),但是我想证明某种可判定性的结果。我已经设法证明了向量的可确定性(“如果我对A具有可确定性,那么我可以在VectorDef.t A n上获得可确定性”),但是我不知道如何对我的树类型执行相同的操作。 / p>

我尝试对树的结构进行显式递归,但是最终我需要调用我的“可确定向量”函数,该函数不会通过终止检查器。这是合理的,因为向量函数期望为其基础类型的任意元素提供一个区分符,而且显然不会触底!

我不知道如何告诉Coq(通过归纳法)我对某些术语具有决定性,而这些是出现在向量中的唯一术语。做这种事情有标准的把戏吗?

下面,有问题的数据类型:

Require Vectors.VectorDef.

Definition vec := VectorDef.t.

Section VTree.
  (* If it helps, I have a definition for this function *)
  Variable dec_vec : forall A : Type,
                     (forall x y : A, {x = y} + {x <> y}) ->
                     forall (n : nat) (v v' : vec A n), {v = v'} + {v <> v'}.

  Variable V : Set.
  Variable F : Set.
  Variable a : F -> nat.

  Inductive VTree : Type :=
  | varTerm : V -> VTree
  | funTerm (f : F) (ts : vec VTree (a f)) : VTree.

  Section DecVTree.
    Hypothesis decV : forall x y : V, {x = y} + {x <> y}.
    Hypothesis decF : forall x y : F, {x = y} + {x <> y}.

    Definition decVTree : forall x y : VTree, {x = y} + {x <> y}.
      (* ??? *)

2 个答案:

答案 0 :(得分:1)

这个问题有两个具有挑战性的方面。

  1. 在Coq中具有索引类型的依赖类型编程
  2. 嵌套递归类型

在Coq中具有索引类型的依赖类型编程

在这里,“索引类型”是指Vector.t之类的归纳类型,其中构造函数会精简一些类型参数。这些参数称为索引,并且必须在类型签名中出现在::=之间:

Inductive Vector.t (A : Type) : nat (* <- index *) -> Type :=
| nil : Vector.t A 0
| cons : A -> forall n, Vector.t A n -> Vector.t A (S n).

索引归纳类型对于定义命题无关紧要的命题非常有用。但是对于实际数据,这里的简短故事是:不要这样做。从技术上讲,这是可能的,但是这是一个非常深的兔子洞,并且使用起来非常麻烦,这在很大程度上是因为Coq中的依赖模式匹配是一种不直观的构造。例如,请参见此博客文章:https://homes.cs.washington.edu/~jrw12/dep-destruct.html

一个不太极端的解决方案是放弃该程序的其他“依赖类型”方面。斩波块的下一个候选者是sumbool{ _ } + { _ })。如果函数(和参数)返回bool,这将使它们的定义变得相当容易( * cough * ,请参阅下一节)。证明它们的正确性仍然是一个问题,但至少您需要进行一些计算。

归纳索引类型的两种常规替代方法是:

  • 只需使用平面版本(list而不是vec),就放弃了一些“按构造”保证。

  • 使类型成为索引的函数,而不是Definition,而不是Fixpoint(或Inductive)。在这里,我们将unitprod用作此类类型的构建基块,但您可能必须自己为更复杂的类型组成。许多依赖模式匹配将是必要的。

    Fixpoint vec (A : Type) (n : nat) := match n with
                                         | O => unit | S n => (A * vec n)%type
                                         end.
    

您可能还想重新考虑要实现的语言的表示形式。例如,您是否真的要显式地将arities表示为符号函数? (当然可能是这种情况。)例如,您是否可以不将其限制为arities 0、1、2的符号?

嵌套递归类型

这些是递归类型,其递归出现在其他数据类型内(可能是递归的)。为了简化讨论,使代码更整洁,并且由于Coq中依赖类型存在上述问题,请考虑使用list而不是vec并使用较少的构造函数来使用以下类型:

Inductive LTree : Type :=
| funTerm : list LTree -> LTree.

您可以使用Fixpoint在这种类型上定义递归函数,但是必须特别注意如何嵌套递归调用。当然,这实际上与任何递归类型有关,但是当不嵌套递归时,模式会更加自然,因此问题不太明显。

以下是我们如何确定LTree相等的方法。我们放弃依赖项sumbool,而返回booldec_list的定义是标准的和通用的。

Require Import List.
Import ListNotations.

Section List.

Context {A : Type} (decA : A -> A -> bool).

Fixpoint dec_list (l l' : list A) : bool :=
  match l, l' ith
  | [], [] => true
  | a :: l0, a' :: l0' =>
    decA a a' && dec_list l0 l0'
  | _, _ => false
  end.

End List.

然后LTree的平等看起来是无辜的...

Fixpoint decLTree (x y : LTree) : bool :=
  match x, y with
  | funTerm lx, funTerm ly =>
    dec_list decLTree lx ly
  end.

...但是有一些非常细微的细节,需要使说服Coq递归在结构上逐渐减少。

decLTree的格式是否正确,特别取决于dec_list如何使用其参数decA,因此dec_list必须是透明的定义:

  1. 它仅适用于第一个列表的子项(如果需要,可以添加一些struct使其成为第二个列表)。

  2. decA绑定到Fixpoint dec_list外部。如果该行的内容为decLTree,则该函数Fixpoint dec_list {A : Type} (decA : A -> A -> bool)的格式不正确。

还可以通过为LTree / VTree编写一些通用的递归/归纳方案来打包这些技巧。

答案 1 :(得分:1)

尽管Li-yao提出了一些有用的观点,但依赖类型还不错!事实证明,我以前的脚本不起作用的原因是我使用Qed而不是Defined来完成向量的可判定性证明。

这是一个完整的工作证明:

Require Vectors.VectorDef.
Require Import Logic.Eqdep_dec.
Require Import PeanoNat.

Definition vec := VectorDef.t.

Section dec_vec.
  Variable A : Type.
  Hypothesis decA : forall x y : A, {x = y} + {x <> y}.

  Definition dec_vec {n} (v v' : vec A n) : {v = v'} + {v <> v'}.
    refine (VectorDef.rect2 (fun _ x y => {x = y} + {x <> y})
                            (left (eq_refl))
                            (fun n v v' veq a a' => _)
                            v v').
    - destruct (decA a a') as [ eqaH | neaH ].
      + rewrite <- eqaH; clear eqaH a'.
        destruct veq as [ eqvH | nevH ].
        * rewrite <- eqvH. apply left. exact eq_refl.
        * apply right. intro consH. inversion consH.
          exact (nevH (inj_pair2_eq_dec nat Nat.eq_dec (vec A) n v v' H0)).
      + apply right.
        intro consH. inversion consH. contradiction.
  Defined.
End dec_vec.

Section VTree.
  Variable V : Set.
  Variable F : Set.
  Variable a : F -> nat.

  Inductive VTree : Type :=
  | varTerm : V -> VTree
  | funTerm (f : F) (ts : vec VTree (a f)) : VTree.

  Section DecVTree.
    Hypothesis decV : forall x y : V, {x = y} + {x <> y}.
    Hypothesis decF : forall x y : F, {x = y} + {x <> y}.

    Lemma varTerm_ne_funTerm v f ts : varTerm v <> funTerm f ts.
    Proof.
      intros eqH. inversion eqH.
    Qed.

    Fixpoint decVTree (x y : VTree) : {x = y} + {x <> y}.
      refine (match x, y with
              | varTerm v, varTerm v' => _
              | varTerm v, funTerm f ts => _
              | funTerm f ts, varTerm v => _
              | funTerm f ts, funTerm f' ts' => _
              end
             ).
      - destruct (decV v v') as [ eqH | neH ].
        + exact (left (f_equal varTerm eqH)).
        + enough (H: varTerm v <> varTerm v');
            try (exact (right H)).
          injection; tauto.
      - exact (right (varTerm_ne_funTerm v f ts)).
      - exact (right (not_eq_sym (varTerm_ne_funTerm v f ts))).
      - destruct (decF f f') as [ feqH | fneH ].
        + revert ts'. rewrite <- feqH. clear feqH; intro ts'.
          destruct (dec_vec VTree decVTree ts ts') as [ tseqH | tsneH ].
          * apply left. apply f_equal. exact tseqH.
          * apply right. intro funH. inversion funH.
            exact (tsneH (inj_pair2_eq_dec
                            F decF (fun f => vec VTree (a f)) f ts ts' H0)).
        + enough (H: funTerm f ts <> funTerm f' ts');
            try (exact (right H)).
          injection; tauto.
    Qed.
  End DecVTree.
End VTree.