(** * 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 .