Make dependent parameter available in Functor definition - interface

I have the following wrapper for Vect
data Foo : (r : Nat) -> (t: Type) -> Type where
MkFoo : Vect r t -> Foo r t
I'd like to implement Functor for Foo, and use the value of r in the implementation, but I can't get it to compile.
Functor (Foo r) where
map f (MkFoo v) = MkFoo (ff v) where
ff : {r' : Nat} -> Vect r' a -> Vect r' b
gives
Error: While processing right hand side of map. r is not accessible in this
context.
Foo.idr:61:28--61:32
|
61 | map f (MkFoo v) = MkFoo (ff v) where
| ^^^^
This worked in Idris 1 but I can't figure out how to port it to Idris2. I've tried making r not erased, with
Functor ({r : _} -> Foo r) where
map f (MkFoo v) = MkFoo (ff v) where
ff : {r' : Nat} -> Vect r' a -> Vect r' b
but I get
Error: While processing type
of Functor implementation at Foo.idr:60:1--62:46. When
unifying Type -> Type and Type.
Mismatch between: Type -> Type and Type.
Foo.idr:60:21--60:26
|
60 | Functor ({r : _} -> Foo r) where
| ^^^^^

Try:
{r : _} -> Functor (Foo r) where
map f (MkFoo v) = MkFoo (ff v) where
ff : {r' : Nat} -> Vect r' a -> Vect r' b

Related

How can I create a polymorphic "map xs f" function?

The statement of the problem.
Consider this definition of map:
Fixpoint map (xs: list nat): (nat -> nat) -> list nat := match xs with
| nil => fun _ => nil
| x::xs' => fun f => (f x) :: (map xs' f)
end.
It works like this:
Coq < Eval simpl in map (1::2::3::List.nil) (fun x => x + 1).
= 2 :: 3 :: 4 :: nil
: list nat
How can I extend it to work on any types?
For example, in Haskell I can simply write as follows:
map :: forall a b. [a] -> (a -> b) -> [b]
map xs = case xs of
[ ] -> \_ -> [ ]
(x:xs') -> \f -> f x: map xs' f
But in Coq, I do not understand where I could place this forall quantifier.
My efforts.
The syntax reference explains the syntax of Fixpoint thusly:
Fixpoint ident binders {struct ident}? : type? := term
— So evidently there is no place in the syntax for a quantifier that binds a type variable over both binders and type. I tried placing forall here and there by guesswork but I could not make it work.
I can see how the type section could be made polymorphic in the result type of the function parameter without touching the binders section:
Fixpoint map (xs: list nat): forall B, (nat -> B) -> list B := match xs with
| nil => fun _ => nil
| x::xs' => fun f => f x :: (map xs' f)
end.
— But unfortunately this also gives an error, and cryptic enough for me at that:
In environment
map : list nat -> forall B : Type, (nat -> B) -> list B
xs : list nat
T : Type
The term "nil" has type "list ?A" while it is expected to have type
"(nat -> T) -> list T".
So, even for this simpler case I have no solution.
So, what can be done?
In Coq, the Fixpoint command is just a wrapper around the fix term constructor, which allows us to define anonymous recursive functions. A direct definition of map could be given as follows:
Require Import Coq.Lists.List.
Import ListNotations.
Definition map_anon : forall A B, (A -> B) -> list A -> list B :=
fix map A B (f : A -> B) (l : list A) : list B :=
match l with
| [] => []
| x :: l => f x :: map A B f l
end.
This is morally equivalent to the following definition:
Fixpoint map A B (f : A -> B) (l : list A) : list B :=
match l with
| [] => []
| x :: l => f x :: map A B f l
end.
Notice that A and B are bound as regular variables: in Coq, there is no distinction between types and terms, and these declarations causes Coq to infer their types as being Type. No forall quantifier is needed for the definition.
You can list all your arguments, including type arguments, after the name of the function. You'll put any arguments that depend on other arguments after the argument they depend on.
Fixpoint map (A B: Type) (xs: list A) (f: A -> B): list B :=
[...]
If you prefer foralls, you simply need to move everything (except the recursive argument and any arguments it depends on) to after the :.
Fixpoint map (A B: Type) (xs: list A): forall (f: A -> B), list B :=
[...]
Two things to note here. Since nothing after f depends on f, you could use the -> notation. This is purely notation and doesn't have any semantic difference.
Fixpoint map (A B: Type) (xs: list A): (A -> B) -> list B :=
[...]
The other thing to note is that when the arguments are after the : like this, we have to define a function, not just something in list B.
Fixpoint map (A B: Type) (xs: list A): (A -> B) -> list B :=
fun f => [...]
This is why you got the error The term "nil" has type "list ?A" while it is expected to have type "(nat -> T) -> list T".. We needed a function, not just something of type list B, which is what nil is.

Why does coq's typechecker reject my map definition?

I try to experiment with list's definition.
For example let's see this definition:
Inductive list1 : Type -> Type := nil1 : forall (A : Type), list1 A
| cons1 : forall (A : Type), A -> list1 A -> list1 A.
You might think that the definition above is equivalent to this:
Inductive list0 (A : Type) : Type := nil0 : list0 A
| cons0 : A -> list0 A -> list0 A.
Why this map:
Fixpoint map0 {A : Type} {B : Type} (f : A -> B) (xs : list0 A) : list0 B :=
match xs with
nil0 _ => nil0 B
| cons0 _ v ys => cons0 B (f v) (map0 f ys)
end.
accepted, but this one is not:
Fail Fixpoint map1 {A : Type} {B : Type} (f : A -> B) (xs : list1 A) :=
match xs with
nil1 _ => nil1 B
| cons1 _ v ys => cons1 B (f v) (map1 f ys)
end.
?
This is indeed a confusing aspect of datatype definitions. The problem is that list1 is not equivalent to list0, because of how indices and parameters are treated in these definitions. In Coq jargon, an "index" means an argument declared to the right of the colon, as in list1. A "parameter", by contrast, is an argument declared to the left of the colon, as A in list0.
When you use an index, the return type of match expressions must be generic with respect to the index. This can be seen in the type of list1_rec, a combinator for writing recursive definitions on lists:
Check list1_rec.
list1_rec
: forall P : forall T : Type, list1 T -> Set,
(forall A : Type, P A (nil1 A)) ->
(forall (A : Type) (a : A) (l : list1 A), P A l -> P A (cons1 A a l)) ->
forall (T : Type) (l : list1 T), P T l
This type says that given a generic type P indexed by lists and an element l : list1 A, you can produce a result of type P A l by telling Coq what to return on nil1 and cons1. However, the type of the cons1 branch (the third argument of list1) says that the branch must work not only for the A that appears in the type of l, but also for all other types A'. Compare this to the type of list0_rec:
Check list0_rec.
list0_rec
: forall (A : Type) (P : list0 A -> Set),
P (nil0 A) ->
(forall (a : A) (l : list0 A), P l -> P (cons0 A a l)) ->
forall l : list0 A, P l
The branch of cons0 does not have the forall A bit, which means that the branch only has to work for the type A in l : list0 A.
This makes a difference when writing a function such as map. In map0, we are allowed to apply f : A -> B because we know that the argument of cons0 has type A. In map1, the argument of cons1 has a different generic type A', leading to this error message:
Fail Fixpoint map1 {A : Type} {B : Type} (f : A -> B) (xs : list1 A) :=
match xs with
nil1 A' => nil1 B
| cons1 A' v ys => cons1 B (f v) (map1 f ys)
end.
(* The term "v" has type "A'" while it is expected to have type "A". *)
To be complete, you can define function map over list1 :
Fixpoint map1 {A : Type} {B : Type} (f : A -> B) (xs : list1 A) :=
match xs with
| nil1 A' => fun _ => nil1 B
| cons1 A' v ys => fun f => cons1 B (f v) (map1 f ys)
end f.
This is an example of the so-called convoy pattern. Usually, one needs to add a return clause to the match construct so that it typechecks, but here Coq is smart enough to infer it.
However, I certainly discourage using this definition of lists as it will be cumbersome to use in similar cases.

Proving theorems about inductive types using _ind; App rule

Variables A B : Prop.
Theorem proj1 : A /\ B -> A.
In order to learn, I'm trying to prove this theorem by explicitly writing down a proof term using and_ind.
I would assume the correct proof term is
fun (H : A /\ B) => and_ind A B A (fun a _ => a) H
But this raises an error, and instead the correct term is
fun (H : A /\ B) => and_ind (fun a _ => a) H
I don't understand this. The definition of and_ind is
and_ind =
fun (A B P : Prop) (f : A -> B -> P) (a : A /\ B) => match a with
| conj x x0 => f x x0
end
: forall A B P : Prop, (A -> B -> P) -> A /\ B -> P
How can I see from that that the parameters (A B P : Prop) have to be omitted?
The "App" rule
from the Reference Manual seems to state clearly that quantified variables have to be explicitly "instantiated" using the function application syntax that I tried.
In Coq, you can declare some arguments of a function as implicit. When you call the function, you don't supply values for the implicit arguments; Coq automatically tries to infer suitable values, based on other information available during type checking. The A, B and P arguments of and_ind are all declared as implicit, and can be inferred from the type of the H argument and the result type of the function argument.
You can see what arguments are considered implicit with the About command:
About and_ind.
(* and_ind : forall A B P : Prop, (A -> B -> P) -> A /\ B -> P *)
(* Arguments A, B, P are implicit *)
(* Argument scopes are [type_scope type_scope type_scope function_scope _] *)
(* and_ind is transparent *)
(* Expands to: Constant Coq.Init.Logic.and_ind *)
You can turn off implicit arguments with an individual call with an # sign:
Check fun A B H => #and_ind A B A (fun a _ => a) H.
(* fun (A B : Prop) (H : A /\ B) => and_ind (fun (a : A) (_ : B) => a) H *)
(* : forall A B : Prop, A /\ B -> A *)
(Notice that Coq automatically omits implicit arguments when printing a term as well.)
The Coq manual has more information on that subject.

Type that contains all functions of N elements in Coq

I am learning Coq and as an exercise I want to define a type FnArity (N:nat) to encode all functions of N arguments. That is:
Check FnArity 3 : (forall A B C : Set, A -> B -> C).
Should work but
Check FnArity 2 : (forall A B C D : Set, A -> B -> C -> D).
Should not work.
This is for pedagogic purposes so any relevant resources are welcome.
EDIT: From the answers so far I realize I am probably approaching this wrong so here is the proposition I am trying to prove:
Composing N composition operators is equivalent to a composition operator that composes f and g where g expects N arguments. In haskell-ish terms:
(.).(.) ... N times ... (.).(.) f g = \a1, .. aN -> f (g (a1, .. , aN))
EDIT2: In coq terms:
Definition compose { A B C : Type } (F : C -> B) (G : A -> C ) : A -> B :=
fun x => F ( G (x) ).
Definition compose2 {A1 A2 B C : Type} (F : C -> B) (G : A1 -> A2 -> C)
: A1 -> A2 -> B := fun x y => F ( G x y ).
Definition compose3 {A1 A2 A3 B C : Type} (F : C -> B) (G : A1 -> A2 -> A3 -> C)
: A1 -> A2 -> A3 -> B := fun x y z => F ( G x y z ).
(* The simplest case *)
Theorem dual_compose : forall {A B C D : Type} (f: D -> C) (g : A -> B -> D) ,
(compose compose compose) f g = compose2 f g.
Proof. reflexivity. Qed.
Theorem triple_compose : forall {A1 A2 A3 B C : Type} (f: C -> B) (g : A1 -> A2 -> A3 -> C) ,
(compose (compose (compose) compose) compose) f g =
compose3 f g.
What I want is to define the generalized theorem for composeN.
The types that you wrote down do not quite represent what you stated in your problem: forall A B C, A -> B -> C is not the type of all functions of three arguments, but the type of certain polymorphic functions of two arguments. You probably meant to write something like { A & { B & { C & A -> B -> C }}} instead, where A, B and C are existentially quantified. You probably also meant to say Compute (FnArity 3) instead of using the Check command, since the latter is the one that evaluates a term (and, as jbapple pointed out, no term can have the type that you had originally written).
Here's a piece of code that does what you want, I think. We start by writing a function FnArityAux1 : list Type -> Type -> Type, that computes a function type with arguments given on a list:
Fixpoint FnArityAux1 (args : list Type) (res : Type) : Type :=
match args with
| [] => res
| T :: args' => T -> FnArityAux1 args' res
end.
For instance, FnArityAux1 [nat; bool] bool evaluates to nat -> bool -> bool. We can then use this function to define FnArity as follows:
Fixpoint FnArityAux2 (args : list Type) (n : nat) : Type :=
match n with
| 0 => { T : Type & FnArityAux1 args T }
| S n' => { T : Type & FnArityAux2 (args ++ [T]) n' }
end.
Definition FnArity n := FnArityAux2 [] n.
In this definition, we use another auxiliary function FnArityAux2 that has an argument args whose purpose is to carry around all the existentially quantified types produced so far. For each "iteration step", it quantifies over another type T, adds that type to the list of arguments, and recurses. When the recursion is over, we use FnArityAux1 to combine all accumulated types into a single function type. Then, we can define FnArity simply by starting the process with an empty list -- that is, no quantified types at all.
No, this is not possible, since (forall A B C : Set, A -> B -> C) is uninhabited.
Goal (forall A B C : Set, A -> B -> C) -> False.
intros f.
specialize (f True True False).
apply f; trivial.
Qed.
As such, Check FnArity 3 : (forall A B C : Set, A -> B -> C). can never work.

extracting evidence of equality from match

I am trying to make the following work:
Definition gen `{A:Type}
{i o: nat}
(f: nat -> (option nat))
{ibound: forall (n n':nat), f n = Some n' -> n' < i}
(x: svector A i) (t:nat) (ti: t < o): option A
:= match (f t) with
| None => None
| Some t' => Vnth x (ibound t t' _)
end.
In place of last "_" I need an evidence that "f t" is equals to "Some t'". I could not figure out how to get it from the match. Vnth is defined as:
Vnth
: ∀ (A : Type) (n : nat), vector A n → ∀ i : nat, i < n → A
Writing this function requires an instance of what is known as the convoy pattern (see here). I believe the following should work, although I can't test it, since I don't have the rest of your definitions.
Definition gen `{A:Type}
{i o: nat}
(f: nat -> (option nat))
{ibound: forall (n n':nat), f n = Some n' -> n' < i}
(x: svector A i) (t:nat) (ti: t < o): option A
:= match f t as x return f t = x -> option A with
| None => fun _ => None
| Some t' => fun p => Vnth x (ibound t t' p)
end (eq_refl (f t)).