(value) 构造函数中的全量化类型变量不能按需要显式键入

All-quantified type variable in (value) constructor cannot be explicitly typed as wanted

我有以下 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,因此,所有量化类型变量 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.

根据这个定义,我收到了一条非常奇怪的错误消息。

The term "true" has type "Datatypes.bool" while it is expected to have type "bool".

任何人都可以指出这个问题的根源吗?

不幸的是,Coq 中的原始匹配语句对于您在此处应用的推理类型并不总是很聪明。 "convoy pattern"(有关更多信息,请参阅 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 在通过 "convoy" 传递函数后无法验证终止条件。似乎要解决这个问题,首先定义对 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 来保存 unwrap 构造函数中名为 X 的类型,然后它会隐藏 Datatypes.bool 定义。通常,在 Coq 内核语言中没有办法只匹配一个特定类型(尽管类型类提供了一种在某种程度上模拟它的方法)。

这是另一种实现方式,它使用策略构造 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'.),我们将能够使用为 Wrap_rect 生成的归纳原理构造另一个解决方案=18=] 数据类型(我刚刚从 wrappedGADT' 中的 match 表达式中删除了未使用的变量 k):

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.

如果我们展开 Wrap_rect,这个解决方案可以导致解决方案 a-la ,实现为 Fixpoint 本身。