mirror of
https://github.com/achlipala/frap.git
synced 2024-11-10 00:07:51 +00:00
Start of Polymorphism
This commit is contained in:
parent
0e74bcf948
commit
b89cb28352
2 changed files with 211 additions and 1 deletions
207
Polymorphism.v
Normal file
207
Polymorphism.v
Normal file
|
@ -0,0 +1,207 @@
|
|||
(** Formal Reasoning About Programs <http://adam.chlipala.net/frap/>
|
||||
* Supplementary Coq material: polymorphism and generic data structures
|
||||
* Author: Adam Chlipala
|
||||
* License: https://creativecommons.org/licenses/by-nc-nd/4.0/ *)
|
||||
|
||||
Require Import Frap.
|
||||
|
||||
Set Implicit Arguments.
|
||||
(* This command sets up automatic inference of tedious arguments. *)
|
||||
|
||||
(* One of the recurring ingredients in effective machine formalization is data
|
||||
* structures, and, as in general programming, we want to be able to define data
|
||||
* structures that are *generic* in the kinds of data they contain. Coq
|
||||
* supports such things in roughly the same way as Haskell and OCaml, via
|
||||
* *parametric polymorphism*, which is closely related to the idea of *generics*
|
||||
* in languages like Java. *)
|
||||
|
||||
(* Our first example: the [option] type family. While Java and friends force
|
||||
* all sorts of different types to include the special value [null], in Coq we
|
||||
* request that option explicitly by wrapping a type in [option]. Specifically,
|
||||
* any value of type [option A], for some type [A], is either [None] (sort of
|
||||
* like [null]) or [Some v] for a [v] of type [A]. *)
|
||||
Inductive option (A : Set) : Set :=
|
||||
| None
|
||||
| Some (v : A).
|
||||
|
||||
Arguments None [A].
|
||||
(* This command asks Coq to *infer* the [A] type for each specific use of
|
||||
* [None]. *)
|
||||
|
||||
(* Here are a few example terms using [option]. *)
|
||||
Example no_number : option nat := None.
|
||||
Example a_number : option nat := Some 42.
|
||||
Example no_number_squared : option (option nat) := None.
|
||||
Example no_number_squared_inside : option (option nat) := Some None.
|
||||
Example a_number_squared : option (option nat) := Some (Some 42).
|
||||
|
||||
(* Pattern matching is the key ingredient for working with inductive definitions
|
||||
* of all sorts. Here are some examples matching on [option]s. *)
|
||||
|
||||
Definition increment_optional (no : option nat) : option nat :=
|
||||
match no with
|
||||
| None => None
|
||||
| Some n => Some (n + 1)
|
||||
end.
|
||||
|
||||
(* Here we use type [A * B] of *pairs*, inhabited by values [(a, b)], with
|
||||
* [a : A] and [b : B]. *)
|
||||
Definition add_optional (po : option (nat * nat)) : option nat :=
|
||||
match po with
|
||||
| None => None
|
||||
| Some (n, m) => Some (n + m)
|
||||
end.
|
||||
|
||||
|
||||
(** * Lists *)
|
||||
|
||||
(* For functional programming (as in Coq), the king of all generic data
|
||||
* structures is the *list*, which you explored a bit in the first problem set.
|
||||
* Let's recap that type definition. *)
|
||||
Inductive list (A : Set) : Set :=
|
||||
| nil
|
||||
| cons (hd : A) (tl : list A).
|
||||
|
||||
Arguments nil [A].
|
||||
|
||||
(* [nil] is the empty list, while [cons], standing for "construct," extends a
|
||||
* list of length [n] into one of length [n+1]. *)
|
||||
|
||||
(* Here are some simple lists. *)
|
||||
|
||||
Example nats0 : list nat := nil.
|
||||
Example nats1 : list nat := cons 1 nil.
|
||||
Example nats2 : list nat := cons 1 (cons 2 nil).
|
||||
|
||||
(* Coq features a wonderful notation system, to help us write more concise and
|
||||
* readable code after introducing new syntactic forms. We will not give a
|
||||
* systematic presentation of the notation system, but we will show many
|
||||
* examples, from which it is possible to infer generality by scientific
|
||||
* induction. And, of course, the interested reader can always check the
|
||||
* notations chapter of the Coq reference manual. *)
|
||||
|
||||
(* First, our examples can get more readable with an infix operator for [cons]. *)
|
||||
|
||||
Infix "::" := cons.
|
||||
|
||||
Example nats1' : list nat := 1 :: nil.
|
||||
Example nats2' : list nat := 1 :: 2 :: nil.
|
||||
|
||||
(* Getting even more fancy, we declare a notation for list literals. *)
|
||||
|
||||
Notation "[ ]" := nil.
|
||||
Notation "[ x1 ; .. ; xN ]" := (cons x1 (.. (cons xN nil) ..)).
|
||||
|
||||
Example nats0'' : list nat := [].
|
||||
Example nats1'' : list nat := [1].
|
||||
Example nats2'' : list nat := [1; 2].
|
||||
Example nats3'' : list nat := [1; 2; 3].
|
||||
|
||||
(* Here are some classic recursive functions that operate over lists.
|
||||
* First, here is how to compute the length of a list. Recall that we put
|
||||
* *implicit* function arguments in curly braces, asking Coq to infer them at
|
||||
* call sites. *)
|
||||
|
||||
Fixpoint length {A} (ls : list A) : nat :=
|
||||
match ls with
|
||||
| nil => 0
|
||||
| _ :: ls' => 1 + length ls'
|
||||
end.
|
||||
|
||||
(* The first problem set involved an exercise with list append and reverse
|
||||
* operations. To avoid spoiling the proofs there about those functions, we
|
||||
* will give their definitions here without proving the classic theorems from
|
||||
* the problem set. *)
|
||||
|
||||
Fixpoint app {A} (ls1 ls2 : list A) : list A :=
|
||||
match ls1 with
|
||||
| nil => ls2
|
||||
| x :: ls1' => x :: app ls1' ls2
|
||||
end.
|
||||
|
||||
Infix "++" := app.
|
||||
|
||||
Fixpoint rev {A} (ls : list A) : list A :=
|
||||
match ls with
|
||||
| nil => nil
|
||||
| x :: ls' => rev ls' ++ [x]
|
||||
end.
|
||||
|
||||
(* One of the classic gotchas in functional-programming class is how slow this
|
||||
* naive [rev] is. Each [app] operation requires linear time, so running
|
||||
* linearly many [app]s brings us to quadratic time for [rev]. Using a helper
|
||||
* function, we can bring [rev] to its optimal linear time. *)
|
||||
|
||||
Fixpoint rev_append {A} (ls acc : list A) : list A :=
|
||||
match ls with
|
||||
| nil => acc
|
||||
| x :: ls' => rev_append ls' (x :: acc)
|
||||
end.
|
||||
|
||||
(* This function [rev_append] takes an extra *accumulator* argument, in which we
|
||||
* gradually build up the original input in reversed order. The base case just
|
||||
* returns the accumulator. Now reversal just needs to do a [rev_append] with
|
||||
* an empty initial accumulator. *)
|
||||
|
||||
Definition rev' {A} (ls : list A) : list A :=
|
||||
rev_append ls [].
|
||||
|
||||
(* A few test cases can help convince us that this seems to work. *)
|
||||
|
||||
Compute rev [1; 2; 3; 4].
|
||||
Compute rev' [1; 2; 3; 4].
|
||||
Compute rev ["hi"; "bye"; "sky"].
|
||||
Compute rev' ["hi"; "bye"; "sky"].
|
||||
|
||||
(* OK, great. Now it seems worth investing in a correctness proof. We'll
|
||||
* discover it interactively in class, but here's a worked-out final
|
||||
* answer, with the several lemmas that we discover are useful. *)
|
||||
|
||||
(* List concatenation is associative. *)
|
||||
Lemma app_assoc : forall A (ls1 ls2 ls3 : list A),
|
||||
(ls1 ++ ls2) ++ ls3 = ls1 ++ (ls2 ++ ls3).
|
||||
Proof.
|
||||
induct ls1; simplify.
|
||||
|
||||
equality.
|
||||
|
||||
rewrite IHls1.
|
||||
equality.
|
||||
Qed.
|
||||
|
||||
(* The natural correctness condition for [rev_append]: it does what it says on
|
||||
* the package, combining reversal with appending! *)
|
||||
Lemma rev_append_ok : forall A (ls acc : list A),
|
||||
rev_append ls acc = rev ls ++ acc.
|
||||
Proof.
|
||||
induct ls; simplify.
|
||||
|
||||
equality.
|
||||
|
||||
rewrite IHls.
|
||||
simplify.
|
||||
rewrite app_assoc.
|
||||
simplify.
|
||||
equality.
|
||||
Qed.
|
||||
|
||||
(* Concatenating the empty list has no effect. *)
|
||||
Lemma app_nil : forall A (ls : list A),
|
||||
ls ++ [] = ls.
|
||||
Proof.
|
||||
induct ls; simplify.
|
||||
|
||||
equality.
|
||||
|
||||
equality.
|
||||
Qed.
|
||||
|
||||
(* Now we can prove equivalence of [rev'] and [rev], with no new induction. *)
|
||||
Theorem rev'_ok : forall A (ls : list A),
|
||||
rev' ls = rev ls.
|
||||
Proof.
|
||||
simplify.
|
||||
unfold rev'.
|
||||
rewrite rev_append_ok.
|
||||
apply app_nil.
|
||||
Qed.
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
This is an in-progress, open-source book by [Adam Chlipala](http://adam.chlipala.net/) simultaneously introducing [the Coq proof assistant](http://coq.inria.fr/) and techniques for proving correctness of programs. That is, the game is doing completely rigorous, machine-checked mathematical proofs, showing that programs meet their specifications.
|
||||
|
||||
Just run `make` here to build everything, including the book `frap.pdf` and the accompanying Coq source modules. Alternatively, run `make lib' to build just the book library, not the chapter example files or PDF.
|
||||
Just run `make` here to build everything, including the book `frap_book.pdf` and the accompanying Coq source modules. Alternatively, run `make lib' to build just the book library, not the chapter example files or PDF.
|
||||
|
||||
# Code associated with the different chapters
|
||||
|
||||
The main narrative, also present in the book PDF, presents standard program-proof ideas, without rigorous proofs. Matching Coq files here show how to make it rigorous. Interleaved with that narrative, there are also other lectures' worth of material, for building up more practical background on Coq itself. That secondary track appears in this list, too, at a higher level of indentation.
|
||||
|
||||
* Chapter 2: `BasicSyntax.v`
|
||||
* `DataStructures.v`: polymorphism and generic data structures
|
||||
* Chapter 3: `Interpreters.v`
|
||||
* Chapter 4: `TransitionSystems.v`
|
||||
* Chapter 5: `ModelChecking.v`
|
||||
|
|
Loading…
Reference in a new issue