(** Formal Reasoning About Programs * Chapter 16: Deriving Programs from Specifications * Author: Adam Chlipala * License: https://creativecommons.org/licenses/by-nc-nd/4.0/ * Some material borrowed from Fiat *) Require Import FrapWithoutSets. Require Import Program Setoids.Setoid Classes.RelationClasses Classes.Morphisms Morphisms_Prop. (** * The computation monad *) Definition comp (A : Type) := A -> Prop. Definition ret {A} (x : A) : comp A := eq x. Definition bind {A B} (c1 : comp A) (c2 : A -> comp B) : comp B := fun b => exists a, c1 a /\ c2 a b. Definition pick_ {A} (P : A -> Prop) : comp A := P. Definition refine {A} (c1 c2 : comp A) := forall a, c2 a -> c1 a. Ltac morphisms := unfold refine, impl, pointwise_relation, bind; hnf; first_order. Global Instance refine_PreOrder A : PreOrder (@refine A). Proof. constructor; morphisms. Qed. Add Parametric Morphism A : (@refine A) with signature (@refine A) --> (@refine A) ++> impl as refine_refine. Proof. morphisms. Qed. Add Parametric Morphism A B : (@bind A B) with signature (@refine A) ==> (pointwise_relation _ (@refine B)) ==> (@refine B) as refine_bind. Proof. morphisms. Qed. Add Parametric Morphism A B : (@bind A B) with signature (flip (@refine A)) ==> (pointwise_relation _ (flip (@refine B))) ==> (flip (@refine B)) as refine_bind_flip. Proof. morphisms. Qed. Theorem bind_ret : forall A B (v : A) (c2 : A -> comp B), refine (bind (ret v) c2) (c2 v). Proof. morphisms. Qed. Notation "'pick' x 'where' P" := (pick_ (fun x => P)) (at level 80, x at level 0). Notation "x <- c1 ; c2" := (bind c1 (fun x => c2)) (at level 81, right associativity). (** * Picking a number not in a list *) (* A specification of what it means to choose a number that is not in a * particular list *) Definition notInList (ls : list nat) := pick n where ~In n ls. (* We can use a simple property to justify a decomposition of the original * spec. *) Theorem notInList_decompose : forall ls, refine (notInList ls) (upper <- pick upper where forall n, In n ls -> upper >= n; pick beyond where beyond > upper). Proof. simplify. unfold notInList, refine, bind, pick_, not. first_order. apply H in H0. linear_arithmetic. Qed. (* A simple traversal will find the maximum list element, which is a good upper * bound. *) Definition listMax := fold_right max 0. (* ...and we can prove it! *) Theorem listMax_upperBound : forall init ls, forall n, In n ls -> fold_right max init ls >= n. Proof. induct ls; simplify; propositional. linear_arithmetic. apply IHls in H0. linear_arithmetic. Qed. (* Now we restate that result as a computation refinement. *) Theorem listMax_refines : forall ls, refine (pick upper where forall n, In n ls -> upper >= n) (ret (listMax ls)). Proof. unfold refine, pick_, ret; simplify; subst. apply listMax_upperBound; assumption. Qed. (* An easy way to find a number higher than another: add 1! *) Theorem increment_refines : forall n, refine (pick higher where higher > n) (ret (n + 1)). Proof. unfold refine, pick_, ret; simplify; subst. linear_arithmetic. Qed. Ltac begin := eexists; simplify; (* We run this next step to hide an evar, so that rewriting isn't too eager to * make up values for it. *) match goal with | [ |- refine _ (?f _) ] => set f end. Ltac finish := match goal with | [ |- refine ?e (?f ?arg) ] => let g := eval pattern arg in e in match g with | ?g' _ => let f' := eval unfold f in f in unify f' g'; reflexivity end end. (* Let's derive an efficient implementation. *) Theorem implementation : { f : list nat -> comp nat | forall ls, refine (notInList ls) (f ls) }. Proof. begin. rewrite notInList_decompose. rewrite listMax_refines. setoid_rewrite increment_refines. (* ^-- Different tactic here to let us rewrite under a binder! *) rewrite bind_ret. finish. Defined. (* We can extract the program that we found as a standlone, executable Gallina * term. *) Definition impl := Eval simpl in proj1_sig implementation. Print impl. (* We'll temporarily expose the definition of [max], so we can compute neatly * here. *) Transparent max. Eval compute in impl (1 :: 7 :: 8 :: 2 :: 13 :: 6 :: nil).