In these implementations of curry and compose :
def uncurry[A,B,C] (f : (A => B => C)) : (A , B) => C = (a , b) => f(a) (b)
def compose [A ,B , C] (f : B => C , g : A => B) : A => C = (a : A) => f(g(a))
In compose implementation is (a : A) just syntactic sugar for (a) as
def compose [A ,B , C] (f : B => C , g : A => B) : A => C = (a : A) => f(g(a))
compiles without issue ?
Yes, but is the other way around: (a) is syntactic sugar for (a:A), since the compiler can infer that a is of type A. You don't even need the parenthesis for (a):
def compose [A ,B , C] (f : B => C , g : A => B) : A => C = a => f(g(a))
Not necessarily related to the question, but for this particular case, that functionality is already available, so you can just do:
def compose [A ,B , C] (f : B => C , g : A => B) : A => C = f compose g
Related
Now I'm reading next book about Scala and found some exercises for self-evaluation.
Page 27, exercise 2.7:
Let's look at another example, currying, which converts a finction f of two arguments into a function of one argument that partially applies f. Here again there's only one implementation that compiles. Write this implementation
def curry[A, B, C](f: (A, B) => C): A => (B => C) = ...
It's really important to mention, that exercise says about only one possible implementation, that compiles. If book mean that there is physically only one option, then I don't understand why the book says there is only one solution.
For example, solution from book:
def curry[A, B, C](f: (A, B) => C): A => (B => C) = a => b => f(a,b)
And my own one:
def curry[A, B, C](f: (A, B) => C): A => (B => C) = a => f(a, _)
Both variants were successfully compiled. So,let me introduce my vision of solution:
Let's imagine next category:
f - morphism which defined via f: (A, B) → C
g - morphism which defined via g: B → C
l - morphism which defined via l: A → C
As I understand such category, we have next expression for function output:
A → (B → C) = A → C
Such transition can be done because of composition property of category. And that's why I'm used more short expression for A → (B → C as a => f(a, _).
Do I understand it correct?
I have a two-part question.
Goal: I want to define the notion of a category internal to a given category.
I came up with the following simple code, that however produces an inexplicable error message, namely:
File "./Category.v", line 5, characters 4-5:
Syntax error: '}' expected after [record_fields] (in [constructor_list_or_record_decl]).
Record Category :=
{ Ob : Type;
Hom : Ob -> Ob -> Type;
_o_ : forall {a b c}, Hom b c -> Hom a b -> Hom a c;
1 : forall {x}, Hom x x;
Assoc : forall a b c d (f : Hom c d) (g : Hom b c) (h : Hom a b),
f o (g o h) = (f o g) o h;
LeftId : forall a b (f : Hom a b), 1 o f = f;
RightId : forall a b (f : Hom a b), f o 1 = f;
Truncated : forall a b (f g : Hom a b) (p q : f = g), p = q }.
How to "internalize" this definition? Specifically, I want to define a category internal to the above specified type Category. This means a type "internal category" such that the objects are categories, i.e. belong to the above type Category and the arrows are morphisms of the type Category. All of this assuming the relevant pullbacks exist. If this is not clear, please refer to https://ncatlab.org/nlab/show/internal+category
My guess is that the best way to pull this off is to define the internal category as a Module inheriting from the above specified type Category. The aim is to ultimately get a hierarchy of structures internal to an "ambient category". So I ultimately want to go beyond just defining a category internal to another category, but other structures as well. Any pointers are appreciated.
You are not using Agda, so _o_ does not define an infix notation. Also, you cannot have a filed named 1 either. Again, you would have to rely on the notation system.
The following is accepted.
Record Category := {
Ob : Type ;
Hom : Ob -> Ob -> Type ;
comp : forall {a b c}, Hom b c -> Hom a b -> Hom a c ;
id : forall {x}, Hom x x ;
Assoc :
forall a b c d (f : Hom c d) (g : Hom b c) (h : Hom a b),
comp f (comp g h) = comp (comp f g) h ;
LeftId : forall a b (f : Hom a b), comp id f = f ;
RightId : forall a b (f : Hom a b), comp f id = f ;
Truncated : forall a b (f g : Hom a b) (p q : f = g), p = q
}.
Then you can use notations for composition and the unit:
Arguments comp {_ _ _ _} _ _.
Notation "f ∘ g" := (comp f g) (at level 20).
Arguments id {_ _}.
Notation "1" := (id).
Check Assoc.
(* Assoc
: forall (c : Category) (a b c0 d : Ob c) (f : Hom c c0 d)
(g : Hom c b c0) (h : Hom c a b), f ∘ (g ∘ h) = (f ∘ g) ∘ h *)
Check LeftId.
(* LeftId
: forall (c : Category) (a b : Ob c) (f : Hom c a b), 1 ∘ f = f *)
Since Théo already provided a great answer to your first question, I'll focus on the second one. In principle, you can define the concept of internal category simply by translating the textbook definition in Coq. We would begin by defining what it means for something to be a pullback:
Record is_pullback {C : Category}
{X Y Z : Ob C} (f : Hom C X Z) (g : Hom C Y Z) (P : Ob C) := {
proj1 : Hom C P X;
proj2 : Hom C P Y;
pair : forall W (a : Hom C W X) (b : Hom C W Y),
f \o a = g \o b -> Hom C W P;
(* ... plus axioms saying that pairing and projections are
inverses of each other *)
}.
Then, we would define a category with pullbacks as a category with a choice of pullbacks:
Record PBCategory := {
Cat :> Category;
pb : forall X Y Z (f : Hom C X Z) (g : Hom C Y Z), Ob Cat;
pb_is_pb : forall X Y Z f g, is_pullback f g (pb X Y Z f g);
}.
(The :> is a coercion declaration. It tells Coq that every category with pullbacks can be coerced into a category via Cat.)
Finally, we would write down the definition of an internal category:
Record IntCategory (C : PBCategory) := {
ArrOb : Ob C;
ObOb : Ob C;
source : Hom C ArrOb ObOb;
dest : Hom C ArrOb ObOb;
comp : Hom C (pb _ _ _ _ source dest) ArrOb;
(* + axioms *)
}.
At this point, however, I believe that we would hit a wall. Writing down the axioms for an internal category in diagrammatic language is too cumbersome. For instance, when phrasing the associativity axiom, we would have to explicitly reason about the isomorphism
(ArrOb x_ObOb ArrOb) x_ObOb ArrOb ~ ArrOb x_ObOb (ArrOb x_ObOb ArrOb)
which is almost always ignored in a textbook presentation. Alas, it is not possible to define an internal category as a particular instance or extension of your Category type.
It might be possible to circumvent this issue by working with indexed categories instead of an internal category, since every internal category gives rise to an indexed category via the Yoneda embedding. That is, we would formalize what it means for something to be a functor of type C^op -> Cat, and then we could define an internal category as being a representable indexed category. This might simplify the definition, but I am not it would be that much easier to work with...
Edit
Here is a potential encoding, though I don't know if this would suit your application or not: https://x80.org/collacoq/urunebifup.coq. The idea is to express composition not as an arrow whose domain is a pullback, but rather as an operation on arrows (more-or-less like what you would get with the Yoneda embedding).
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.
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.
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.