I'm trying to learn Coq, but I find it hard to make the leap from what I read in Software Foundations and Certified Programming with Dependent Types to my own use cases.
In particular, I thought I'd try to make a verified version of the nth
function on lists. I managed to write this:
Require Import Arith.
Require Import List.
Import ListNotations.
Lemma zltz: 0 < 0 -> False.
Proof.
intros. contradict H. apply Lt.lt_irrefl.
Qed.
Lemma nltz: forall n: nat, n < 0 -> False.
Proof.
intros. contradict H. apply Lt.lt_n_0.
Qed.
Lemma predecessor_proof: forall {X: Type} (n: nat) (x: X) (xs: list X),
S n < length (x::xs) -> n < length xs.
Proof.
intros. simpl in H. apply Lt.lt_S_n. assumption.
Qed.
Fixpoint safe_nth {X: Type} (n: nat) (xs: list X): n < length xs -> X :=
match n, xs with
| 0, [] => fun pf: 0 < length [] => match zltz pf with end
| S n', [] => fun pf: S n' < length [] => match nltz (S n') pf with end
| 0, x::_ => fun _ => x
| S n', x::xs' => fun pf: S n' < length (x::xs') => safe_nth n' xs' (predecessor_proof n' x xs' pf)
end.
This works, but it raises two questions:
- How would experienced Coq users write this? Are the three lemmas really necessary? Is this a use case for
{ | }
types? - How do I call this function from other code, i.e., how do I supply the required proofs?
I tried this:
Require Import NPeano.
Eval compute in if ltb 2 (length [1; 2; 3]) then safe_nth 2 [1; 2; 3] ??? else 0.
But of course this won't work until I figure out what to write for the ???
part. I tried putting (2 < length [1; 2; 3])
there but that has type Prop
rather than type 2 < length [1; 2; 3]
. I could write and prove a lemma of that specific type, and that works. But what's the general solution?