Coq &引用;术语抽象化…;“定义不清”;解构时
我在尝试销毁依赖类型的项时,经常在Coq中遇到错误。我知道有两个关于堆栈溢出的问题与这个问题有关,但这两个问题都不够一般,我无法在自己的证明中理解 下面是一个发生错误的简单示例 我们定义了一个类型族Coq &引用;术语抽象化…;“定义不清”;解构时,coq,Coq,我在尝试销毁依赖类型的项时,经常在Coq中遇到错误。我知道有两个关于堆栈溢出的问题与这个问题有关,但这两个问题都不够一般,我无法在自己的证明中理解 下面是一个发生错误的简单示例 我们定义了一个类型族t: Inductive t: nat -> Set := | t_S: forall (n: nat), t (S n). 现在我们将试图证明这类家族的每个成员t(sn)都居住着一个术语,即t\sn Goal forall (n: nat) (p: t (S n)), p = t_S n.
t
:
Inductive t: nat -> Set :=
| t_S: forall (n: nat), t (S n).
现在我们将试图证明这类家族的每个成员t(sn)
都居住着一个术语,即t\sn
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
这一事实,从而导致=
的对侧具有不匹配的类型
为什么会发生这种情况?人们如何避免这种情况?这一事实的一个简单证明是
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.
因此证明项不是p
上的简单匹配项。相反,Coq巧妙地概括了p:t(sn)
中的sn
,同时将目标类型更改为在sn
情况下仍然匹配
具体来说,上面的证明术语使用类型
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_sn
相同,但它允许sn
被推广。n
的每个实例现在都是sn
的形式,因此可以用一些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.
那么,为什么这一切都是必要的呢?归纳(作为一种特殊情况,案例匹配)要求归纳类型中的任何索引都是通用的。这可以通过查看
t
:t\rect:forall(P:forall n:nat,tn->Type),(forall n:nat,P(sn)(t\sn))->forall(n:nat)(x:tn),pnx
当使用归纳法时,我们需要为所有自然数定义p
。尽管归纳法的另一个假设,forall n:nat,P(sn)(t_S n)
,只使用了P(sn)
,但它仍然需要有一个值为零。对于您的目标,P(sn)P:=(P=t_S n)
,但是没有为0
定义P
。改变目标的巧妙方法是将P
扩展到0
,以符合sn
的定义
Goal forall (n: nat) (p: t (S n)), p = t_S n.