打st时我发现了另一个奇怪的行为。这是代码:
Inductive nostutter {X:Type} : list X -> Prop :=
| ns_nil : nostutter []
| ns_one : forall (x : X), nostutter [x]
| ns_cons: forall (x : X) (h : X) (t : list X), nostutter (h::t) -> x <> h -> nostutter (x::h::t).
Example test_nostutter_manual: not (nostutter [3;1;1;4]).
Proof.
intro.
inversion_clear H.
inversion_clear H0.
unfold not in H2.
(* We are here *)
specialize (H2 eq_refl).
apply H2.
Qed.
展开后的状态是这样的:
1 subgoal (ID 229)
H1 : 3 <> 1
H : nostutter [1; 4]
H2 : 1 = 1 -> False
============================
False
当我在加载其他Logical Foundations文件的IndProp.v中运行specialize (H2 eq_refl).
时,它可以工作。它以某种方式理解需要将“ 1”作为参数。 IndProp.v的标题是这样:
Set Warnings "-notation-overridden,-parsing".
From LF Require Export Logic.
Require Import String.
Require Coq.omega.Omega.
当我将代码移到另一个文件“ nostutter.v”时,该相同代码给出了预期的错误:
术语“ eq_refl”具有类型“ RelationClasses.Reflexive Logic.eq”,而 预期其类型为“ 1 = 1”。
nostutter.v的标题:
Set Warnings "-notation-overridden,-parsing".
Require Import List.
Import ListNotations.
Require Import PeanoNat.
Import Nat.
Local Open Scope nat_scope.
我必须向eq_refl
显式添加一个参数:specialize (H2 (eq_refl 1)).
我认为专业化与专业无关。它是什么?如何解决?
答案 0 :(得分:3)
问题是导入PeanoNat.Nat
。
导入PeanoNat
时,模块类型Nat
会进入作用域,因此导入Nat
会带来PeanoNat.Nat
。如果您打算导入Coq.Init.Nat
,则必须在导入PeanoNat
之前先将其导入,或者使用Import Init.Nat.
进行导入。
PeanoNat.Nat
会引起麻烦? Arith/PeanoNat.v(static link)包含模块 1 Nat
。在该模块内,我们发现 2 不寻常的外观
Include NBasicProp <+ UsualMinMaxLogicalProperties <+ UsualMinMaxDecProperties.
All this means是包括NBasicProp
,UsualMinMaxLogicalProperties
和UsualMinMaxDecProperties
中的每一个,这又意味着这些模块中定义的所有内容都包括在当前模块中。将这一行分成三个Include
命令,我们可以找出哪个正在重新定义eq_refl
。原来是NBasicProp
,可以在this file(static link)中找到。我们还没有到那里:eq_refl的重新定义不在这里。但是,我们从NBasicProp
的角度看到NMaxMinProp
的定义。
这将我们引向NMaxMin.v,这又将我们引向NSub.v,这将我们引向NMulOrder.v,这将我们引向NAddOrder.v,将我们引向NOrder.v,将我们引向NAdd.v .v,将我们引向NBase.v,...
在这里我会追逐。最终,我们以模块BackportEq
进入Structures/Equality.v(static link),最终为我们重新定义了eq_refl
。
Module BackportEq (E:Eq)(F:IsEq E) <: IsEqOrig E.
Definition eq_refl := @Equivalence_Reflexive _ _ F.eq_equiv.
Definition eq_sym := @Equivalence_Symmetric _ _ F.eq_equiv.
Definition eq_trans := @Equivalence_Transitive _ _ F.eq_equiv.
End BackportEq.
此定义方式,eq_refl
(不带任何参数)的类型为Reflexive eq
,其中Reflexive
是类
Class Reflexive (R : relation A) :=
reflexivity : forall x : A, R x x.
(位于Classes / RelationClasses.v中)
因此,这意味着我们总是需要提供一个额外的参数来获取类型x = x
的东西。这里没有定义隐式参数。
PeanoNat.Nat
之类的模块通常不是一个好主意?如果上面的大白鹅追捕行动还没有足够的说服力,那么让我说,扩展和导入其他模块和模块类型的此类模块通常不打算导入。它们通常有短名称(例如N
,Z
或Nat
),因此您想要从中使用的任何定理都可以轻松访问,而不必键入长名称。它们通常有很长的进口链,因此包含大量物品。如果导入它们,那么现在大量的项目正在污染您的全局名称空间。正如您在eq_refl
中看到的那样,这可能会导致意外的行为,而您认为这是一个熟悉的常数。
在本次冒险中遇到的大多数模块属于“模块类型/功能”类。可以说,它们很难完全理解,但是可以找到简短的指南here。
我的侦查是通过在可能从其他位置导入的任何内容打开CoqIDE中的文件并运行命令Locate eq_refl.
(或更好的是ctrl + shift + L)来完成的。 Locate
还可以告诉您常量从何处导入。我希望有一种更简单的方法来查看模块类型中的导入路径,但我不这么认为。您可能会猜到我们最终会基于改写的eq_refl
的类型进入Coq.Classes.RelationClasses,但这并不精确。