销毁时“对术语……的抽象是不明确的”

时间:2019-12-07 04:50:09

标签: coq

在尝试破坏依赖类型的术语时,我经常在Coq中遇到错误。我知道关于堆栈溢出有两个与此问题相关的问题,但是对于我自己的证据而言,这两个问题都不足够概括。

这是错误发生位置的简单示例。

我们定义了一个类型族t

Inductive t: nat -> Set :=
| t_S: forall (n: nat), t (S n).

我们现在将尝试证明此类型家庭的每个成员t (S n)都有一个术语,即t_S n

Goal forall (n: nat) (p: t (S n)), p = t_S n.

我们从开始:

intros n p.

下一步,我要破坏p

destruct p.

…但这会遇到以下错误:

Abstracting over the terms "n0" and "p" leads to a term fun (n1 : nat) (p0 : t n1) => p0 = t_S n
which is ill-typed.
Reason is: Illegal application: 
The term "@eq" of type "forall A : Type, A -> A -> Prop"
cannot be applied to the terms
 "t n1" : "Set"
 "p0" : "t n1"
 "t_S n" : "t (S n)"
The 3rd term has type "t (S n)" which should be coercible to "t n1".

在我看来,它正在尝试将p转换为t_S n1,但是以某种方式未能调和n1必须等于n的事实,从而导致=的相对两侧具有不匹配的类型。

为什么会发生这种情况?如何解决这个问题?

1 个答案:

答案 0 :(得分:2)

这个事实的简单证明是

Goal forall (n: nat) (p: t (S n)), p = t_S n.
Proof.
  intros n p.
  refine (
    match p with
    | t_S n => _
    end
  ).
  reflexivity.
Qed.

要了解其工作原理,这将有助于了解Coq在此处构造的证明术语。



Goal forall (n: nat) (p: t (S n)), p = t_S n.
Proof.
  intros n p.
  refine (
    match p with
    | t_S n => _
    end
  ).
  reflexivity.
  Show Proof.
(fun (n : nat) (p : t (S n)) =>
 match
   p as p0 in (t n0)
   return
     (match n0 as x return (t x -> Type) with
      | 0 => fun _ : t 0 => IDProp
      | S n1 => fun p1 : t (S n1) => p1 = t_S n1
      end p0)
 with
 | t_S n0 => eq_refl
 end)

因此,证明字词并不是p上的简单匹配项。相反,Coq巧妙地概括了S n中的p: t (S n),同时将目标类型更改为在S n情况下仍然匹配的目标。

具体来说,上面的证明词使用类型

match (S n) as n' return (t n' -> Type) with
| 0 => fun p => IDProp (* Basically the same as `unit`; a singleton type *)
| S n' => fun p => p = t_S n'
end p

显然,这与p = t_S n相同,但是它可以使S n泛化。 n的每个实例现在都具有S n的形式,因此可以用某些n'通用地替换它。这是如何用个别策略编写的。

Goal forall (n: nat) (p: t (S n)), p = t_S n.
Proof.
  intro n.
  change (
    forall p: t (S n),
      match (S n) as n' return (t n' -> Type) with
      | 0 => fun p => Empty_set (* This can actually be any type. We may as well use the simplest possible type. *)
      | S n' => fun p => p = t_S n'
      end p
  ).
  generalize (S n); clear n.
  intros n p.
  (* p: t n, not t (S n), so we can destruct it *)
  destruct p.
  reflexivity.
Qed.

那么为什么这一切都是必要的?归纳(在特殊情况下,大小写匹配)要求归纳类型中的任何索引都是通用的。通过查看tt_rect: forall (P: forall n: nat, t n -> Type), (forall n: nat, P (S n) (t_S n)) -> forall (n: nat) (x: t n), P n x的归纳原理可以看出这一点。

使用归纳法时,我们需要为所有自然数定义P。即使归纳的另一个假设forall n: nat, P (S n) (t_S n)仅使用P (S n),它的值仍需为零。对于您拥有的目标,P (S n) p := (p = t_S n),但尚未为P定义0。更改目标的巧妙技巧是将P扩展到0,使其方式与S n的定义一致。