Paul,

Sometimes it helps to go exhaustively through every detail to be sure there is no magic going on. Proceed only if you want exhaustive detail...

If it seems that people are skipping some steps in their argument, it is because they are! They already understand it so well that they forgot to add them. Here is what I believe is an excruciatingly complete proof, to assure you that no handwaving is going on.

The values of Nat are defined inductively on their structure, using its initial algebra. Take the data type definition

  data Nat = Zero | Succ Nat

There is actually an implied undefined as well, so this is really

  Nat = undefined | Zero | Succ Nat

Just as 3 = const 3 x for any x, we can convert from pointed to pointfree notation (i.e. go from a recursive definition to a least-defined fixed point definition):

  f = const undefined | const Zero | Succ
  Nat = LFP (m -> infinity) ( f^m undefined )

[ For the notationally picky, I am using | instead of + in the functor for clarity, which causes no ambiguity.]

Because we are overachievers, we will prove our theorem not just for the least-defined fixed point of f, but change the limit to a union and prove it for (f^m undefined) for every m, which includes the fixed point.

Why the extra work? Because now we have an inductive argument. The base case is undefined, and each step is another application of f.

DEFINITION:

add Zero     n = n                 -- Line 1
add (Succ m) n = Succ (add m n)    -- Line 2

THEOREM:

forall x :: Nat, add x Zero = x

PROOF: By induction

 BASE CASE:  x = f^0 undefined = undefined

  add undefined Zero = undefined
    { Line 1, strict pattern match failure in first arg }

 STEP CASE:  Assume add x Zero = x, Prove add y Zero = y where y in f x

   What y are in f x?
   f x = (const undefined | const Zero | Succ) x
       = const undefined x | const Zero x | Succ x
       = undefined | Zero | Succ x

   We have to consider each of these possibilities for y.

   1) add undefined Zero = undefined   { Base case }

   2) add Zero      Zero = Zero        { Def. line 1 }

   3) add (Succ x) Zero
    = Succ (add x Zero)                { Def. line 2 }
    = Succ x                           { Step case assumption }

   This exhausts the three cases for y.

Therefore, by induction add x Zero = x for all x :: Nat

Note how structural induction maps to induction over integers. You do not have to enumerate some flattened form of the domain and do induction over their enumerated order. Indeed, some domains (like binary trees) are not even countably infinite, so the induction hypothesis would not be valid when used in this way.

Instead you rely on the fact that all algebraic data types already have an (at most countably infinite) complete partial order based on constructor application (or eqivalently, initial algebra composition) [and always remembering that there is an implied union in | with undefined], grounded at undefined, and that consequently you can do induction over constructor application.

I hope that there are no errors in the above and that I have not left out even the smallest detail. You should be able to see from the above that structural induction works on any algebraic data type.

Obviously, after the first time only a masochist would be so verbose. But the induction hypothesis does after all require a first time! :)

Dan Weston

PR Stanley wrote:
Hi
One of you chaps mentioned the Nat data type
data Nat = Zero | Succ Nat

Let's have
add :: Nat -> Nat -> Nat
add Zero n = n
add (Succ m)n = Succ (add m n)

Prove
add m Zero = m

I'm on the verge of giving up on this. :-(
Cheers
Paul

_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to