我正在尝试使用一种表示通用代数上下文中的表达式的数据类型。在(数学和纸上)数学中表达这种现象的通常方法是,您拥有一组功能符号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}.
(* ??? *)
答案 0 :(得分:1)
这个问题有两个具有挑战性的方面。
在这里,“索引类型”是指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
)。在这里,我们将unit
和prod
用作此类类型的构建基块,但您可能必须自己为更复杂的类型组成。许多依赖模式匹配将是必要的。
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
,而返回bool
。 dec_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
必须是透明的定义:>
它仅适用于第一个列表的子项(如果需要,可以添加一些struct
使其成为第二个列表)。
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.