Library dependentElimination


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
  | 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
  | 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
  | 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
  | 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
  
  | 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
  | witness t h => wit t h
  end .