I have a datatype that depends on a predicate P : a -> Type, meaning that some of its data constructors refer have an implicit P x as argument. I would like idris to be able to automatically infer this implicit. For this, I annotated the implicit with the keyword auto and I wrote a function isP : (x : a) -> Dec (P x) with a %hint annotation before the type declaration. Namely, something like:
module P
P : a -> Type
%hint
isP : (x : a) -> Dec (P x)
and in a separate file
module Main
import P
data Foo : Type where
Bar : (x : a) -> .{auto prf : P x} -> Foo
That said, I can't declare values of said Foo type because Idris claims the it can't infer prf.
Is this because prf has type P x instead of Dec (P x) or is it because the %hint flag does not get imported?
In either case, how can I get Idris to use a Dec value to try to find an implicit?
The %hint flag is imported, as you guess, it's because Dec (P x) is different than P x.
There's a trick though, you can use this datatype:
data IsYes : prop -> Type where
SoTrue : IsYes (Yes prop)
(basically, you define a type that holds a Yes for a given property)
and then you can use default instead of auto to check if the property holds:
data Foo : Type where
Bar : (x : a) -> .{default SoTrue prf : IsYes (isP x)} -> Foo
Note: with this trick, you don't even need %hint anymore, you just check the outcome of isP at compile time.
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.
The Coq standard library has two subsets of classes modules, one anchored in Coq.Classes.RelationClasses and the other in Coq.Classes.CRelationClasses. The latter seems to have been added more recently (2012).
I must be missing something obvious as they both look very similar to me.
What is the reason they exist?
The key difference is in the type of relations they support:
(* RelationClasses, actually defined in Relation_Definitions *)
Definition relation (A : Type) := A -> A -> Prop.
(* CRelationClasses *)
Definition crelation (A : Type) := A -> A -> Type.
In addition to crelation producing a Type instead of a Prop, another key difference is that crelation is universe polymorphic, but relation is not.
Require Import Relation_Definitions.
Require Import Coq.Classes.CRelationClasses.
Set Printing Universes.
Print relation.
(*
relation =
fun A : Type#{Coq.Relations.Relation_Definitions.1} => A -> A -> Prop
: Type#{Coq.Relations.Relation_Definitions.1} ->
Type#{max(Set+1,Coq.Relations.Relation_Definitions.1)}
*)
Print crelation.
(*
crelation#{u u0} =
fun A : Type#{u} => A -> A -> Type#{u0}
: Type#{u} -> Type#{max(u,u0+1)}
(* u u0 |= *)
*)
Check (fun (R: crelation (Type -> Type)) => R crelation).
Fail Check (fun (R: relation (Type -> Type)) => R relation).
(*
The command has indeed failed with message:
In environment
R : relation (Type#{test.7} -> Type#{test.8})
The term "relation" has type
"Type#{Coq.Relations.Relation_Definitions.1} ->
Type#{max(Set+1,Coq.Relations.Relation_Definitions.1)}"
while it is expected to have type "Type#{test.7} -> Type#{test.8}"
(universe inconsistency: Cannot enforce Coq.Relations.Relation_Definitions.1
<= test.8 because test.8 < Coq.Relations.Relation_Definitions.1).
*)
Suppose I have a definition f : x -> y -> z where x can be easily inferred.
I therefore choose to make x an implicit argument using Arguments.
Consider the following example:
Definition id : forall (S : Set), S -> S :=
fun S s => s.
Arguments id {_} s.
Check (id 1).
Clearly S = nat can be and is inferred by Coq, and Coq replies:
id 1
: nat
However, at a later time, I want to make the implicit argument explicit, say, for readability.
In other words, I would like something like:
Definition foo :=
id {nat} 1. (* We want to make it clear that the 2nd argument is nat*)
Is this possible at all?
If so, what is the appropriate syntax?
You can prepend the name with # to remove all implicits and provide them explicitly:
Check #id nat 1.
You can also use (a:=v) to pass an implicit argument by name. This can both clarify what argument is being passed and also allows you to pass some implicits without passing _ for the others:
Check id (S:=nat) 1.
Definition third {A B C:Type} (a:A) (b:B) (c:C) := c.
Check third (B:=nat) (A:=unit) tt 1 2.
One possible way is by prepending # to the definition.
For example:
Definition id : forall (S : Set), S -> S :=
fun S s => s.
Arguments id {_} s.
Check #id nat 1.
Which results in:
id 1
: nat
I am wondering how best to handle this sort of situation in Coq:
Suppose I need to define and prove a number of things about an arbitrary structure (for the purposes of this discussion, let's say a set with a binary relation). Of course, I can always supply the set and relation as arguments for each such definition/proof:
Inductive star (X : Set) (R : X -> X -> Prop) := ...
Lemma star_trans (X : Set) (R : X -> X -> Prop) : ...
Naturally, this gets tiresome after a while. What I would like to do is have X and R as parameters locally within some demarcated area of code like so:
Parameter X : Set.
Parameter R : X -> X -> Prop.
Inductive star := ...
Lemma star_trans : ...
in such a way that the definitions and theorems, when used outside this area of code, capture X and R under universal quantification in order to give them the proper type. For instance, Check star should yield star : forall X : Set, (X -> X -> Prop) -> X -> X -> Prop.
I figured that this might be what modules are for, but I can't figure out how to use them for this situation.
This is exactly what the "Section mechanism" does: see https://coq.inria.fr/distrib/current/refman/Reference-Manual004.html#Section .
Section rel_star.
Variables (X : Set) (R : X -> X -> Prop).
Inductive star := ...
Lemma star_trans: ...
End rel_star.
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. :)