Num
没有,Arrow
有2个等)。您可以将这些称为运算符。此外,AST中恰好有一个节点可能是“聚焦”的。如果数据类型具有焦点,则使用Z
索引,否则使用H
索引。我需要关于代码的一些建议。希望一次性询问所有相关问题是否没问题。
如何定义只有一个焦点的内部节点类型
InternalZ
?现在我这样说,"我们有S n
个孩子——其中n
个不是焦点,一个(在某个给定的索引上)是焦点。稍微更直观一些的选项(看起来像拉链)可能是InternalZ:forall n m,arityCode(n + 1 + m)- > Vector.t(t H)n - > t Z - > Vector.t(t H)m - > t Z
。虽然我知道我不想处理那个加法。完善类型:在
eq
的两种有趣情况中,我都要比较两个n
(孩子数量)。如果它们相同,我应该能够将arityCode
s和Vector.t
s "强制转换"为相同的类型。现在我用了两个引理来解决这个问题。我应该怎么做才能得到正确的结果?似乎Adam Chlipala的"convoy pattern"可以帮助,但我没有搞清楚怎么做。如果我取消
Vector.eqb
调用的注释,Coq就会抱怨“无法猜测固定参数”。我理解这个错误,但我不确定如何规避它。首先想到的是,我可能必须按其子项深度对t
进行索引。
我的代码:
Module Typ.
Import Coq.Arith.EqNat.
Import Coq.Structures.Equalities.
Import Coq.Arith.Peano_dec.
Import Fin.
Import Vector.
(* h: unfocused, z: focused *)
Inductive hz : Set := H | Z.
(* how many children can these node types have *)
Inductive arityCode : nat -> Type :=
| Num : arityCode 0
| Hole : arityCode 0
(* | Cursor : arityCode 1 *)
| Arrow : arityCode 2
| Sum : arityCode 2
.
Definition codeEq (n : nat) (l r : arityCode n) : bool :=
match l, r with
| Num, Num => true
| Hole, Hole => true
| Arrow, Arrow => true
| Sum, Sum => true
| _, _ => false
end.
(* our AST *)
Inductive t : hz -> Type :=
| Leaf : arityCode 0 -> t H
| Cursor : t H -> t Z
| InternalH : forall n, arityCode n -> Vector.t (t H) n -> t H
| InternalZ : forall n, arityCode (S n) -> Vector.t (t H) n -> Fin.t n * t Z -> t Z
(* alternative formulation: *)
(* | InternalZ : forall n m, arityCode (n + 1 + m) -> Vector.t (t H) n -> t Z -> Vector.t (t H) m -> t Z *)
.
Lemma coerceArity (n1 n2 : nat) (pf : n1 = n2) (c1 : arityCode n1) : arityCode n2.
exact (eq_rect n1 arityCode c1 n2 pf).
Qed.
Lemma coerceVec {A : Type} {n1 n2 : nat} (pf : n1 = n2) (c1 : Vector.t A n1) : Vector.t A n2.
exact (eq_rect n1 (Vector.t A) c1 n2 pf).
Qed.
(* this is the tricky bit *)
Fixpoint eq {h_or_z : hz} (ty1 ty2 : t h_or_z) : bool :=
match ty1, ty2 with
| Leaf c1, Leaf c2 => codeEq c1 c2
| Cursor ty1, Cursor ty2 => eq ty1 ty2
| InternalH n1 c1 ty1, InternalH n2 c2 ty2 =>
match eq_nat_dec n1 n2 with
| right _neqPrf => false
| left eqPrf =>
let c1' := coerceArity eqPrf c1 in
let ty1' := coerceVec eqPrf ty1 in
codeEq c1' c2 (* && Vector.eqb _ eq ty1' ty2 *)
end
| InternalZ n1 c1 v1 (l1, f1), InternalZ n2 c2 v2 (l2, f2) =>
match eq_nat_dec n1 n2 with
| right _neqPrf => false
| left eqPrf =>
let eqPrf' := f_equal S eqPrf in
let c1' := coerceArity eqPrf' c1 in
let v1' := coerceVec eqPrf v1 in
codeEq c1' c2 (* && Vector.eqb _ eq v1' v2 *) && Fin.eqb l1 l2 && eq f1 f2
end
| _, _ => false
end.
End Typ.
Vector.cast
,它本质上与coerceVec
相同。 - Anton Trunov