Lately I spent some time learning Coq. It is a very useful programming language, but guess what? It has no IO!
So, once we prove our theorems, what do we do? We extract our code!
Let’s pick a language that has IO, for example Haskell.
For fun, let’s try to prove the following and extract the definitions to Haskell:
The sum of a list containing natural numbers is always greater than or equal to 0.
But, in order to prove this, we must define everything on a core level, that is:
1. We need to define what a list is
2. We need to define what a natural number is
We can, but we won’t do that since they are already defined in Coq itself. So we’ll just go ahead and use “list” and “nat” in our code.
Here’s the definition of our recursive function that will calculate the sum of all the elements in a list of natural numbers:
Fixpoint my_sum (l : list nat) : nat := match l with | nil => 0 | x :: l' => x + my_sum l' end.
It’s a pretty straight-forward definition. Let’s write some examples:
Example my_sum1 : my_sum [1 ; 2 ; 3] = 6. Proof. simpl. reflexivity. Qed. Example my_sum2 : my_sum [] = 0. Proof. simpl. reflexivity. Qed. Example my_sum3 : my_sum [1] = 1. Proof. simpl. reflexivity. Qed.
And now, for the proof of our theorem:
(* Sum of a list of naturals will always produce a result greater than or equal to 0 *)
Theorem my_sum_nat_gt_0 : forall l : list nat, 0 <= my_sum l.
Proof.
intros l.
induction l.
- (* base *) simpl. reflexivity.
- (* i.h. *) simpl. case a.
+ (* zero *) simpl. exact IHl.
+ (* S n *) intros n. rewrite IHl. exact (Plus.le_plus_r (S n) (my_sum l)).
Qed.
Cool! Now for the final part, which is the extraction:
Require Extraction. Extraction Language Haskell. Recursive Extraction my_sum.
And finally, our Haskell code generated by the commands above:
data Nat =
O
| S Nat
data List a =
Nil
| Cons a (List a)
add :: Nat -> Nat -> Nat
add n m =
case n of {
O -> m;
S p -> S (add p m)}
my_sum :: (List Nat) -> Nat
my_sum l =
case l of {
Nil -> O;
Cons x l' -> add x (my_sum l')}
We can now use our function my_sum for which we know our theorem to be true, and we can use it in a programming language that supports IO.
Pretty cool stuff, right? 🙂
Bonus: You can’t do these kind of proofs in Haskell itself, since it has no support for dependent types (we’ll cover that in a future post).