(** * First-order quantifiers *)
(** [ex P], or simply [exists x, P x], or also [exists x:A, P x], expresses the existence of an [x] of some type [A] in [Set] which satisfies the predicate [P].  This is existential quantification.  [ex2 P Q], or simply [exists2 x, P x & Q x], or also [exists2 x:A, P x & Q x], expresses the existence of an [x] of type [A] which satisfies both predicates [P] and [Q].  Universal quantification is primitively written [forall x:A, Q]. By symmetry with existential quantification, the construction [all P] is provided too. *)
Register ex as core.ex.type.
Register ex_intro as core.ex.intro.
Section Projections.
Variables (A:Prop) (P:A->Prop).
Definition ex_proj1 (x:ex P) : A :=
match x with ex_intro _ a _ => a end.
Definition ex_proj2 (x:ex P) : P (ex_proj1 x) :=
match x with ex_intro _ _ b => b end.
Register ex_proj1 as core.ex.proj1.
Register ex_proj2 as core.ex.proj2.
End Projections.
Inductive ex2 (A:Type) (P Q:A -> Prop) : Prop :=
ex_intro2 : forall x:A, P x -> Q x -> ex2 (A:=A) P Q.
(** [ex2] of a predicate can be projected to an [ex].  This allows [ex_proj1] and [ex_proj2] to be usable with [ex2].  We have two choices here: either we can set up the definition so that [ex_proj1] of a coerced [X : ex2 P Q] will unify with [let (a, _, _) := X in a] by restricting the first argument of [ex2] to be a [Prop], or we can define a more general [ex_of_ex2] which does not satisfy this conversion rule.  We choose the former, under the assumption that there is no reason to turn an [ex2] into an [ex] unless it is to project out the components. *)
Definition ex_of_ex2 (A : Prop) (P Q : A -> Prop) (X : ex2 P Q) : ex P
:= ex_intro P
(let (a, _, _) := X in a)
(let (x, p, _) as s return (P (let (a, _, _) := s in a)) := X in p).
Section ex2_Projections.
Variables (A:Prop) (P Q:A->Prop).
Definition ex_proj3 (x:ex2 P Q) : Q (ex_proj1 (ex_of_ex2 x)) :=
match x with ex_intro2 _ _ _ _ b => b end.
End ex2_Projections.
Definition all (A:Type) (P:A -> Prop) := forall x:A, P x.
Register all as core.all.
(* Rule order is important to give printing priority to fully typed exists *)
Notation "'exists' x .. y , p" := (ex (fun x => .. (ex (fun y => p)) ..))
(at level 200, x binder, right associativity,
format "'[' 'exists'  '/  ' x  ..  y ,  '/  ' p ']'")
: type_scope.
Notation "'exists2' x , p & q" := (ex2 (fun x => p) (fun x => q))
(at level 200, x name, p at level 200, right associativity) : type_scope.
Notation "'exists2' x : A , p & q" := (ex2 (A:=A) (fun x => p) (fun x => q))
(at level 200, x name, A at level 200, p at level 200, right associativity,
format "'[' 'exists2'  '/  ' x  :  A ,  '/  ' '[' p  &  '/' q ']' ']'")
: type_scope.
Notation "'exists2' ' x , p & q" := (ex2 (fun x => p) (fun x => q))
(at level 200, x strict pattern, p at level 200, right associativity) : type_scope.
Notation "'exists2' ' x : A , p & q" := (ex2 (A:=A) (fun x => p) (fun x => q))
(at level 200, x strict pattern, A at level 200, p at level 200, right associativity,
format "'[' 'exists2'  '/  ' ' x  :  A ,  '/  ' '[' p  &  '/' q ']' ']'")
: type_scope.
(** Derived rules for universal quantification *)
Section universal_quantification.
Variable A : Type.
Variable P : A -> Prop.

Theorem gen : forall (B:Prop) (f:forall y:A, B -> P y), B -> all P.
End universal_quantification.