如何为构造函数设置隐式参数

时间:2019-07-01 22:48:50

标签: coq logical-foundations

打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)).

我认为专业化与专业无关。它是什么?如何解决?

1 个答案:

答案 0 :(得分:3)

问题是导入PeanoNat.Nat

导入PeanoNat时,模块类型Nat会进入作用域,因此导入Nat会带来PeanoNat.Nat。如果您打算导入Coq.Init.Nat,则必须在导入PeanoNat之前先将其导入,或者使用Import Init.Nat.进行导入。

为什么在这种情况下导入PeanoNat.Nat会引起麻烦?

Arith/PeanoNat.vstatic link)包含模块 1 Nat。在该模块内,我们发现 2 不寻常的外观

Include NBasicProp <+ UsualMinMaxLogicalProperties <+ UsualMinMaxDecProperties.

All this means是包括NBasicPropUsualMinMaxLogicalPropertiesUsualMinMaxDecProperties中的每一个,这又意味着这些模块中定义的所有内容都包括在当前模块中。将这一行分成三个Include命令,我们可以找出哪个正在重新定义eq_refl。原来是NBasicProp,可以在this filestatic link)中找到。我们还没有到那里:eq_refl的重新定义不在这里。但是,我们从NBasicProp的角度看到NMaxMinProp的定义。

这将我们引向NMaxMin.v,这又将我们引向NSub.v,这将我们引向NMulOrder.v,这将我们引向NAddOrder.v,将我们引向NOrder.v,将我们引向NAdd.v .v,将我们引向NBase.v,...

在这里我会追逐。最终,我们以模块BackportEq进入Structures/Equality.vstatic 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之类的模块通常不是一个好主意?

如果上面的大白鹅追捕行动还没有足够的说服力,那么让我说,扩展和导入其他模块和模块类型的此类模块通常不打算导入。它们通常有短名称(例如NZNat),因此您想要从中使用的任何定理都可以轻松访问,而不必键入长名称。它们通常有很长的进口链,因此包含大量物品。如果导入它们,那么现在大量的项目正在污染您的全局名称空间。正如您在eq_refl中看到的那样,这可能会导致意外的行为,而您认为这是一个熟悉的常数。


  1. 在本次冒险中遇到的大多数模块属于“模块类型/功能”类。可以说,它们很难完全理解,但是可以找到简短的指南here

  2. 我的侦查是通过在可能从其他位置导入的任何内容打开CoqIDE中的文件并运行命令Locate eq_refl.(或更好的是ctrl + shift + L)来完成的。 Locate还可以告诉您常量从何处导入。我希望有一种更简单的方法来查看模块类型中的导入路径,但我不这么认为。您可能会猜到我们最终会基于改写的eq_refl的类型进入Coq.Classes.RelationClasses,但这并不精确。