我有以下GADT。
Inductive GADT : Type -> Type :=
| A : forall A, GADT A
| B : GADT bool.
以下数据类型包含一个带有全限定类型变量的构造函数。
Inductive Wrap A :=
| wrap : GADT A -> Wrap A
| unwrap : forall X, GADT X -> (X -> Wrap A) -> Wrap A.
然后我想定义一个使用unwrap
中的函数的递归函数。
Fail Fixpoint wrappedGADT {A} (xs: Wrap A) : option (GADT A) :=
match xs with
| wrap _ x => Some x
| unwrap _ _ fx k => match fx with
| A _ => None
| B => wrappedGADT (k true)
end
end.
使用此定义,我收到以下错误消息。
The term "true" has type "bool" while it is expected to have type "T".
我假设当我检查fx
并获取案例B
时,参数fx
的类型为GADT bool
,因此,全量化的类型变量{{1 }}也是X
。这个假设是错的吗?
接下来,我尝试按如下方式明确键入bool
。
unwrap
根据这个定义,我得到一个非常奇怪的错误信息。
Fail Fixpoint wrappedGADT {A} (xs: Wrap A) : option (GADT A) :=
match xs with
| wrap _ x => Some x
| @nwrap _ bool fx k => match fx with
| A _ => None
| B => wrappedGADT (k true)
end
end.
任何人都可以指出这个问题的根源吗?
答案 0 :(得分:3)
不幸的是,Coq中的原始匹配语句对于您在此处应用的推理方式并不总是非常聪明。 “护航模式”(有关它的更多信息,请参阅CPDT)通常是解决此类问题的答案。这里的直接应用看起来像是:
Fail Fixpoint wrappedGADT {A} (xs: Wrap A) {struct xs} : option (GADT A) :=
match xs with
| wrap _ x => Some x
| unwrap _ _ fx k => match fx in (GADT T)
return ((T -> Wrap A) -> option (GADT A)) with
| A _ => fun k0 => None
| B => fun k0 => wrappedGADT (k0 true)
end k
end.
但是,这会遇到另一个问题,即Coq在通过“车队”传递函数后无法验证终止条件。似乎要解决这个问题,首先要在k
的值上定义递归调用的函数,然后对其进行控制:
Fixpoint wrappedGADT {A} (xs: Wrap A) {struct xs} : option (GADT A) :=
match xs with
| wrap _ x => Some x
| unwrap _ _ fx k => let r := fun x => wrappedGADT (k x) in
match fx in (GADT T)
return ((T -> option (GADT A)) -> option (GADT A)) with
| A _ => fun _ => None
| B => fun r' => r' true
end r
end.
对于第二次代码尝试,您要创建一个局部变量bool
来保存X
构造函数中名为unwrap
的类型,然后隐藏Datatypes.bool
定义。一般来说,没有办法只匹配Coq内核语言中的一个特定类型(尽管类型类提供了一种模拟它的方法)。
答案 1 :(得分:3)
这是一个替代实现,它使用策略构造wrappedGADT
的正文。它有一个优点,即它不需要用户手动return
注释。整体结构与match
表达式的原始代码非常相似。
在此处使用induction xs
而不是destruct xs
至关重要,因为Wrap
类型是递归的。
Fixpoint wrappedGADT' {A} (xs: Wrap A) : option (GADT A).
induction xs as [x | ? fx k r].
- exact (Some x).
- destruct fx as [T | ].
+ exact None.
+ exact (r true).
Defined.
Print wrappedGADT'.
这是两个实现在扩展上相等的证明。
Goal forall (A : Type) (xs : Wrap A),
wrappedGADT xs = wrappedGADT' xs.
Proof with auto.
intros A xs.
induction xs...
destruct g...
simpl; rewrite H; destruct (w true)...
Qed.
如果我们查看为wrappedGADT'
生成的术语(使用Print wrappedGADT'.
),我们将能够使用为{{1}生成的Wrap_rect
归纳原则构建另一个解决方案数据类型(我刚从Wrap
中的k
表达式中删除了未使用的变量match
:
wrappedGADT'
如果我们展开Definition wrappedGADT'' {A} (xs: Wrap A) : option (GADT A) :=
Wrap_rect _
_
(fun t => Some t)
(fun _ fx k r =>
match fx in (GADT T)
return ((T -> option (GADT A)) -> option (GADT A)) with
| A _ => fun _ => None
| B => fun r' => r' true
end r)
xs.
,此解决方案可以导致解决方案a-la Daniel's,实现为Wrap_rect
本身。