Design functor to be able to use different modules - coq

I have two different module signatures into two different files. They have the same field name but different behaviour.
And functor inside define another functor.
PI.v
Module Type TPI.
Parameter sig : Signature.
Parameter trsInt: forall f, pi f.
End TPI.
Module P (Export T: TPI).
Lemma A...
Module Export Mono <: MonoType.
End P.
and
MI.v
Module Type TMI.
Parameter sig : Signature.
Parameter trsInt : forall f, mi f.
End TMI.
Module M (Export TM: TMI).
Lemma B ...
Module Export Mono <: MonoType.
End M.
Where MonoType inside another file for example Mono.v
Here is my situation.
I have another file called B.v inside that file I need to use definitions and lemmas in both files PI.v and MI.v.
And one definition I need to use both of them for example.
Definition redPair R is :=
match is with
| Type_PI => pi_int R is
| Type_MI => mi_int R is
end.
I have a problem at R because pi_int R and mi_int R has different sig (Signature), where pi_int used trsInt inside the module signature TPI and mint_int used trsInt inside the module signature TMI.
Here is the way I defined it:
Module PI (Import P : TPI).
Definition pi_int R is := P.trsInt ...
(* inside PI I define another functor for MI *)
Module MI (Import M : TMI).
Definition mi_int R is := M.trsInt ...
Definition redPair R is :=
match is with
| Type_PI => pi_int R is
| Type_MI => mi_int R is (* error here saying that it used the sig of
P.sig but in this case mi_int used the sig of M.sig *)
end.
End MI.
End PI.
My question is that is there a way that I can have a good structure of module that I can have the same signature at the definition redPair? Thank you for your help.

In practice, in the definition of redPair, you have no guarantee that P.sig and M.sig have the same type, which is why you get this error message.
What one can do to solve this kind of problem is to enforce the type equality via a "sharing constraint". Here is a code similar to yours demonstrating how I force P.sig and M.sig to be equal to nat:
Module Type TPI.
Parameter sig : Type.
Parameter x : sig.
End TPI.
Module Type TMI.
Parameter sig : Type.
Parameter x : sig.
End TMI.
Module PI (Import P : TPI with Definition sig := nat).
Definition pi_int := x.
Module MI (Import M : TMI with Definition sig := nat).
Definition mi_int := x.
Definition f (x : bool) :=
if x
then pi_int
else mi_int.
End MI.
End PI.
You could also choose to leave P.sig unconstrained, but then enforce M.sig to be the same:
Module PI (Import P : TPI).
Definition pi_int := x.
Module MI (Import M : TMI with Definition sig := P.sig).
Definition mi_int := x.
Definition f (x : bool) :=
if x
then pi_int
else mi_int.
End MI.
End PI.
Now that a solution to your problem is mentioned, I would advise to be wary of using modules and functors in Coq. Most often than not, you actually only need dependent records. Modules and functors are complicated for everyone, you have to care about abstraction, sharing constraints, etc.
The status quo seems to be that you should avoid using them unless you actually need them for what they offer over dependent records: abstraction and/or subtyping.
As a matter of fact, I am at a bit of unease with your definitions right now. For instance, does it make sense for MI to be defined inside of PI? That seems arbitrary without more context. Maybe it does, I'm just stating that you should be careful when using modules, and make sure you have a good grasp of what you're doing, or it might backfire on you! :)
But if you're just experimenting, please do experiment with them. It's good to know the range of possibilities and the pros and cons of each.

Related

Why does Coq object to the following PairUsualDecidableTypeFull module type?

In Coq.Structures.EqualitiesFacts, there is a convenient PairUsualDecidableType module type for building a UsualDecidableType module from the cartesian product of two others.
It seems that there is no corresponding PairUsualDecidableTypeFull module type for doing the same with UsualDecidableTypeFulls.
I tried to create one, beginning as follows:
Module PairUsualDecidableTypeFull(D1 D2:UsualDecidableTypeFull) <: UsualDecidableTypeFull.
Definition t := (D1.t * D2.t)%type.
Definition eq := #eq t.
Instance eq_equiv : Equivalence eq := _.
Definition eq_refl : forall x : t, x = x. Admitted.
Definition eq_sym : forall x y : t, x = y -> y = x. Admitted.
Definition eq_trans : forall x y z : t, x = y -> y = z -> x = z. Admitted.
Definition eq_dec : forall x y, { eq x y }+{ ~eq x y }. Admitted.
Definition eqb : t -> t -> bool. Admitted.
Definition eqb_eq : forall x y : t, eqb x y = true <-> x = y. Admitted.
End PairUsualDecidableTypeFull.
but Coq complains that:
Signature components for label eq_refl do not match: the body of definitions differs.
I do not understand what "signature components" means. Given that the output of Print UsualDecidableTypeFull includes:
Definition eq_refl : forall x : t, #Logic.eq t x x.
the type of eq_refl at least looks right. What else could be wrong?
I am a total amateur and extremely new to Coq, running version 8.9.0. Perhaps what I'm trying to do doesn't make sense for some reason; the fact that the standard libraries include PairUsualDecidableType but not PairUsualDecidableTypeFull makes me a little suspicious I've missed something.
Any guidance would be most welcome, and thanks in advance.
First, the standard library is known to be incomplete. Thus, the fact that one particular definition/lemma/module is not provided does not mean that it should not be there. And it is even more true for modules, since the module system of Coq is little used.
Concerning your problem, in Coq, the boundary between Module and Module Type is thin. In particular, you can have definitions in Module Type, and not only declarations (I am not sure that these terms "definition" and "declaration" are the right words to use here, but I hope it is at least understandable). For instance,
Module Type Sig.
Parameter n : nat.
Definition plus x y := x + y.
End Sig.
is a signature declaring a field n of type nat and defining a field plus as the addition of natural numbers. When writing a module that must comply with this signature, you can implement the declarations as you wish, as long as types correspond, but for definitions, you must basically write exactly the same body as in the signature. For instance, you can write:
Module M <: Sig.
Definition n := 3.
Definition plus x y := x + y.
End M.
You can observe which fields are declarations and which are definitions using Print: declarations appear as Parameter and definitions appear as Definition (but the actual body of the definition is not printed, which is admittedly confusing). In your case, eq, eq_equiv, eq_refl, eq_sym and eq_trans are all definitions in UsualDecidableTypeFull, so you have no choice for their implementation, you must define eq as Logic.eq, eq_equiv as eq_equivalence (cf. the definitions in Equalities), etc. When using Admitted to implement eq_refl, you are using a body different from the one given in the signature. Your module definition is thus rejected with the message the body of definitions differs.
If I come back to your initial problem of writing a functor PairUsualDecidableTypeFull, by digging into Equalities and EqualitiesFacts, I wrote this implementation that reuses as much as possible existing components of the standard library.
Module DT_to_Full (D:DecidableType') <: DecidableTypeFull.
Include Backport_DT (D).
Include HasEqDec2Bool.
End DT_to_Full.
Module PairUsualDecidableTypeFull (D1 D2:UsualDecidableTypeFull)
<: UsualDecidableTypeFull.
Module M := PairUsualDecidableType D1 D2.
Include DT_to_Full (M).
End PairUsualDecidableTypeFull.
I managed to work around this by simply "wrapping" Coq's UsualDecidableTypeFull by defining:
Module Type UDTFW <: UsualDecidableType.
Parameter t : Type.
Definition eq := #Logic.eq t.
Definition eq_equiv : Equivalence eq := _.
Parameter eq_refl : forall x : t, x = x.
Parameter eq_sym : forall x y : t, x = y -> y = x.
Parameter eq_trans : forall x y z : t, x = y -> y = z -> x = z.
Parameter eq_dec : forall x y, { #Logic.eq t x y }+{ ~#Logic.eq t x y }.
Parameter eqb : t -> t -> bool.
Parameter eqb_eq : forall x y : t, eqb x y = true <-> x = y.
End UDTFW.
together with:
Module Make_UDTFW (X : UsualDecidableTypeFull) <: UDTFW.
Definition t := X.t.
Definition eq := X.eq.
Definition eq_equiv := X.eq_equiv.
Definition eq_refl := X.eq_refl.
Definition eq_sym := X.eq_sym.
Definition eq_trans := X.eq_trans.
Definition eq_dec := X.eq_dec.
Definition eqb := X.eqb.
Definition eqb_eq := X.eqb_eq.
End Make_UDTFW.
Having introduced this bizarre-looking extra level of indirection at the module level, the defintion of PairUsualDecidableTypeFull in the question actually works, except using UDTFW everywhere intead of UsualDecidableTypeFull.
This rather ugly workaround turns out to suffice for my purposes but I'd be very interested to understand what the real issue is here.

A simple case of universe inconsistency

I can define the following inductive type:
Inductive T : Type -> Type :=
| c1 : forall (A : Type), A -> T A
| c2 : T unit.
But then the command Check (c1 (T nat)) fails with the message: The term T nat has type Type#{max(Set, Top.3+1)} while it is expected to have type Type#{Top.3} (universe inconsistency).
How can I tweak the above inductive definition so that c1 (T nat) does not cause a universe inconsistency, and without setting universe polymorphism on?
The following works, but I would prefer a solution without adding equality:
Inductive T (A : Type) : Type :=
| c1 : A -> T A
| c2' : A = unit -> T A.
Definition c2 : T unit := c2' unit eq_refl.
Check (c1 (T nat)).
(*
c1 (T nat)
: T nat -> T (T nat)
*)
Let me first answer the question of why we get the universe inconsistency in the first place.
Universe inconsistencies are the errors that Coq reports to avoid proofs of False via Russell's paradox, which results from considering the set of all sets which do not contain themselves.
There's a variant which is more convenient to formalize in type theory called Hurken's Paradox; see Coq.Logic.Hurkens for more details. There is a specialization of Hurken's paradox which proves that no type can retract to a smaller type. That is, given
U := Type#{u}
A : U
down : U -> A
up : A -> U
up_down : forall (X:U), up (down X) = X
we can prove False.
This is almost exactly the setup of your Inductive type. Annotating your type with universes, you start with
Inductive T : Type#{i} -> Type#{j} :=
| c1 : forall (A : Type#{i}), A -> T A
| c2 : T unit.
Note that we can invert this inductive; we may write
Definition c1' (A : Type#{i}) (v : T A) : A
:= match v with
| c1 A x => x
| c2 => tt
end.
Lemma c1'_c1 (A : Type#{i}) : forall v, c1' A (c1 A v) = v.
Proof. reflexivity. Qed.
Suppose, for a moment, that c1 (T nat) typechecked. Since T nat : Type#{j}, this would require j <= i. If it gave us that j < i, then we would be set. We could write c1 Type#{j}. And this is exactly the setup for the variant of Hurken's that I mentioned above! We could define
u = j
U := Type#{j}
A := T Type#{j}
down : U -> A := c1 Type#{j}
up : A -> U := c1' Type#{j}
up_down := c1'_c1 Type#{j}
and hence prove False.
Coq needs to implement a rule for avoiding this paradox. As described here, the rule is that for each (non-parameter) argument to a constructor of an inductive, if the type of the argument has a sort in universe u, then the universe of the inductive is constrained to be >= u. In this case, this is stricter than Coq needs to be. As mentioned by SkySkimmer here, Coq could recognize arguments which appear directly in locations which are indices of the inductive, and disregard those in the same way that it disregards parameters.
So, to finally answer your question, I believe the following are your only options:
You can Set Universe Polymorphism so that in T (T nat), your two Ts take different universe arguments. (Equivalently, you can write Polymorphic Inductive.)
You can take advantage of how Coq treats parameters of inductive types specially, which mandates using equality in your case. (The requirement of using equality is a general property of going from indexed inductive types to parameterized inductives types---from moving arguments from after the : to before it.)
You can pass Coq the flag -type-in-type to entirely disable universe checking.
You can fix bug #7929, which I reported as part of digging into this question, to make Coq handle arguments of constructors which appear in index-position in the inductive in the same way it handles parameters of inductive types.
(You can find another edge case of the system, and manage to trick Coq into ignoring the universes you want to slip past it, and probably find a proof of False in the process. (Possibly involving module subtyping, see, e.g., this recent bug in modules with universes.))

Coq: Bypassing the uniform inheritance condition

I've trouble understanding the (point of the) gauntlet one has to pass to bypass the uniform inheritance condition (UIC). Per the instruction
Let /.../ f: forall (x₁:T₁)..(xₖ:Tₖ)(y:C u₁..uₙ), D v₁..vₘ be a
function which does not verify the uniform inheritance condition. To
declare f as coercion, one has first to declare a subclass C' of C
/.../
In the code below, f is such a function:
Parameter C: nat -> Type.
Parameter D: nat -> Prop.
Parameter f: forall {x y}(z:C x), D y.
Parameter f':> forall {x y}(z:C x), D y. (*violates uic*)
Print Coercions. (* #f' *)
Yet I do not have to do anything except putting :> to declare it as a coercion. Maybe the gauntlet will somehow help to avoid breaking UIC? Not so:
Definition C' := fun x => C x.
Fail Definition Id_C_f := fun x d (y: C' x) => (y: C d). (*attempt to define Id_C_f as in the manual*)
Identity Coercion Id_C_f: C' >-> C.
Fail Coercion f: C' >-> D. (*Cannot recognize C' as a source class of f*)
Coercion f'' {x y}(z:C' x): D y := f z. (*violates uic*)
Print Coercions. (* #f' #f'' Id_C_f *)
The question: What am I missing here?
I've trouble understanding the (point of the) gauntlet one has to pass to bypass the uniform inheritance condition (UIC).
Intuitively, the uniform inheritance condition says (roughly) "it's syntactically possible to determine every argument to the coercion function just from the type of the source argument".
The developer that added coercions found it easier (I presume) to write the code implementing coercions if the uniform inheritance condition is assumed. I'm sure that a pull request relaxing this constraint and correctly implementing more general coercions would be welcomed!
That said, note that there is a warning message (not an error message) when you declare a coercion that violates the UIC. It will still add it to the table of coercions. Depending on your version of Coq, the coercion might never trigger, or you might get an error message at type inference time when the code applying the coercion builds an ill-typed term because it tries to apply the coercion assuming the UIC holds when it actually doesn't, or (in older versions of Coq) you can get anomalies (see, e.g., bug reports #4114, #4507, #4635, #3373, and #2828).
That said, here is an example where Identity Coercions are useful:
Require Import Coq.PArith.PArith. (* positive *)
Require Import Coq.FSets.FMapPositive.
Definition lookup {A} (map : PositiveMap.t A) (idx : positive) : option A
:= PositiveMap.find idx map.
(* allows us to apply maps as if they were functions *)
Coercion lookup : PositiveMap.t >-> Funclass.
Definition nat_tree := PositiveMap.t nat.
Axiom mymap1 : PositiveMap.t nat.
Axiom mymap2 : nat_tree.
Local Open Scope positive_scope. (* let 1 mean 1:positive *)
Check mymap1 1. (* mymap1 1 : option nat *)
Fail Check mymap2 1.
(* The command has indeed failed with message:
Illegal application (Non-functional construction):
The expression "mymap2" of type "nat_tree"
cannot be applied to the term
"1" : "positive" *)
Identity Coercion Id_nat_tree : nat_tree >-> PositiveMap.t.
Check mymap2 1. (* mymap2 1 : option nat *)
Basically, in the extremely limited case where you have an identifier which would be recognized as the source of an existing coercion if you unfolded its type a bit, you can use Identity Coercion to do that. (You can also do it by defining a copy of your existing coercion with a different type signature, and declaring that a coercion too. But then if you have some lemmas that mention one coercion, and some lemmas that mention the other, rewrite will have issues.)
There is one other use case for Identity Coercions, which is that, when your source is not an inductive type, you can use them for folding and not just unfolding identifiers, by playing tricks with Modules and Module Types; see this comment on #3115 for an example.
In general, though, there isn't a way that I know of to bypass the uniform inheritance condition.

Import <Module> vs. Include <Module> in Coq Module system

What is the exact semantics of Include M1 inside another module, say M ? How is it different from doing Import M1 inside the module M ? More precisely what is the semantics of the following command:
Module Type M := M1 <+ M2 <+ M3.
To summarize the semantics of both vernacular commands:
The command Include M1 (which can be used in the definition of a module or a module type) asks Coq to make a copy of all the fields of M1. Thus, it acts just like a "copy-and-paste" of the contents of M1 inside the ambient module (resp. module type).
The command Import M1 (which can also be used in the definition of a module or a module type, but additionally requires that M1 is a module) allows one to refer to the fields of M1 with their short identifier (i.e., without needing to use qualified identifiers M1.field_name)
Next, the syntax Module Type M := M1 <+ M2 <+ M3 is a shortcut for:
Module Type M.
Include M1.
Include M2.
Include M3.
End M.
See the corresponding sections of Coq's reference manual on the Include and Import commands, and you may also want to take a look at the Export command (a variant of Import).
If at some point you hesitate between Include and Import, you should probably try to use Import in the first place, because it will have a lighter effect (it won't clone the contents of the specified module but just define shorter names).
Finally, below are two comprehensive examples that illustrate the use of modules, module types, and functors in Coq as well as the commands Include and Import:
(********************************************)
(* Example involving a parameterized module *)
(********************************************)
(* A signature *)
Module Type MT.
Parameter t : Type.
End MT.
(* A bigger signature *)
Module Type MT1.
Include MT.
Parameter u : t.
Parameter f : t -> t.
End MT1.
(* A parameterized module *)
Module F1 (M1 : MT1).
Import M1. (* => we can now write f rather than M1.f *)
Definition fu := f u.
End F1.
(* A module implementing MT1 *)
Module M1 <: MT1. (* => check the signature but don't make the module opaque *)
Definition t := nat.
Definition u := O.
Definition f := S.
End M1.
(* Instantiation *)
Module FM1 := F1 M1.
Compute FM1.fu.
and I recall that Compute is a shortcut for Eval vm_compute in
(********************************************)
(* Extra example: a parameterized signature *)
(* *)
(* It can be noted that this feature of Coq *)
(* module types has no equivalent in OCaml… *)
(********************************************)
Module Type MT2 (M : MT).
Parameter u : M.t.
Import M. (* => we can now write t rather than M.t *)
Parameter f : t -> t.
End MT2.
(* Another parameterized module *)
Module F2 (M : MT) (M2 : MT2 M).
Import M2.
Definition fu := f u.
End F2.
(* Modules implementing MT and MT2 *)
Module M <: MT.
Definition t := bool.
End M.
Module M2 <: MT2 M.
Definition u := false.
Definition f := negb.
End M2.
(* Instantiation *)
Module FM2 := F2 M M2.
Compute FM2.fu.

Extracting integer from int_Ring type in mathcomp's ssralg

A bit of set up for the question: The notation `_i is defined to be the i-th component of a sequence, but is also meant to be the i-th coefficient of a polynomial. The following code outputs Negz 2 : int_ZmodType:
From mathcomp Require Import all_ssreflect.
From mathcomp Require Import all_algebra.
Open Scope ring_scope.
Definition my_seq := [:: Posz 4; Negz 2].
Eval compute in my_seq`_1.
The type of my_seq is seq int. The type int has constructors Posz and Negz.
The header of
https://github.com/math-comp/math-comp/blob/master/mathcomp/algebra/poly.v
informs us that Poly s is a polynomial with coefficients from the sequence s. It also says that p`_i is the i-th coefficient of a polynomial p. I expected the following code to output Negz 2:
Definition my_polynomial := Poly my_seq.
Eval compute in my_polynomial`_1.
The resulting term is not Negz 2, though it does have type int_Ring. There is a sequence constructor polyseq for polynomials. Indeed, the type of polyseq my_polynomial is seq int_Ring. However, doing Eval compute in (polyseq my_polynomial)`_1. gives the same mess.
In transitioning from the concrete type int to int_Ring, has the value of the integer been lost? Or, is there a way to recover the value of an int from an int_Ring? The way int_Ring is packaged, it doesn't look like it's possible, because the constructors don't reference elements. However, the same can be said of int_ZmodType. For reference, those types are defined in
https://github.com/math-comp/math-comp/blob/master/mathcomp/algebra/ssralg.v
This is not completely answering the question, but I managed to prove that the coefficient is indeed Negz 2. I give the proof here for the record. Note that I am not familiar at all with ssreflect, so there may be better and more natural ways to do this.
From mathcomp Require Import all_ssreflect.
From mathcomp Require Import all_algebra.
Open Scope ring_scope.
Definition my_seq := [:: Posz 4; Negz 2].
Eval compute in my_seq`_1.
Definition my_polynomial := Poly my_seq.
Example test : my_polynomial `_1 = Negz 2.
Proof.
cbn.
rewrite 2!polyseq_cons. cbn.
rewrite 2!size_polyC. cbn.
rewrite polyseqC. cbn. reflexivity.
Qed.
EDIT: As explained in the comments below, there exist simpler proofs of this fact.
Here's a version of your code that works:
From mathcomp Require Import all_ssreflect.
From mathcomp Require Import all_algebra.
Open Scope ring_scope.
Definition my_seq := [:: Posz 4; Negz 2].
Definition my_poly := #Polynomial _ my_seq erefl.
Compute my_poly`_1.
Instead of using the simpler Poly wrapper function defined in the library, this version calls directly the constructor of polynomial. If you look at the definition of this type, you'll see that a polynomial is simply a record containing the sequence of coefficients of the polynomial, plus a proof of a boolean equality asserting that the last element of this sequence (the leading coefficient) is not zero. (The second argument in the above expression is a proof that true = true, which is understood by Coq as the same thing as a proof of (last 1 polyseq != 0) = true, by the rules of computation.) You can check manually that there is nothing preventing the expression we're computing from reducing, so Coq is able to figure out the answer.
To see what is wrong with your original attempt, we have to unfold it a little bit. I have included the relevant definitions here in order, expanding some notations:
Poly s := foldr cons_poly (polyC 0)
polyC c := insubd poly_nil [:: c]
(* from eqtype.v *)
insubd {T : Type} {P : pred T} {sT : subType T P} u0 (x : T) : sT :=
odflt u0 (insub x)
insub {T : Type} {P : pred T} {sT : subType T P} (x : T) : option sT
:= if #idP (P x) is ReflectT Px then #Some sT (Sub x Px) else None
And here we find the culprit: Poly is defined in terms of insub, which in turn is defined by case analysis on idP, which is an opaque lemma! And Coq's reduction gets stuck when an opaque term gets in the way. (In case you are curious, what is going on here is that insub is testing, using idP, whether the leading coefficient of the polynomial is indeed different from zero, and, if so, using that fact to build the polynomial.)
The problem is that many definitions in ssreflect were not made to compute fully inside the logic. This is due to two reasons. One is performance: by allowing everything to fully reduce, we can make type checking much slower. The other is that ssreflect is tailored for convenience of reasoning, so many definitions are not the most efficient. The CoqEAL library was developed to connect definitions with better computational behavior to ones that are easier to reason about, like in ssreflect; unfortunately, I don't know if the project is still being maintained.