from what I know, a type class in Coq is "almost" a record, with magic around for type inference.
Although I am not aware anybody saying it explicitly, I have the feeling of type classes can do whatever records can do.
However, this feeling lead me to some problem with accessors of type class, which seems standard with record:
Require Import List.
Set Printing All.
Set Implicit Arguments.
(* Record version that works: *)
Record Foo_r (A_r: Type) := { getter_r : list A_r; }.
Definition foo_r : Foo_r nat := {| getter_r := 2::nil |}.
Compute (getter_r foo_r).
(* Type class version that does not work: *)
Class Foo_t (A_t: Type) := { getter_t : list A_t; }.
Instance foo_t : Foo_t nat := {| getter_t := 2::nil |}.
Compute (getter_t foo_t).
It is interesting that getter_r and getter_t have a very similar function signature, and the fact that Foo_t is indeed just a record:
Print getter_r.
(* fun (A_r : Type) (f : Foo_r A_r) => let (getter_r) := f in getter_r
: forall A_r : Type, Foo_r A_r -> list A_r *)
Print getter_t.
(* fun (A_t : Type) (Foo_t0 : Foo_t A_t) => let (getter_t) := Foo_t0 in getter_t
: forall A_t : Type, Foo_t A_t -> list A_t *)
May I ask that, conceptually, is this a design decision to disallow accessors for type class? or I just use it wrong? thanks.
If you check the type of getter_t using About getter_t. you will see:
getter_t : forall (A_t : Type) (_ : Foo_t A_t), list A_t
Arguments A_t, Foo_t are implicit and maximally inserted
Foo_t is maximally inserted, meaning that if you just mention getter_t with no further information, Coq should be able to infer the argument of this type. In other words, saying
Compute getter_t foo_t.
is the same as
Compute #getter_t _ _ foo_t.
which is not correct because #getter_t takes only two arguments (#-notation means "I'm going to list all the arguments, including the implicit ones").
You can say
Compute #getter_t _ foo_t.
Or just
Compute getter_t.
Related
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.
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.))
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.)
I'm trying to deal with canonical structures in ssreflect. There are 2 pieces of code that I took from here.
I will bring pieces for the bool and the option types.
Section BoolFinType.
Lemma bool_enumP : Finite.axiom [:: true; false]. Proof. by case. Qed.
Definition bool_finMixin := Eval hnf in FinMixin bool_enumP.
Canonical bool_finType := Eval hnf in FinType bool bool_finMixin.
Lemma card_bool : #|{: bool}| = 2. Proof. by rewrite cardT enumT unlock. Qed.
End BoolFinType.
Section OptionFinType.
Variable T : finType.
Notation some := (#Some _) (only parsing).
Local Notation enumF T := (Finite.enum T).
Definition option_enum := None :: map some (enumF T).
Lemma option_enumP : Finite.axiom option_enum.
Proof. by case => [x|]; rewrite /= count_map (count_pred0, enumP). Qed.
Definition option_finMixin := Eval hnf in FinMixin option_enumP.
Canonical option_finType := Eval hnf in FinType (option T) option_finMixin.
Lemma card_option : #|{: option T}| = #|T|.+1.
Proof. by rewrite !cardT !enumT {1}unlock /= !size_map. Qed.
End OptionFinType.
Now, suppose I have a function f from finType to Prop.
Variable T: finType.
Variable f: finType -> Prop.
Goal f T. (* Ok *)
Goal f bool. (* Not ok *)
Goal f (option T). (* Not ok *)
In the last two cases I get the following error:
The term "bool/option T" has type "Set/Type" while it is expected to have type "finType".
What am I doing wrong?
The instance search for canonical structures is a bit counter intuitive in these cases. Suppose that you have the following things:
a structure type S, and a type T;
a field proj : S -> T of S;
an element x : T; and
an element st : S that has been declared as canonical, such that proj st is defined as x.
In your example, we would have:
S = finType
T = Type
proj = Finite.sort
x = bool
st = bool_finType.
Canonical structure search is triggered only in the following case: when the type-checking algorithm is trying to find a value to validly fill in the hole in the equation proj _ = x. Then, it will use st : S to fill in this hole. In your example, you expected the algorithm to understand that bool can be used as finType, by transforming it into bool_finType, which is not quite what is described above.
To make Coq infer what you want, you need to use a unification problem of that form. For instance,
Variable P : finType -> Prop.
Check ((fun (T : finType) (x : T) => P T) _ true).
What is going on here? Remember that Finite.sort is declared as a coercion from finType to Type, so x : T really means x : Finite.sort T. When you apply the fun expression to true : bool, Coq has to find a solution for Finite.sort _ = bool. It then finds bool_finType, because it was declared as canonical. So the element of bool is what triggers the search, but not quite bool itself.
As ejgallego pointed out, this pattern is so common that ssreflect provides the special [finType of ...] syntax. But it might still be useful to understand what is going on under the hood.
I’m trying to re-implement an example from CPDT from memory. I wrote:
Inductive myType : Set := MyNat | MyBool.
Definition typeDenote (t : myType) : Set :=
match t with
| MyNat => nat
| MyBool => bool
end.
Inductive unaryOp : myType -> myType -> Set :=
| Twice : unaryOp MyNat MyNat.
Definition twice (n:nat) : nat := n + n.
Definition tunaryDenote (a b : myType) (t : unaryOp a b)
: typeDenote a -> typeDenote b :=
match t with
| Twice => twice
end.
The resulting error is:
Toplevel input, characters 125-130
> | Twice => twice
> ^^^^^
Error: In environment
a : myType
b : myType
t : unaryOp a b
The term "twice" has type "nat -> nat" while it is expected to have type
"forall H : typeDenote ?141, typeDenote ?142"
I don’t understand this error message. I would think that once the match on Twice : unaryOp MyNat MyNat succeeds, Coq infers that a and b are MyNats, and thus typeDenote a -> typeDenote b ≡ nat -> nat, making twice a perfectly fine candidate for the return value. Where’d I go wrong?
Just like #AntonTrunov said, it is typechecked without any issue on my Coq 8.5pl1. However if you need to add some extra annotations for your version to accept the function, you want to have a look at this section of the manual to figure out what to do.
My guess is that you want to have a match ... in ... return ... with to say that the return type should be refined by the information obtained by matching t at the type unaryOp a b (indeed: a and b will take concrete values in the Twice branch).
This is the definition you get using that technique:
Definition tunaryDenote (a b : myType) (t : unaryOp a b)
: typeDenote a -> typeDenote b :=
match t in unaryOp a b return typeDenote a -> typeDenote b with
| Twice => twice
end.
I think that the answer is that Coq's type inference is limited, and does not do the reasoning you want it to do.
Coq's type inference does not do arbitrary calculation but simple unification. It looks at twice, understand that it is nat->nat and concludes that it is not (syntactically) of the form typeDenote a -> TypeDenote b.
If it was doing calculation, it was likely to be non-terminating, since its type system is very sophisticated so you can encode non-trivial computation there.
I tried a newer version of Coq, and like others have said, it typechecks without issue on Coq 8.5. :)