(** * Non-dependent Elimination vs Dependent Elimination. *)
(**
We consider here several types, and define their elimination principles
both in the non-dependent and the dependent cases.
*)
(****************************************************)
(** ** Booleans *)
(**
This is a basic coproduct type [1+1].
*)
Inductive Bool : Type :=
| tt : Bool
| ff : Bool
.
(** *** Non dependent elimination *)
(**
This can be seen as a mapping from the boolean type into its Church
encoding (or catamorphism).
Alternatively, the non-dependent elimination of a boolean [b] can be
seen as the polymorphic function
[[
fun t e => if b then t else e
: forall A, A -> A -> A
]]
Note how both [t] and [e] must have the same type [A].
*)
Definition Bool_elim (b: Bool):
forall A: Type, A->A->A :=
fun (A: Type) (t f: A) =>
match b with
| tt => t
| ff => f
end .
(** *** Dependent elimination *)
(**
This can be seen as a generalization of the non-dependent elimination,
except that the return type now depends on the value of the boolean [b].
That is, the resulting type from the elimination is no longer a fixed
type [A], but it is [F b] for some function [F: Bool -> Type].
Consequently, we no longer need to require that [t] and [e] have a common
type [A]. Instead, is suffices that [t] has the type [F tt] and that [e] has
the type [F ff]. Intuitively, [t] is returned by the elimination when [b]
is [tt], so in that case [F tt] and [F b] coincide. Simiarly, [e] is
returned when [b] is [ff], so [F ff] and [F b] coincide.
Hence, even if t and e have distinct types, when they are returned they
both can be considered as having type [F b].
*)
Definition Bool_dep_elim (b: Bool):
forall F : Bool -> Type, F tt -> F ff -> F b :=
fun (F: Bool -> Type) (t: F tt) (f: F ff) =>
match b with (* as x return F x *)
| tt => t
| ff => f
end .
(** Note that is the above match, t and f have different types, yet it
type-checks!
This requires a non trivial type checking rule. For more details,
you can see the [dependentMatch] file which shows how match works.
*)
(****************************************************)
(** ** Naturals *)
(**
This is a recursive type, corresponding to the initial F-algebra
[F X = 1 + X].
Coq, which is based on the Calculus of Inductive Constructions,
allows recursive types only when [F] is a (covariant) functor. In such
way, it ensures that the logic is kept consistent.
*)
Inductive Nat :=
| Zero : Nat
| Succ : Nat -> Nat
.
(** *** Non dependent elimination *)
(**
A mapping to the Church encoding / catamorphism.
Note that in [A -> (A -> A) -> A], the first argument plays the role
of [Zero], while the second one plays the role of [Succ].
*)
Fixpoint Nat_elim (n: Nat):
forall A: Type, A -> (A -> A) -> A :=
fun (A: Type) (z: A) (s: A -> A) =>
match n with
| Zero => z
| Succ m => s (Nat_elim m A z s)
end .
(** Note that the [Fixpoint] above is checked by Coq to be terminating.
When recursing, each recursive call must be made on a "smaller" term.
This is required to safeguard the logic consistency.
*)
(** *** Dependent elimination *)
(**
Now the return type depends on [n]. The resulting elimination principle
is the usual induction principle on naturals.
*)
Fixpoint Nat_dep_elim (n: Nat):
forall F: Nat -> Type,
F Zero ->
(forall m: Nat, F m -> F (Succ m)) ->
F n :=
fun (F: Nat -> Type)
(z: F Zero)
(s: forall m: Nat, F m -> F (Succ m)) =>
match n with (* as x return F x *)
| Zero => z
| Succ m => s m (Nat_dep_elim m F z s)
end .
(****************************************************)
(** ** Lists of naturals *)
Inductive List: Type :=
| Nil: List
| Cons: Nat -> List -> List
.
(** *** Non dependent elimination *)
(**
Again, its Church encoding / catamorphism.
*)
Fixpoint List_elim (l: List):
forall A: Type, A -> (Nat -> A -> A) -> A :=
fun (A: Type) (nil: A) (cons: Nat -> A -> A) =>
match l with
| Nil => nil
| Cons n l2 => cons n (List_elim l2 A nil cons)
end .
(** *** Dependent elimination *)
(**
The induction principle on List.
*)
Fixpoint List_dep_elim (l: List):
forall F: List -> Type,
F Nil ->
(forall (n: Nat) (l2: List), F l2 -> F (Cons n l2)) ->
F l :=
fun (F: List -> Type)
(nil: F Nil)
(cons: forall (n: Nat) (l2: List), F l2 -> F (Cons n l2)) =>
match l with (* as x return F x *)
| Nil => nil
| Cons n l2 => cons n l2 (List_dep_elim l2 F nil cons)
end .
(****************************************************)
(** ** List of naturals, length-indexed *)
(**
Intuitively, if [n] is a [Nat], the type [SizedList n] below
represents the lists of naturals whose length is exactly [n].
Consequently, were we do not have to inductively define
just one type, but a family of types indexed by a natural. This
is represented by the [Nat -> Type] type below.
The [Nat] argument here is said to be the "index".
Note how the constructors [NilS], [ConsS] return different types
within the family -- i.e. their return type involves a different
index.
*)
Inductive SizedList: Nat -> Type :=
| NilS : SizedList Zero
| ConsS : Nat -> forall (n: Nat), SizedList n -> SizedList (Succ n)
.
(** *** Non dependent elimination *)
(**
Here we should have a fixed return type [A], but since we are eliminating
a family of types, we must let [A] to depend on the [Nat] index.
*)
Fixpoint SizedList_elim (n: Nat) (l: SizedList n):
forall A: Nat -> Type,
A Zero ->
(Nat -> forall (k: Nat), A k -> A (Succ k)) ->
A n :=
fun (A: Nat -> Type)
(nil: A Zero)
(cons: Nat -> forall (k: Nat), A k -> A (Succ k)) =>
match l with (* in SizedList x return A x *)
| NilS => nil
| ConsS m n2 l2 => cons m n2 (SizedList_elim n2 l2 A nil cons)
end .
(** *** Dependent elimination *)
(**
The return type now depends on both the index [Nat], and the list itself!
*)
Fixpoint SizedList_dep_elim (n: Nat) (l: SizedList n):
forall F: (forall m: Nat, SizedList m -> Type),
F Zero NilS ->
(forall (n2: Nat) (k: Nat) (l2: SizedList k),
F k l2 -> F (Succ k) (ConsS n2 k l2)) ->
F n l :=
fun (F: forall m: Nat, SizedList m -> Type)
(nil: F Zero NilS)
(cons: forall (n2: Nat) (k: Nat) (l2: SizedList k),
F k l2 -> F (Succ k) (ConsS n2 k l2)) =>
match l with
(* as x in SizedList y return F y x *)
| NilS => nil
| ConsS m n2 l2 => cons m n2 l2 (SizedList_dep_elim n2 l2 F nil cons)
end .
(****************************************************)
(** ** Existential quantification *)
(**
Here, given the parameters [T] and [P] we define the type [Ex T P].
Unlike SizedList this is not conisdered an indexed family of types.
This is because the constructors do not need to involve different
indexes.
*)
Inductive Ex (T: Type) (P: T -> Type): Type :=
| witness : forall (t: T), P t -> Ex T P
.
(** *** Non dependent elimination *)
(**
Note: the return type does not depend on any index, since there
are none -- [T],[P] are parameters, not indexes.
*)
Definition Ex_elim (T: Type) (P: T -> Type) (w: Ex T P):
(forall A: Type,
(forall (t: T), P t -> A) ->
A) :=
fun (A: Type)
(wit: forall (t: T), P t -> A) =>
match w with
| witness t h => wit t h
end .
(** *** Dependent elimination. *)
Definition Ex_dep_elim (T: Type) (P: T -> Type) (w: Ex T P):
(forall F: Ex T P -> Type,
(forall (t: T) (h: P t), F (witness T P t h)) ->
F w) :=
fun (F: Ex T P -> Type)
(wit: forall (t: T) (h: P t), F (witness T P t h)) =>
match w with (* as x return F x *)
| witness t h => wit t h
end .