Lean define groups - type-theory

This is a follow-up question of Lean pass type as parameter
I tried jmc's suggestion, which seemed to work, but then I got stuck at another point. The original purpose of the question was to define the categories of groups and rings, but now apparently I am unable to define group morphisms:
class group :=
(set: Type)
(add: set → set → set)
infix + := group.add
class group_morphism (G H: group) :=
(f: G.set → H.set)
(additive: ∀ g h : G.set, f(g + h) = (f g) + (f h))
I get an error at the first +. Lean seems to think that this refers to H.add, whereas it is supposed to refer to G.add.

The issue is that group should not be a class. If you look at the type of group.add, by #check #group.addyou will get
group.add : Π [c : group], group.set → group.set → group.set
The square brackets around c : group indicate that this is an implicit argument that will be inferred by type class inference. You won't have to explicitly type this argument, but Lean will try to work out what it is. Type class inference works best for types where there is only one inhabitant you ever want to use.
In mathlib the definition of group is closer to
class group (set : Type) :=
(add : set → set → set)
On a particular type, there is usually only one group structure you want to refer to, so in mathlib, a type like add_group int has only one inhabitant you care about.
Lean automatically chose H as the canonical representative of the type group, but this is not the one you wanted.
So usually when you deal with groups the type and the group structure are kept as separate objects, they are not put into a pair. However for category theory, the usual approach doesn't work, an object is a pair of a type and a group structure.
The setup in mathlib is closer to the following. The coe_to_sort tells Lean how to take a group_obj and interpret it as a Type without having explicitly write G.set, the group_obj_group instance tells Lean how to automatically infer the group structure on the type of a group_obj
class group (set : Type) :=
(add: set → set → set)
structure group_obj :=
(set : Type)
(group : group set)
instance coe_to_sort : has_coe_to_sort group_obj :=
{ S := Type,
coe := group_obj.set }
instance group_obj_group (G : group_obj) : group G := G.group
infix `+` := group.add
structure group_morphism (G H : group_obj) :=
(f: G → H)
(additive: ∀ g h : G.set, f(g + h) = (f g) + (f h))

You are redefining the + notation which will very quickly lead to headaches. Have a polymorphic + notation is very helpful. (How will you denote the addition in a ring?)
Further points:
you should use structure instead of class
mathematically, you are defining monoids and monoid homs, not groups and group homs
This works though
(set: Type)
(add: set → set → set)
def add {G : group} := group.add G
class group_morphism (G H: group) :=
(f: G.set → H.set)
(additive: ∀ g h : G.set, f(add g h) = add (f g) (f h))

Related

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.

Retrieving constraints from GADT to ensure exhaustion of pattern matching in Coq

Let's define two helper types:
Inductive AB : Set := A | B.
Inductive XY : Set := X | Y.
Then two other types that depend on XY and AB
Inductive Wrapped : AB -> XY -> Set :=
| W : forall (ab : AB) (xy : XY), Wrapped ab xy
| WW : forall (ab : AB), Wrapped ab (match ab with A => X | B => Y end)
.
Inductive Wrapper : XY -> Set :=
WrapW : forall (xy : XY), Wrapped A xy -> Wrapper xy.
Note the WW constructor – it can only be value of types Wrapped A X and Wrapped B Y.
Now I would like to pattern match on Wrapper Y:
Definition test (wr : Wrapper Y): nat :=
match wr with
| WrapW Y w =>
match w with
| W A Y => 27
end
end.
but I get error
Error: Non exhaustive pattern-matching: no clause found for pattern WW _
Why does it happen? Wrapper forces contained Wrapped to be A version, the type signature forces Y and WW constructor forbids being A and Y simultaneously. I don't understand why this case is being even considered, while I am forced to check it which seems to be impossible.
How to workaround this situation?
Let's simplify:
Inductive MyTy : Set -> Type :=
MkMyTy : forall (A : Set), A -> MyTy A.
Definition extract (m : MyTy nat) : nat :=
match m with MkMyTy _ x => S x end.
This fails:
The term "x" has type "S" while it is expected to have type "nat".
wat.
This is because I said
Inductive MyTy : Set -> Type
This made the first argument to MyTy an index of MyTy, as opposed to a parameter. An inductive type with a parameter may look like this:
Inductive list (A : Type) : Type :=
| nil : list A
| cons : A -> list A -> list A.
Parameters are named on the left of the :, and are not forall-d in the definition of each constructor. (They are still present in the constructors' types outside of the definition: cons : forall (A : Type), A -> list A -> list A.) If I make the Set a parameter of MyTy, then extract can be defined:
Inductive MyTy (A : Set) : Type :=
MkMyTy : A -> MyTy A.
Definition extract (m : MyTy nat) : nat :=
match m with MkMyTy _ x => S x end.
The reason for this is that, on the inside, a match ignores anything you know about the indices of the scrutinee from the outside. (Or, rather, the underlying match expression in Gallina ignores the indices. When you write a match in the source code, Coq tries to convert it into the primitive form while incorporating information from the indices, but it often fails.) The fact that m : MyTy nat in the first version of extract simply did not matter. Instead, the match gave me S : Set (the name was automatically chosen by Coq) and x : S, as per the constructor MkMyTy, with no mention of nat. Meanwhile, because MyTy has a parameter in the second version, I actually get x : nat. The _ is really a placeholder this time; it is mandatory to write it as _, because there's nothing to match, and you can Set Asymmetric Patterns to make it disappear.
The reason we distinguish between parameters and indices is because parameters have a lot of restrictions—most notably, if I is an inductive type with parameters, then the parameters must appear as variables in the return type of each constructor:
Inductive F (A : Set) : Set := MkF : list A -> F (list A).
(* ^--------^ BAD: must appear as F A *)
In your problem, we should make parameters where we can. E.g. the match wr with Wrap Y w => _ end bit is wrong, because the XY argument to Wrapper is an index, so the fact that wr : Wrapper Y is ignored; you would need to handle the Wrap X w case too. Coq hasn't gotten around to telling you that.
Inductive Wrapped (ab : AB) : XY -> Set :=
| W : forall (xy : XY), Wrapped ab xy
| WW : Wrapped ab (match ab with A => X | B => Y end).
Inductive Wrapper (xy : XY) : Set := WrapW : Wrapped A xy -> Wrapper xy.
And now your test compiles (almost):
Definition test (wr : Wrapper Y): nat :=
match wr with
| WrapW _ w => (* mandatory _ *)
match w with
| W _ Y => 27 (* mandatory _ *)
end
end.
because having the parameters gives Coq enough information for its match-elaboration to use information from Wrapped's index. If you issue Print test., you can see that there's a bit of hoop-jumping to pass information about the index Y through the primitive matchs which would otherwise ignore it. See the reference manual for more information.
The solution turned out to be simple but tricky:
Definition test (wr : Wrapper Y): nat.
refine (match wr with
| WrapW Y w =>
match w in Wrapped ab xy return ab = A -> xy = Y -> nat with
| W A Y => fun _ _ => 27
| _ => fun _ _ => _
end eq_refl eq_refl
end);
[ | |destruct a]; congruence.
Defined.
The issue was that Coq didn't infer some necessary invariants to realize that WW case is ridiculous. I had to explicitly give it a proof for it.
In this solution I changed match to return a function that takes two proofs and brings them to the context of our actual result:
ab is apparently A
xy is apparently Y
I have covered real cases ignoring these assumptions, and I deferred "bad" cases to be proven false later which turned to be trivial. I was forced to pass the eq_refls manually, but it worked and does not look that bad.

Characteristic function of a union

In a constructive setting such as Coq's, I expect the proof of a disjunction A \/ B to be either a proof of A, or a proof of B. If I reformulate this on subsets of a type X, it says that if I have a proof that x is in A union B, then I either have a proof that x is in A, or a proof that x is in B. So I want to define the characteristic function of a union by case analysis,
Definition characteristicUnion (X : Type) (A B : X -> Prop)
(x : X) (un : A x \/ B x) : nat.
It will be equal to 1 when x is in A, and to 0 when x is in B. However Coq does not let me destruct un, because "Case analysis on sort Set is not allowed for inductive definition or".
Is there another way in Coq to model subsets of type X, that would allow me to construct those characteristic functions on unions ? I do not need to extract programs, so I guess simply disabling the previous error on case analysis would work for me.
Mind that I do not want to model subsets as A : X -> bool. That would be unecessarily stronger : I do not need laws of excluded middle such as "either x is in A or x is not in A".
As pointed out by #András Kovács, Coq prevents you from "extracting" computationally relevant information from types in Prop in order to allow some more advanced features to be used. There has been a lot of research on this topic, including recently Univalent Foundations / HoTT, but that would go beyond the scope of this question.
In your case you want indeed to use the type { A } + { B } which will allow you to do what you want.
I think the union of subsets should be a subset as well. We can do this by defining union as pointwise disjunction:
Definition subset (X : Type) : Type := X -> Prop.
Definition union {X : Type}(A B : subset X) : subset X := fun x => A x \/ B x.

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

Type hierarchy definition in Coq or Agda

I would like to build a kind of type hierarchy:
B is of type A ( B::A )
C and D are of type of B (C,D ::B)
E and F are of type of C (E,F ::C)
I asked here if this is possible to be directly implemented in Isabelle, but the answer as you see was No. Is it possible to encode this directly in Agda or Coq?
PS: Suppose A..F are all abstract and some functions are defined over each type)
Thanks
If I understood your question correctly, you want something that looks like the identity type. When we declare the type constructor _isOfType_, we mention two Sets (the parameter A and the index B) but the constructor indeed makes sure that the only way to construct an element of such a type is to enforce that they are indeed equal (and that a is of this type):
data _isOfType_ {ℓ} {A : Set ℓ} (a : A) : (B : Set ℓ) → Set where
indeed : a isOfType A
We can now have functions taking as arguments proofs that things are of the right type. Here I translated your requirements & assumed that I had a function f able to combine two Cs into one. Pattern-matching on the appropriate assumptions reveals that E and F are indeed on type C and can therefore be fed to f to discharge the goal:
example : ∀ (A : Set₃) (B : Set₂) (C D : Set₁) (E F : Set) →
B isOfType A
→ C isOfType B → D isOfType B
→ E isOfType C → F isOfType C
→ (f : C → C → C) → C
example A B .Set D E F _ _ _ indeed indeed f = f E F
Do you have a particular use case in mind for this sort of patterns or are you coming to Agda with ideas you have encountered in other programming languages? There may be a more idiomatic way to formulate your problem.