Playing with nostutter excersizes I found another odd behaviour. Here is the code:
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.
Status after unfold is this:
1 subgoal (ID 229)
H1 : 3 <> 1
H : nostutter [1; 4]
H2 : 1 = 1 -> False
============================
False
When I run specialize (H2 eq_refl).
inside IndProp.v that loads other Logical foundations files, it works. Somehow it understands that it needs to put "1" as a parameter. Header of IndProp.v is this:
Set Warnings "-notation-overridden,-parsing".
From LF Require Export Logic.
Require Import String.
Require Coq.omega.Omega.
When I move the code into another file "nostutter.v", this same code gives an expected error:
The term "eq_refl" has type "RelationClasses.Reflexive Logic.eq" while it is expected to have type "1 = 1".
Header of nostutter.v:
Set Warnings "-notation-overridden,-parsing".
Require Import List.
Import ListNotations.
Require Import PeanoNat.
Import Nat.
Local Open Scope nat_scope.
I have to explicitly add a parameter to eq_refl
: specialize (H2 (eq_refl 1)).
I think it's not related specifically to specialize. What is it? How to fix?
The problem is importing PeanoNat.Nat
.
When you import PeanoNat
, the module type Nat
comes into scope, so importing Nat
brings in PeanoNat.Nat
. If you meant to import Coq.Init.Nat
, you'll either have to import it before importing PeanoNat
, or import it with Import Init.Nat.
.
PeanoNat.Nat
cause trouble in this case?Arith/PeanoNat.v (static link) contains the module1 Nat
. Inside that module, we find2 the unusual looking line
Include NBasicProp <+ UsualMinMaxLogicalProperties <+ UsualMinMaxDecProperties.
All this means is that each of NBasicProp
, UsualMinMaxLogicalProperties
and UsualMinMaxDecProperties
are included, which in turn means that everything defined in those modules is included in the current module. Separating this line out into three Include
commands, we can figure out which one is redefining eq_refl
. It turns out to be NBasicProp
, which is found in this file (static link). We're not quite there yet: the redefinition of eq_refl isn't here. However, we see the definition of NBasicProp
in terms of NMaxMinProp
.
This leads us to NMaxMin.v, which in turn leads us to NSub.v, which leads us to NMulOrder.v, which leads us to NAddOrder.v, which leads us to NOrder.v, which leads us to NAdd.v, which leads us to NBase.v, ...
I'll cut to the chase here. Eventually we end up in Structures/Equality.v (static link) with the module BackportEq
which finally gives us our redefinition of 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.
The way this is defined, eq_refl
(without any arguments) has type Reflexive eq
, where Reflexive
is the class
Class Reflexive (R : relation A) :=
reflexivity : forall x : A, R x x.
(found in Classes/RelationClasses.v)
So that means that we'll always need to supply an extra argument to get something of type x = x
. There are no implicit arguments defined here.
PeanoNat.Nat
generally a bad idea?If the wild goose chase above wasn't convincing enough, let me just say that modules like this one, which extend and import other modules and module types, are often not meant to be imported. They often have short names (like N
, Z
or Nat
) so any theorem you want to use from them is easily accessible without having to type out a long name. They usually have a long chain of imports and thus contain a vast number of items. If you import them, now that vast number of items is polluting your global namespace. As you saw with eq_refl
, that can cause unexpected behavior with what you thought was a familiar constant.
Most of the modules encountered in this adventure are of the "module type/functor" variety. Suffice to say, they're difficult to understand fully, but a short guide can be found here.
My sleuthing was done by opening files in CoqIDE and running the command Locate eq_refl.
(or better yet, ctrl+shift+L) after anything that might import from elsewhere. Locate
can also tell you where a constant was imported from. I wish there were an easier way to see the path of imports in module types, but I don't think so. You could guess that we'd end up in Coq.Classes.RelationClasses based on the type of the overwritten eq_refl
, but that isn't as precise.