IndPrinciplesInduction Principles

With the Curry-Howard correspondence and its realization in Coq in mind, we can now take a deeper look at induction principles.

Basics

The automatically generated induction principle for nat:

Check nat_ind :
   P : natProp,
    P 0 →
    ( n : nat, P nP (S n)) →
     n : nat, P n.
In English: Suppose P is a property of natural numbers (that is, P n is a Prop for every n). To show that P n holds of all n, it suffices to show:
  • P holds of 0
  • for any n, if P holds of n, then P holds of S n.

We can directly use the induction principle with apply:

Theorem mult_0_r' : n:nat,
  n × 0 = 0.
Proof.
  apply nat_ind.
  - (* n = O *) reflexivity.
  - (* n = S n' *) simpl. intros n' IHn'. rewriteIHn'.
    reflexivity. Qed.
Why the induction tactic is nicer than apply:
  • apply requires extra manual bookkeeping (the intros in the inductive case)
  • apply requires n to be left universally quantified
  • apply requires us to manually specify the name of the induction principle.

Coq generates induction principles for every datatype defined with Inductive, including those that aren't recursive.
If we define type t with constructors c1 ... cn, Coq generates:
    t_ind : P : tProp,
              ... case for c1 ... →
              ... case for c2 ... → ...
              ... case for cn ... →
               n : t, P n
The specific shape of each case depends on the arguments to the corresponding constructor.

An example with no constructor arguments:

Inductive time : Type :=
  | day
  | night.

Check time_ind :
   P : timeProp,
    P day
    P night
     t : time, P t.

An example with constructor arguments:

Inductive natlist : Type :=
  | nnil
  | ncons (n : nat) (l : natlist).

Check natlist_ind :
   P : natlistProp,
    P nnil
    ( (n : nat) (l : natlist),
        P lP (ncons n l)) →
     l : natlist, P l.

In general, the automatically generated induction principle for inductive type t is formed as follows:
  • Each constructor c generates one case of the principle.
  • If c takes no arguments, that case is:
          "P holds of c"
  • If c takes arguments x1:a1 ... xn:an, that case is:
          "For all x1:a1 ... xn:an, if [P] holds of each of the arguments of type [t], then [P] holds of [c x1 ... xn]"
    But that oversimplifies a little. An assumption about P holding of an argument x of type t actually occurs immediately after the quantification of x.

For example, suppose we had written the definition of natlist a little differently:

Inductive natlist' : Type :=
  | nnil'
  | nsnoc (l : natlist') (n : nat).
Now the induction principle case for nsnoc1 is a bit different than the earlier case for ncons:

Check natlist'_ind :
   P : natlist'Prop,
    P nnil'
    ( l : natlist', P l n : nat, P (nsnoc l n)) →
     n : natlist', P n.

Induction Principles for Propositions

Inductive definitions of propositions also cause Coq to generate induction priniciples. For example, recall our proposition ev, repeated here as ev'':

Inductive ev'' : natProp :=
| ev_0 : ev'' 0
| ev_SS (n : nat) : ev'' nev'' (S (S n)).

Check ev''_ind :
   P : natProp,
    P 0 →
    ( n : nat, ev'' nP nP (S (S n))) →
     n : nat, ev'' nP n.
In English, ev''_ind says: Suppose P is a property of natural numbers. To show that P n holds whenever n is even, it suffices to show:
  • P holds for 0,
  • for any n, if n is even and P holds for n, then P holds for S (S n).

The precise form of an Inductive definition can affect the induction principle Coq generates.

Inductive le1 : natnatProp :=
     | le1_n : n, le1 n n
     | le1_S : n m, (le1 n m) → (le1 n (S m)).

Notation "m <=1 n" := (le1 m n) (at level 70).
n could instead be a parameter:

Inductive le2 (n:nat) : natProp :=
  | le2_n : le2 n n
  | le2_S m (H : le2 n m) : le2 n (S m).

Notation "m <=2 n" := (le2 m n) (at level 70).


Check le1_ind :
   P : natnatProp,
    ( n : nat, P n n) →
    ( n m : nat, n <=1 mP n mP n (S m)) →
     n n0 : nat, n <=1 n0P n n0.

Check le2_ind :
   (n : nat) (P : natProp),
    P n
    ( m : nat, n <=2 mP mP (S m)) →
     n0 : nat, n <=2 n0P n0.
The latter is simpler, and corresponds to Coq's own definition.

Explicit Proof Objects for Induction (Optional)

Recall again the induction principle on naturals that Coq generates for us automatically from the Inductive declation for nat.

Check nat_ind :
   P : natProp,
    P 0 →
    ( n : nat, P nP (S n)) →
     n : nat, P n.
There's nothing magic about this induction lemma: it's just another Coq lemma that requires a proof. Coq generates the proof automatically too...

Print nat_ind.
We can rewrite that more tidily as follows:
Fixpoint build_proof
         (P : natProp)
         (evPO : P 0)
         (evPS : n : nat, P nP (S n))
         (n : nat) : P n :=
  match n with
  | 0 ⇒ evPO
  | S kevPS k (build_proof P evPO evPS k)
  end.

Definition nat_ind_tidy := build_proof.
Recursive function build_proof thus pattern matches against n, recursing all the way down to 0, and building up a proof as it returns.