(How) can I define partial coercions in Coq? - coq

I want to set Coq up, without redefining the : with a notation (and without a plugin, and without replacing the standard library or redefining the constants I'm using---no cheating like that), so that I have something like a partial coercion from option nat to nat, which is defined only on Some _. In particular, I want
Eval compute in Some 0 : nat.
to evaluate to 0, and I want
Check None : nat.
to raise an error.
The closest I've managed is the ability to do this with two :s:
Definition dummy {A} (x : option A) := A.
Definition inverted_option {A} (x : option A)
:= match x with Some _ => A | _ => True end.
Definition invert_Some {A} (x : option A) : inverted_option x
:= match x with Some v => v | None => I end.
Coercion invert_Some : option >-> inverted_option.
Notation nat' := (inverted_option (A:=nat) (Some _)).
Eval compute in (Some 0 : nat') : nat.
Check (None : nat') : nat.
(* The term "None" has type "option ?A" while it is expected to have type
"nat'". *)
However, this only works when nat' is a notation, and I can't define a coercion out of a notation. (And trying to define a coercion from inverted_option (Some _) to nat violated the uniform inheritance condition.) I thought I might be able to get around this issue by using canonical structures, but I haven't managed to figure out how to interleave canonical structure resolution with coercion insertion (see also Can canonical structure resolution be interleaved with coercion insertion?).
(I ran into this issue when attempting to answer Coq: Defining a subtype.)

Related

How does one print the entire contents of a proof object or Gallina terms in Coq?

How does one print and expand everything in a Coq term (e.g. implicits, notations, definition, everything)? I want to print absolutely everything (including the contents of the identifiers).
For example with a term, I tried a modest example with Set Printing All. but it didn't work as I expect. My script is:
Module print_everything.
Definition id {A:Type} (x:A) := x.
Print id.
Definition id2 := #id.
Print id2.
Set Printing All.
Print id2.
Print All.
End.
but when I sent it to coq I got:
id2 = #id
: forall (A : Type) (_ : A), A
but I expect the body of the function, something like this:
id2 = fun (A : Type) (x : A) => x
: forall (A : Type) (_ : A), A
or
id = fun (A : Type) (x : A) => x
: forall (A : Type) (_ : A), A
How does one remove all "abstractions", including notation, implicit and even identifiers
so that I can see the entire lambda term (ideally when passing a Gallina term to Print_everything)?
If possible I'd like to control the scopes of which definitions are unpacked for me too. e.g. global variables qualid . qualid.
How does one print and expand everything in a Coq term (e.g. implicits, notations, definition, everything)?
It would also be nice to be able to expand and simplify as much as possible.
Related:
How to temporarily disable notations in Coq
https://coq.discourse.group/t/how-to-unset-printing-everything-e-g-implicits-notations-etc/1364/4
it seems Show Proof. works in the middle of a proof: https://www.seas.upenn.edu/~cis500/cis500-f14/sf/ProofObjects.html and https://softwarefoundations.cis.upenn.edu/lf-current/ProofObjects.html

Coq difficulties in defining constructors using coerced types

Definitions
I'm working on formalizing a typed lambda calculus in Coq, and to keep the notation manageable I've come to rely a lot on coercions. However, I've been running into some difficulties which seem odd.
Right now I'm trying to work with the following types:
type: A descriptor of an allowable type in the language (like function, Unit, etc...)
var: A variable type, defined as nat
VarSets: set of vars
Judgement: a var/my_type pair
ty_ctx: Lists of judgements.
ctx_join: Pairs of ty_ctx's describing disjoint sets of variables
The actual definitions are all given below, except for ctx_join which is given in the next block
(* Imports *)
Require Import lang_spec.
From Coq Require Import MSets.
Require Import List.
Import ListNotations.
Module VarSet := Make(Nat_as_OT).
Inductive Judgement : Type :=
| judge (v : var) (t : type)
.
Definition ty_ctx := (list Judgement).
Definition disj_vars (s1 s2 : VarSet.t) := VarSet.Empty (VarSet.inter s1 s2).
Often I'd like to make statements like "this var does not appear in the set of vars bound by ty_ctx", and to that end I've set up a bunch of coercions between these types below.
(* Functions to convert between the different types listed above *)
Fixpoint var_to_varset (v : var) : VarSet.t :=
VarSet.singleton v.
Coercion var_to_varset : var >-> VarSet.t.
Fixpoint bound_variables (g : ty_ctx) : VarSet.t :=
match g with
| nil => VarSet.empty
| cons (judge v _) g' =>VarSet.union (VarSet.singleton v) (bound_variables g')
end.
Coercion bound_variables : ty_ctx >-> VarSet.t.
Inductive ctx_join :=
| join_single (g : ty_ctx)
| join_double (g1 g2 : ty_ctx)
(disjoint_proof : disj_vars g1 g2)
.
Fixpoint coerce_ctx_join (dj : ctx_join) : ty_ctx :=
match dj with
| join_single g => g
| join_double g1 g2 _ => g1 ++ g2
end.
Coercion coerce_ctx_join : ctx_join >-> ty_ctx.
Fixpoint coerce_judgement_to_ty_ctx (j : Judgement) : ty_ctx :=
cons j nil.
Coercion coerce_judgement_to_ty_ctx : Judgement >-> ty_ctx.
You'll notice that the definition of ctx_join relies on coercing its arguments from ty_ctx to VarSet.
I've drawn up the conversion hierarchy just to make things clear
The Problem
I'd like to define an inductive type with the following constructor
Inductive expr_has_type : ty_ctx -> nat -> type -> Prop :=
(* General Expressions *)
| ty_var (g : ty_ctx) (x : var) (t : type) (xfree : disj_vars x g)
: expr_has_type (join_double (judge x t) g xfree) x t
.
The problem is that when I do, I get the following error:
Error:
In environment
expr_has_type : ty_ctx -> nat -> type -> Prop
g : ty_ctx
x : var
t : type
xfree : disj_vars x g
The term "xfree" has type "disj_vars x g" while it is expected to have type
"disj_vars (judge x t) g" (cannot unify "VarSet.In a (VarSet.inter (judge x t) g)" and
"VarSet.In a (VarSet.inter x g)").
However, if I change the type of xfree to disj_vars (VarSet.singleton x) g, then the definition works fine! This seems very odd, as disj_vars is defined only on VarSets, and so it seems like x should automatically be converted toVarSet.singleton x since that's how the coercion is set up.
Even weirder is the fact that if I don't set up the coercion from vars to varsets, then Coq correctly complains about applying dis_vars to a var instead of a VarSet. So the coercion is definitely doing something
Can someone explain to me why the first definition fails? Given the coercions I've set up, to me it like all the definitions above should be equivalent
Note
Changing the type of xfree to disj_vars (judge x t) g also fixes the error. This seems odd too, since to be able to apply disj_vars to j := (judge x t), it first needs to be coerced to a ty_ctx via cons j nil, then to a VarSet via bound_variables, which should produce a VarSet containing only x (which is equivalent to VarSet.singleton x?). So this coercion chain seems to go off without a hitch, while the other one fails even though it's simpler
If you use Set Printing Coercions., the error message will be much more informative about the problem:
The term "xfree" has type "disj_vars (var_to_varset x) (bound_variables g)"
while it is expected to have type
"disj_vars (bound_variables (coerce_judgement_to_ty_ctx (judge x t)))
(bound_variables g)"
The problem is that the coercion of x into a VarSet.t is equal to Var.singleton x, while the coercion in judge reduces to VarSet.union (VarSet.singleton x) VarSet.empty. While these two are propositionally equal, they are not judgmentally equal, so as far as Coq is concerned, the term it came up with is ill-typed.

Proof that two isomorphic types are different

Given these two types,
Inductive unit : Set := tt.
Inductive otherUnit : Set := ttt.
Can Coq prove they are different, ie unit <> otherUnit ?
Both are singleton types in sort Set so it is not easy to find a Set -> Prop that differentiates them. For example, this does not even type check
Definition singletonTT (A : Set) : Prop := forall x : A, x = tt.
because tt has type unit instead of A.
It's actually admissible in Coq that these two types are equal; that is, you can neither prove they are equal or different, and it's consistent to assume either.
You can't express singletonTT in terms of tt because as you point out, it only type checks for the unit type. Instead you need to treat A opaquely; for example, you can express the property of being a singleton as A /\ forall (x y:A), x = y. Of course, both types are singletons so this doesn't distinguish them.
If you do assume Axiom unit_otherUnit : unit = otherUnit, then you can express something like singletonTT by casting tt to otherUnit: eq_rec _ (fun S => S) tt otherUnit ax = ttt.
When types have different cardinalities (which means they aren't isomorphic) you can use their cardinality to distinguish them and prove the types are distinct. Easy examples include False <> True and unit <> bool, and a more complicated example is nat <> (nat -> nat).

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.