diff --git a/._.depend b/._.depend
deleted file mode 100644
index 313e184..0000000
Binary files a/._.depend and /dev/null differ
diff --git a/._Auto.html b/._Auto.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Auto.html and /dev/null differ
diff --git a/._Auto.v b/._Auto.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Auto.v and /dev/null differ
diff --git a/._Basics.html b/._Basics.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Basics.html and /dev/null differ
diff --git a/._Basics.v b/._Basics.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Basics.v and /dev/null differ
diff --git a/._Equiv.html b/._Equiv.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Equiv.html and /dev/null differ
diff --git a/._Equiv.v b/._Equiv.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Equiv.v and /dev/null differ
diff --git a/._Extraction.html b/._Extraction.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Extraction.html and /dev/null differ
diff --git a/._Extraction.v b/._Extraction.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Extraction.v and /dev/null differ
diff --git a/._Hoare.html b/._Hoare.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Hoare.html and /dev/null differ
diff --git a/._Hoare.v b/._Hoare.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Hoare.v and /dev/null differ
diff --git a/._Hoare2.html b/._Hoare2.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Hoare2.html and /dev/null differ
diff --git a/._Hoare2.v b/._Hoare2.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Hoare2.v and /dev/null differ
diff --git a/._Imp.html b/._Imp.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Imp.html and /dev/null differ
diff --git a/._Imp.v b/._Imp.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Imp.v and /dev/null differ
diff --git a/._ImpCEvalFun.html b/._ImpCEvalFun.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._ImpCEvalFun.html and /dev/null differ
diff --git a/._ImpCEvalFun.v b/._ImpCEvalFun.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._ImpCEvalFun.v and /dev/null differ
diff --git a/._ImpParser.html b/._ImpParser.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._ImpParser.html and /dev/null differ
diff --git a/._ImpParser.v b/._ImpParser.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._ImpParser.v and /dev/null differ
diff --git a/._Induction.html b/._Induction.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Induction.html and /dev/null differ
diff --git a/._Induction.v b/._Induction.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Induction.v and /dev/null differ
diff --git a/._LICENSE b/._LICENSE
deleted file mode 100644
index 313e184..0000000
Binary files a/._LICENSE and /dev/null differ
diff --git a/._Lists.html b/._Lists.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Lists.html and /dev/null differ
diff --git a/._Lists.v b/._Lists.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Lists.v and /dev/null differ
diff --git a/._Logic.html b/._Logic.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Logic.html and /dev/null differ
diff --git a/._Logic.v b/._Logic.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Logic.v and /dev/null differ
diff --git a/._Makefile b/._Makefile
deleted file mode 100644
index 313e184..0000000
Binary files a/._Makefile and /dev/null differ
diff --git a/._MoreCoq.html b/._MoreCoq.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreCoq.html and /dev/null differ
diff --git a/._MoreCoq.v b/._MoreCoq.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreCoq.v and /dev/null differ
diff --git a/._MoreInd.html b/._MoreInd.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreInd.html and /dev/null differ
diff --git a/._MoreInd.v b/._MoreInd.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreInd.v and /dev/null differ
diff --git a/._MoreLogic.html b/._MoreLogic.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreLogic.html and /dev/null differ
diff --git a/._MoreLogic.v b/._MoreLogic.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreLogic.v and /dev/null differ
diff --git a/._MoreStlc.html b/._MoreStlc.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreStlc.html and /dev/null differ
diff --git a/._MoreStlc.v b/._MoreStlc.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._MoreStlc.v and /dev/null differ
diff --git a/._Poly.html b/._Poly.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Poly.html and /dev/null differ
diff --git a/._Poly.v b/._Poly.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Poly.v and /dev/null differ
diff --git a/._Preface.html b/._Preface.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Preface.html and /dev/null differ
diff --git a/._Preface.v b/._Preface.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Preface.v and /dev/null differ
diff --git a/._ProofObjects.html b/._ProofObjects.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._ProofObjects.html and /dev/null differ
diff --git a/._ProofObjects.v b/._ProofObjects.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._ProofObjects.v and /dev/null differ
diff --git a/._Prop.html b/._Prop.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Prop.html and /dev/null differ
diff --git a/._Prop.v b/._Prop.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Prop.v and /dev/null differ
diff --git a/._Review1.html b/._Review1.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Review1.html and /dev/null differ
diff --git a/._Review1.v b/._Review1.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Review1.v and /dev/null differ
diff --git a/._Review2.html b/._Review2.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Review2.html and /dev/null differ
diff --git a/._Review2.v b/._Review2.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Review2.v and /dev/null differ
diff --git a/._SfLib.html b/._SfLib.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._SfLib.html and /dev/null differ
diff --git a/._SfLib.v b/._SfLib.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._SfLib.v and /dev/null differ
diff --git a/._Smallstep.html b/._Smallstep.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Smallstep.html and /dev/null differ
diff --git a/._Smallstep.v b/._Smallstep.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Smallstep.v and /dev/null differ
diff --git a/._Stlc.html b/._Stlc.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Stlc.html and /dev/null differ
diff --git a/._Stlc.v b/._Stlc.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Stlc.v and /dev/null differ
diff --git a/._StlcProp.html b/._StlcProp.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._StlcProp.html and /dev/null differ
diff --git a/._StlcProp.v b/._StlcProp.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._StlcProp.v and /dev/null differ
diff --git a/._Sub.html b/._Sub.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Sub.html and /dev/null differ
diff --git a/._Sub.v b/._Sub.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Sub.v and /dev/null differ
diff --git a/._Symbols.html b/._Symbols.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Symbols.html and /dev/null differ
diff --git a/._Symbols.v b/._Symbols.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Symbols.v and /dev/null differ
diff --git a/._Types.html b/._Types.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._Types.html and /dev/null differ
diff --git a/._Types.v b/._Types.v
deleted file mode 100644
index 313e184..0000000
Binary files a/._Types.v and /dev/null differ
diff --git a/._coqdoc.css b/._coqdoc.css
deleted file mode 100644
index 313e184..0000000
Binary files a/._coqdoc.css and /dev/null differ
diff --git a/._coqindex.html b/._coqindex.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._coqindex.html and /dev/null differ
diff --git a/._deps.gif b/._deps.gif
deleted file mode 100644
index 313e184..0000000
Binary files a/._deps.gif and /dev/null differ
diff --git a/._deps.html b/._deps.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._deps.html and /dev/null differ
diff --git a/._deps.map b/._deps.map
deleted file mode 100644
index 313e184..0000000
Binary files a/._deps.map and /dev/null differ
diff --git a/._imp.ml b/._imp.ml
deleted file mode 100644
index 313e184..0000000
Binary files a/._imp.ml and /dev/null differ
diff --git a/._imp.mli b/._imp.mli
deleted file mode 100644
index 313e184..0000000
Binary files a/._imp.mli and /dev/null differ
diff --git a/._imp1.ml b/._imp1.ml
deleted file mode 100644
index 313e184..0000000
Binary files a/._imp1.ml and /dev/null differ
diff --git a/._imp1.mli b/._imp1.mli
deleted file mode 100644
index 313e184..0000000
Binary files a/._imp1.mli and /dev/null differ
diff --git a/._imp2.ml b/._imp2.ml
deleted file mode 100644
index 313e184..0000000
Binary files a/._imp2.ml and /dev/null differ
diff --git a/._imp2.mli b/._imp2.mli
deleted file mode 100644
index 313e184..0000000
Binary files a/._imp2.mli and /dev/null differ
diff --git a/._impdriver.ml b/._impdriver.ml
deleted file mode 100644
index 313e184..0000000
Binary files a/._impdriver.ml and /dev/null differ
diff --git a/._index-bg.jpg b/._index-bg.jpg
deleted file mode 100644
index 313e184..0000000
Binary files a/._index-bg.jpg and /dev/null differ
diff --git a/._index.html b/._index.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._index.html and /dev/null differ
diff --git a/._jquery-1.8.3.js b/._jquery-1.8.3.js
deleted file mode 100644
index 313e184..0000000
Binary files a/._jquery-1.8.3.js and /dev/null differ
diff --git a/._jquery.maphilight.min.js b/._jquery.maphilight.min.js
deleted file mode 100755
index 06d636e..0000000
Binary files a/._jquery.maphilight.min.js and /dev/null differ
diff --git a/._main.js b/._main.js
deleted file mode 100644
index 313e184..0000000
Binary files a/._main.js and /dev/null differ
diff --git a/._normdriver.ml b/._normdriver.ml
deleted file mode 100644
index 313e184..0000000
Binary files a/._normdriver.ml and /dev/null differ
diff --git a/._sf b/._sf
deleted file mode 100755
index bb87e14..0000000
Binary files a/._sf and /dev/null differ
diff --git a/._slides.js b/._slides.js
deleted file mode 100644
index 313e184..0000000
Binary files a/._slides.js and /dev/null differ
diff --git a/._toc.html b/._toc.html
deleted file mode 100644
index 313e184..0000000
Binary files a/._toc.html and /dev/null differ
diff --git a/.depend b/.depend
deleted file mode 100644
index c4c9853..0000000
--- a/.depend
+++ /dev/null
@@ -1,29 +0,0 @@
-Symbols.vo Symbols.glob Symbols.v.beautified: Symbols.v
-Preface.vo Preface.glob Preface.v.beautified: Preface.v
-Basics.vo Basics.glob Basics.v.beautified: Basics.v
-Induction.vo Induction.glob Induction.v.beautified: Induction.v
-Lists.vo Lists.glob Lists.v.beautified: Lists.v
-Poly.vo Poly.glob Poly.v.beautified: Poly.v
-MoreCoq.vo MoreCoq.glob MoreCoq.v.beautified: MoreCoq.v
-Logic.vo Logic.glob Logic.v.beautified: Logic.v
-Prop.vo Prop.glob Prop.v.beautified: Prop.v
-MoreLogic.vo MoreLogic.glob MoreLogic.v.beautified: MoreLogic.v
-ProofObjects.vo ProofObjects.glob ProofObjects.v.beautified: ProofObjects.v
-MoreInd.vo MoreInd.glob MoreInd.v.beautified: MoreInd.v
-Review1.vo Review1.glob Review1.v.beautified: Review1.v
-SfLib.vo SfLib.glob SfLib.v.beautified: SfLib.v
-Imp.vo Imp.glob Imp.v.beautified: Imp.v
-ImpParser.vo ImpParser.glob ImpParser.v.beautified: ImpParser.v
-ImpCEvalFun.vo ImpCEvalFun.glob ImpCEvalFun.v.beautified: ImpCEvalFun.v
-Extraction.vo Extraction.glob Extraction.v.beautified: Extraction.v
-Equiv.vo Equiv.glob Equiv.v.beautified: Equiv.v
-Hoare.vo Hoare.glob Hoare.v.beautified: Hoare.v
-Hoare2.vo Hoare2.glob Hoare2.v.beautified: Hoare2.v
-Smallstep.vo Smallstep.glob Smallstep.v.beautified: Smallstep.v
-Review2.vo Review2.glob Review2.v.beautified: Review2.v
-Auto.vo Auto.glob Auto.v.beautified: Auto.v
-Types.vo Types.glob Types.v.beautified: Types.v
-Stlc.vo Stlc.glob Stlc.v.beautified: Stlc.v
-StlcProp.vo StlcProp.glob StlcProp.v.beautified: StlcProp.v
-MoreStlc.vo MoreStlc.glob MoreStlc.v.beautified: MoreStlc.v
-Sub.vo Sub.glob Sub.v.beautified: Sub.v
diff --git a/Auto.html b/Auto.html
deleted file mode 100644
index b3215bc..0000000
--- a/Auto.html
+++ /dev/null
@@ -1,819 +0,0 @@
-
-
-
-
-
-Auto: More Automation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
AutoMore Automation
-
-
-
-
-
-
-
-
-
-
-RequireExportImp.
-
-
-
-
-
-Up to now, we've continued to use a quite restricted set of
-Coq's tactic facilities. In this chapter, we'll learn more about
-two very powerful features of Coq's tactic language:
-proof search via the auto and eauto tactics, and
-automated forward reasoning via the Ltac hypothesis matching
-machinery. Using these features together with Ltac's scripting facilities
-will enable us to make our proofs startlingly short! Used properly,
-they can also make proofs more maintainable and robust in the face
-of incremental changes to underlying definitions.
-
-
-
-There's a third major source of automation we haven't
-fully studied yet, namely built-in decision procedures for specific
-kinds of problems: omega is one example, but there are others.
-This topic will be defered for a while longer.
-
-
-
-
-
-
- Our motivating example will be this proof, repeated with
- just a few small changes from Imp. We will try to simplify
- this proof in several stages.
-
-
-
-
-LtacinvH := inversionH; subst; clearH.
-
-
-Theoremceval_deterministic: ∀cstst1st2,
- c / st⇓st1→
- c / st⇓st2→
- st1 = st2.
-Proof.
- introscstst1st2E1E2;
- generalizedependentst2;
- ceval_cases (inductionE1) Case;
- introsst2E2; invE2.
- Case "E_Skip". reflexivity.
- Case "E_Ass". reflexivity.
- Case "E_Seq".
- assert (st' = st'0) asEQ1.
- SCase "Proof of assertion". applyIHE1_1; assumption.
- substst'0.
- applyIHE1_2. assumption.
- Case "E_IfTrue".
- SCase "b evaluates to true".
- applyIHE1. assumption.
- SCase "b evaluates to false (contradiction)".
- rewriteHinH5. inversionH5.
- Case "E_IfFalse".
- SCase "b evaluates to true (contradiction)".
- rewriteHinH5. inversionH5.
- SCase "b evaluates to false".
- applyIHE1. assumption.
- Case "E_WhileEnd".
- SCase "b evaluates to false".
- reflexivity.
- SCase "b evaluates to true (contradiction)".
- rewriteHinH2. inversionH2.
- Case "E_WhileLoop".
- SCase "b evaluates to false (contradiction)".
- rewriteHinH4. inversionH4.
- SCase "b evaluates to true".
- assert (st' = st'0) asEQ1.
- SSCase "Proof of assertion". applyIHE1_1; assumption.
- substst'0.
- applyIHE1_2. assumption. Qed.
-
-
-
-
-
-
The auto and eauto tactics
-
-
-
- Thus far, we have (nearly) always written proof scripts that
- apply relevant hypothoses or lemmas by name. In particular, when
- a chain of hypothesis applications is needed, we have specified
- them explicitly. (The only exceptions introduced so far are using
- assumption to find a matching unqualified hypothesis
- or (e)constructor to find a matching constructor.)
-
-The auto tactic solves goals that are solvable by any combination of
-
-
-
-
-
intros,
-
-
-
apply (with a local hypothesis, by default).
-
-
-
-
-
-
- The eauto tactic works just like auto, except that it uses
- eapply instead of apply.
-
-
- Using auto is always "safe" in the sense that it will never fail
- and will never change the proof state: either it completely solves
- the current goal, or it does nothing.
-
-
-Search can take an arbitrarily long time, so there are limits to
- how far auto will search by default
-
-
-
-
-Exampleauto_example_3 : ∀(PQRSTU: Prop),
- (P→Q) → (Q→R) → (R→S) →
- (S→T) → (T→U) →P→U.
-Proof.
- auto. (* When it cannot solve the goal, does nothing! *)
- auto 6. (* Optional argument says how deep to search (default depth is 5) *)
-Qed.
-
-
-
-
-
-When searching for potential proofs of the current goal, auto
- and eauto consider the hypotheses in the current context
- together with a hint database of other lemmas and constructors.
- Some of the lemmas and constructors we've already seen — e.g.,
- eq_refl, conj, or_introl, and or_intror — are installed in this hint
- database by default.
-
-Of course, in any given development there will also be some of our
- own specific constructors and lemmas that are used very often in
- proofs. We can add these to the global hint database by writing
-
-
-
-
- HintResolveT.
-
-
-
- at the top level, where T is a top-level theorem or a
- constructor of an inductively defined proposition (i.e., anything
- whose type is an implication). As a shorthand, we can write
-
-
-
-
- HintConstructorsc.
-
-
-
- to tell Coq to do a HintResolve for all of the constructors
- from the inductive definition of c.
-
-
-
- It is also sometimes necessary to add
-
-
-
-
- HintUnfoldd.
-
-
-
- where d is a defined symbol, so that auto knows to expand
- uses of d and enable further possibilities for applying
- lemmas that it knows about.
-
-Now let's take a pass over ceval_deterministic using auto
- to simplify the proof script. We see that all simple sequences of hypothesis
- applications and all uses of reflexivity can be replaced by auto,
- which we add to the default tactic to be applied to each case.
-
-
-
-
-
-Theoremceval_deterministic': ∀cstst1st2,
- c / st⇓st1→
- c / st⇓st2→
- st1 = st2.
-Proof.
- introscstst1st2E1E2;
- generalizedependentst2;
- ceval_cases (inductionE1) Case;
- introsst2E2; invE2; auto.
- Case "E_Seq".
- assert (st' = st'0) asEQ1.
- SCase "Proof of assertion". auto.
- substst'0.
- auto.
- Case "E_IfTrue".
- SCase "b evaluates to false (contradiction)".
- rewriteHinH5. inversionH5.
- Case "E_IfFalse".
- SCase "b evaluates to true (contradiction)".
- rewriteHinH5. inversionH5.
- Case "E_WhileEnd".
- SCase "b evaluates to true (contradiction)".
- rewriteHinH2. inversionH2.
- Case "E_WhileLoop".
- SCase "b evaluates to false (contradiction)".
- rewriteHinH4. inversionH4.
- SCase "b evaluates to true".
- assert (st' = st'0) asEQ1.
- SSCase "Proof of assertion". auto.
- substst'0.
- auto. Qed.
-
-
-
-
-
-
Searching Hypotheses
-
-
-
- The proof has become simpler, but there is still an annoying amount
- of repetition. Let's start by tackling the contradiction cases. Each
- of them occurs in a situation where we have both
-
-
-
- H1:bevalstb=false
-
-
-
- and
-
-
-
- H2:bevalstb=true
-
-
-
- as hypotheses. The contradiction is evident, but demonstrating it
- is a little complicated: we have to locate the two hypotheses H1 and H2
- and do a rewrite following by an inversion. We'd like to automate
- this process.
-
-
-
- Note: In fact, Coq has a built-in tactic congruence that will do the
- job. But we'll ignore the existence of this tactic for now, in order
- to demonstrate how to build forward search tactics by hand.
-
-
-
-
-
-
- As a first step, we can abstract out the piece of script in question by
- writing a small amount of paramerized Ltac.
-
-
-
-
-LtacrwinvH1H2 := rewriteH1inH2; invH2.
-
-
-Theoremceval_deterministic'': ∀cstst1st2,
- c / st⇓st1→
- c / st⇓st2→
- st1 = st2.
-Proof.
- introscstst1st2E1E2;
- generalizedependentst2;
- ceval_cases (inductionE1) Case;
- introsst2E2; invE2; auto.
- Case "E_Seq".
- assert (st' = st'0) asEQ1.
- SCase "Proof of assertion". auto.
- substst'0.
- auto.
- Case "E_IfTrue".
- SCase "b evaluates to false (contradiction)".
- rwinvHH5.
- Case "E_IfFalse".
- SCase "b evaluates to true (contradiction)".
- rwinvHH5.
- Case "E_WhileEnd".
- SCase "b evaluates to true (contradiction)".
- rwinvHH2.
- Case "E_WhileLoop".
- SCase "b evaluates to false (contradiction)".
- rwinvHH4.
- SCase "b evaluates to true".
- assert (st' = st'0) asEQ1.
- SSCase "Proof of assertion". auto.
- substst'0.
- auto. Qed.
-
-
-
-
-
-But this is not much better. We really want Coq to discover
- the relevant hypotheses for us. We can do this by using the
- matchgoalwith...end facility of Ltac.
-
-In words, this matchgoal looks for two (distinct) hypotheses that have
- the form of equalities with the same arbitrary expression E on the
- left and conflicting boolean values on the right; if such hypotheses are
- found, it binds H1 and H2 to their names, and applies the tactic
- after the ⇒.
-
-
-
- Adding this tactic to our default string handles all the contradiction cases.
-
-Finally, let's see about the remaining cases. Each of them involves
- applying a conditional hypothesis to extract an equality. Currently
- we have phrased these as assertions, so that we have to predict what
- the resulting equality will be (although we can then use auto
- to prove it.) An alternative is to pick the relevant
- hypotheses to use, and then rewrite with them, as follows:
-
-
-
-
-
-Theoremceval_deterministic'''': ∀cstst1st2,
- c / st⇓st1→
- c / st⇓st2→
- st1 = st2.
-Proof.
- introscstst1st2E1E2;
- generalizedependentst2;
- ceval_cases (inductionE1) Case;
- introsst2E2; invE2; tryfind_rwinv; auto.
- Case "E_Seq".
- rewrite (IHE1_1st'0H1) in ×. auto.
- Case "E_WhileLoop".
- SCase "b evaluates to true".
- rewrite (IHE1_1st'0H3) in ×. auto. Qed.
-
-
-
-
-
-Now we can automate the task of finding the relevant hypotheses to
- rewrite with.
-
-But there are several pairs of hypotheses that have the right
- general form, and it seems tricky to pick out the ones we actually need.
- A key trick is to realize that we can try them all!
- Here's how this works:
-
-
-
-
-
rewrite will fail given a trivial equation of the form X=X.
-
-
-
each execution of matchgoal will keep trying to find a valid pair of
- hypotheses until the tactic on the RHS of the match succeeds;
- if there are no such pairs, it fails.
-
-
-
we can wrap the whole thing in a repeat which will keep
- doing useful rewrites until only trivial ones are left.
-
-
-The big pay-off in this approach is that our proof script
- should be robust in the face of modest changes to our language.
- For example, we can add a REPEAT command to the language.
- (This was an exercise in Hoare.v.)
-
-REPEAT behaves like WHILE, except that the loop guard is
- checked after each execution of the body, with the loop
- repeating as long as the guard stays false. Because of this,
- the body will always execute at least once.
-
-These examples just give a flavor of what "hyper-automation" can do...
-
-
-
- The details of using matchgoal are tricky, and debugging is
- not pleasant at all. But it is well worth adding at least simple
- uses to your proofs to avoid tedium and "future proof" your scripts.
-
-
-
-
-
\ No newline at end of file
diff --git a/Auto.v b/Auto.v
deleted file mode 100644
index 871270b..0000000
--- a/Auto.v
+++ /dev/null
@@ -1,560 +0,0 @@
-(** * Auto: More Automation *)
-
-Require Export Imp.
-
-(** Up to now, we've continued to use a quite restricted set of
-Coq's tactic facilities. In this chapter, we'll learn more about
-two very powerful features of Coq's tactic language:
-proof search via the [auto] and [eauto] tactics, and
-automated forward reasoning via the [Ltac] hypothesis matching
-machinery. Using these features together with Ltac's scripting facilities
-will enable us to make our proofs startlingly short! Used properly,
-they can also make proofs more maintainable and robust in the face
-of incremental changes to underlying definitions.
-
-There's a third major source of automation we haven't
-fully studied yet, namely built-in decision procedures for specific
-kinds of problems: [omega] is one example, but there are others.
-This topic will be defered for a while longer.
-
-*)
-
-(** Our motivating example will be this proof, repeated with
- just a few small changes from [Imp]. We will try to simplify
- this proof in several stages. *)
-
-Ltac inv H := inversion H; subst; clear H.
-
-Theorem ceval_deterministic: forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2.
- Case "E_Skip". reflexivity.
- Case "E_Ass". reflexivity.
- Case "E_Seq".
- assert (st' = st'0) as EQ1.
- SCase "Proof of assertion". apply IHE1_1; assumption.
- subst st'0.
- apply IHE1_2. assumption.
- Case "E_IfTrue".
- SCase "b evaluates to true".
- apply IHE1. assumption.
- SCase "b evaluates to false (contradiction)".
- rewrite H in H5. inversion H5.
- Case "E_IfFalse".
- SCase "b evaluates to true (contradiction)".
- rewrite H in H5. inversion H5.
- SCase "b evaluates to false".
- apply IHE1. assumption.
- Case "E_WhileEnd".
- SCase "b evaluates to false".
- reflexivity.
- SCase "b evaluates to true (contradiction)".
- rewrite H in H2. inversion H2.
- Case "E_WhileLoop".
- SCase "b evaluates to false (contradiction)".
- rewrite H in H4. inversion H4.
- SCase "b evaluates to true".
- assert (st' = st'0) as EQ1.
- SSCase "Proof of assertion". apply IHE1_1; assumption.
- subst st'0.
- apply IHE1_2. assumption. Qed.
-
-(** * The [auto] and [eauto] tactics *)
-
-(** Thus far, we have (nearly) always written proof scripts that
- apply relevant hypothoses or lemmas by name. In particular, when
- a chain of hypothesis applications is needed, we have specified
- them explicitly. (The only exceptions introduced so far are using
- [assumption] to find a matching unqualified hypothesis
- or [(e)constructor] to find a matching constructor.) *)
-
-
-Example auto_example_1 : forall (P Q R: Prop), (P -> Q) -> (Q -> R) -> P -> R.
-Proof.
- intros P Q R H1 H2 H3.
- apply H2. apply H1. assumption.
-Qed.
-
-(** The [auto] tactic frees us from this drudgery by _searching_
- for a sequence of applications that will prove the goal *)
-
-Example auto_example_1' : forall (P Q R: Prop), (P -> Q) -> (Q -> R) -> P -> R.
-Proof.
- intros P Q R H1 H2 H3.
- auto.
-Qed.
-
-(** The [auto] tactic solves goals that are solvable by any combination of
- - [intros],
- - [apply] (with a local hypothesis, by default).
-
- The [eauto] tactic works just like [auto], except that it uses
- [eapply] instead of [apply]. *)
-
-(** Using [auto] is always "safe" in the sense that it will never fail
- and will never change the proof state: either it completely solves
- the current goal, or it does nothing.
-*)
-
-(** A more complicated example: *)
-
-Example auto_example_2 : forall P Q R S T U : Prop,
- (P -> Q) ->
- (P -> R) ->
- (T -> R) ->
- (S -> T -> U) ->
- ((P->Q) -> (P->S)) ->
- T ->
- P ->
- U.
-Proof. auto. Qed.
-
-
-(** Search can take an arbitrarily long time, so there are limits to
- how far [auto] will search by default *)
-
-Example auto_example_3 : forall (P Q R S T U: Prop),
- (P -> Q) -> (Q -> R) -> (R -> S) ->
- (S -> T) -> (T -> U) -> P -> U.
-Proof.
- auto. (* When it cannot solve the goal, does nothing! *)
- auto 6. (* Optional argument says how deep to search (default depth is 5) *)
-Qed.
-
-
-(** When searching for potential proofs of the current goal, [auto]
- and [eauto] consider the hypotheses in the current context
- together with a _hint database_ of other lemmas and constructors.
- Some of the lemmas and constructors we've already seen -- e.g.,
- [eq_refl], [conj], [or_introl], and [or_intror] -- are installed in this hint
- database by default. *)
-
-Example auto_example_4 : forall P Q R : Prop,
- Q ->
- (Q -> R) ->
- P \/ (Q /\ R).
-Proof.
- auto. Qed.
-
-
-(** If we want to see which facts [auto] is using, we can use [info_auto] instead. *)
-
-Example auto_example_5: 2 = 2.
-Proof.
- info_auto. (* subsumes reflexivity because eq_refl is in hint database *)
-Qed.
-
-
-(** We can extend the hint database just for the purposes of one
- application of [auto] or [eauto] by writing [auto using ...]. *)
-
-Lemma le_antisym : forall n m: nat, (n <= m /\ m <= n) -> n = m.
-Proof. intros. omega. Qed.
-
-Example auto_example_6 : forall n m p : nat,
- (n<= p -> (n <= m /\ m <= n)) ->
- n <= p ->
- n = m.
-Proof.
- intros.
- auto. (* does nothing: auto doesn't destruct hypotheses! *)
- auto using le_antisym.
-Qed.
-
-
-(** Of course, in any given development there will also be some of our
- own specific constructors and lemmas that are used very often in
- proofs. We can add these to the global hint database by writing
- Hint Resolve T.
- at the top level, where [T] is a top-level theorem or a
- constructor of an inductively defined proposition (i.e., anything
- whose type is an implication). As a shorthand, we can write
- Hint Constructors c.
- to tell Coq to do a [Hint Resolve] for _all_ of the constructors
- from the inductive definition of [c].
-
- It is also sometimes necessary to add
- Hint Unfold d.
- where [d] is a defined symbol, so that [auto] knows to expand
- uses of [d] and enable further possibilities for applying
- lemmas that it knows about. *)
-
-Hint Resolve le_antisym.
-
-Example auto_example_6' : forall n m p : nat,
- (n<= p -> (n <= m /\ m <= n)) ->
- n <= p ->
- n = m.
-Proof.
- intros.
- auto. (* picks up hint from database *)
-Qed.
-
-Definition is_fortytwo x := x = 42.
-
-Example auto_example_7: forall x, (x <= 42 /\ 42 <= x) -> is_fortytwo x.
-Proof.
- auto. (* does nothing *)
-Abort.
-
-Hint Unfold is_fortytwo.
-
-Example auto_example_7' : forall x, (x <= 42 /\ 42 <= x) -> is_fortytwo x.
-Proof.
- info_auto.
-Qed.
-
-Hint Constructors ceval.
-
-Definition st12 := update (update empty_state X 1) Y 2.
-Definition st21 := update (update empty_state X 2) Y 1.
-
-Example auto_example_8 : exists s',
- (IFB (BLe (AId X) (AId Y))
- THEN (Z ::= AMinus (AId Y) (AId X))
- ELSE (Y ::= APlus (AId X) (AId Z))
- FI) / st21 || s'.
-Proof.
- eexists. info_auto.
-Qed.
-
-Example auto_example_8' : exists s',
- (IFB (BLe (AId X) (AId Y))
- THEN (Z ::= AMinus (AId Y) (AId X))
- ELSE (Y ::= APlus (AId X) (AId Z))
- FI) / st12 || s'.
-Proof.
- eexists. info_auto.
-Qed.
-
-
-(** Now let's take a pass over [ceval_deterministic] using [auto]
- to simplify the proof script. We see that all simple sequences of hypothesis
- applications and all uses of [reflexivity] can be replaced by [auto],
- which we add to the default tactic to be applied to each case.
-*)
-
-Theorem ceval_deterministic': forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; auto.
- Case "E_Seq".
- assert (st' = st'0) as EQ1.
- SCase "Proof of assertion". auto.
- subst st'0.
- auto.
- Case "E_IfTrue".
- SCase "b evaluates to false (contradiction)".
- rewrite H in H5. inversion H5.
- Case "E_IfFalse".
- SCase "b evaluates to true (contradiction)".
- rewrite H in H5. inversion H5.
- Case "E_WhileEnd".
- SCase "b evaluates to true (contradiction)".
- rewrite H in H2. inversion H2.
- Case "E_WhileLoop".
- SCase "b evaluates to false (contradiction)".
- rewrite H in H4. inversion H4.
- SCase "b evaluates to true".
- assert (st' = st'0) as EQ1.
- SSCase "Proof of assertion". auto.
- subst st'0.
- auto. Qed.
-
-(** * Searching Hypotheses *)
-
-(** The proof has become simpler, but there is still an annoying amount
- of repetition. Let's start by tackling the contradiction cases. Each
- of them occurs in a situation where we have both
-
- [H1: beval st b = false]
-
- and
-
- [H2: beval st b = true]
-
- as hypotheses. The contradiction is evident, but demonstrating it
- is a little complicated: we have to locate the two hypotheses [H1] and [H2]
- and do a [rewrite] following by an [inversion]. We'd like to automate
- this process.
-
- Note: In fact, Coq has a built-in tactic [congruence] that will do the
- job. But we'll ignore the existence of this tactic for now, in order
- to demonstrate how to build forward search tactics by hand.
-
-*)
-
-(** As a first step, we can abstract out the piece of script in question by
- writing a small amount of paramerized Ltac. *)
-
-Ltac rwinv H1 H2 := rewrite H1 in H2; inv H2.
-
-Theorem ceval_deterministic'': forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; auto.
- Case "E_Seq".
- assert (st' = st'0) as EQ1.
- SCase "Proof of assertion". auto.
- subst st'0.
- auto.
- Case "E_IfTrue".
- SCase "b evaluates to false (contradiction)".
- rwinv H H5.
- Case "E_IfFalse".
- SCase "b evaluates to true (contradiction)".
- rwinv H H5.
- Case "E_WhileEnd".
- SCase "b evaluates to true (contradiction)".
- rwinv H H2.
- Case "E_WhileLoop".
- SCase "b evaluates to false (contradiction)".
- rwinv H H4.
- SCase "b evaluates to true".
- assert (st' = st'0) as EQ1.
- SSCase "Proof of assertion". auto.
- subst st'0.
- auto. Qed.
-
-
-(** But this is not much better. We really want Coq to discover
- the relevant hypotheses for us. We can do this by using the
- [match goal with ... end] facility of Ltac. *)
-
-Ltac find_rwinv :=
- match goal with
- H1: ?E = true, H2: ?E = false |- _ => rwinv H1 H2
- end.
-
-(** In words, this [match goal] looks for two (distinct) hypotheses that have
- the form of equalities with the same arbitrary expression [E] on the
- left and conflicting boolean values on the right; if such hypotheses are
- found, it binds [H1] and [H2] to their names, and applies the tactic
- after the [=>].
-
- Adding this tactic to our default string handles all the contradiction cases. *)
-
-Theorem ceval_deterministic''': forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; try find_rwinv; auto.
- Case "E_Seq".
- assert (st' = st'0) as EQ1.
- SCase "Proof of assertion". auto.
- subst st'0.
- auto.
- Case "E_WhileLoop".
- SCase "b evaluates to true".
- assert (st' = st'0) as EQ1.
- SSCase "Proof of assertion". auto.
- subst st'0.
- auto. Qed.
-
-(** Finally, let's see about the remaining cases. Each of them involves
- applying a conditional hypothesis to extract an equality. Currently
- we have phrased these as assertions, so that we have to predict what
- the resulting equality will be (although we can then use [auto]
- to prove it.) An alternative is to pick the relevant
- hypotheses to use, and then rewrite with them, as follows:
-*)
-
-Theorem ceval_deterministic'''': forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; try find_rwinv; auto.
- Case "E_Seq".
- rewrite (IHE1_1 st'0 H1) in *. auto.
- Case "E_WhileLoop".
- SCase "b evaluates to true".
- rewrite (IHE1_1 st'0 H3) in *. auto. Qed.
-
-(** Now we can automate the task of finding the relevant hypotheses to
- rewrite with. *)
-
-Ltac find_eqn :=
- match goal with
- H1: forall x, ?P x -> ?L = ?R, H2: ?P ?X |- _ =>
- rewrite (H1 X H2) in *
- end.
-
-(** But there are several pairs of hypotheses that have the right
- general form, and it seems tricky to pick out the ones we actually need.
- A key trick is to realize that we can _try them all_!
- Here's how this works:
-
- - [rewrite] will fail given a trivial equation of the form [X = X].
- - each execution of [match goal] will keep trying to find a valid pair of
- hypotheses until the tactic on the RHS of the match succeeds;
- if there are no such pairs, it fails.
- - we can wrap the whole thing in a [repeat] which will keep
- doing useful rewrites until only trivial ones are left.
-*)
-
-
-Theorem ceval_deterministic''''': forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; try find_rwinv; repeat find_eqn; auto.
- Qed.
-
-(** The big pay-off in this approach is that our proof script
- should be robust in the face of modest changes to our language.
- For example, we can add a [REPEAT] command to the language.
- (This was an exercise in [Hoare.v].) *)
-
-Module Repeat.
-
-Inductive com : Type :=
- | CSkip : com
- | CAsgn : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com
- | CRepeat : com -> bexp -> com.
-
-(** [REPEAT] behaves like [WHILE], except that the loop guard is
- checked _after_ each execution of the body, with the loop
- repeating as long as the guard stays _false_. Because of this,
- the body will always execute at least once. *)
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";"
- | Case_aux c "IFB" | Case_aux c "WHILE"
- | Case_aux c "CRepeat" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "c1 ; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "X '::=' a" :=
- (CAsgn X a) (at level 60).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' e1 'THEN' e2 'ELSE' e3 'FI'" :=
- (CIf e1 e2 e3) (at level 80, right associativity).
-Notation "'REPEAT' e1 'UNTIL' b2 'END'" :=
- (CRepeat e1 b2) (at level 80, right associativity).
-
-Inductive ceval : state -> com -> state -> Prop :=
- | E_Skip : forall st,
- ceval st SKIP st
- | E_Ass : forall st a1 n X,
- aeval st a1 = n ->
- ceval st (X ::= a1) (update st X n)
- | E_Seq : forall c1 c2 st st' st'',
- ceval st c1 st' ->
- ceval st' c2 st'' ->
- ceval st (c1 ; c2) st''
- | E_IfTrue : forall st st' b1 c1 c2,
- beval st b1 = true ->
- ceval st c1 st' ->
- ceval st (IFB b1 THEN c1 ELSE c2 FI) st'
- | E_IfFalse : forall st st' b1 c1 c2,
- beval st b1 = false ->
- ceval st c2 st' ->
- ceval st (IFB b1 THEN c1 ELSE c2 FI) st'
- | E_WhileEnd : forall b1 st c1,
- beval st b1 = false ->
- ceval st (WHILE b1 DO c1 END) st
- | E_WhileLoop : forall st st' st'' b1 c1,
- beval st b1 = true ->
- ceval st c1 st' ->
- ceval st' (WHILE b1 DO c1 END) st'' ->
- ceval st (WHILE b1 DO c1 END) st''
- | E_RepeatEnd : forall st st' b1 c1,
- ceval st c1 st' ->
- beval st' b1 = true ->
- ceval st (CRepeat c1 b1) st'
- | E_RepeatLoop : forall st st' st'' b1 c1,
- ceval st c1 st' ->
- beval st' b1 = false ->
- ceval st' (CRepeat c1 b1) st'' ->
- ceval st (CRepeat c1 b1) st''
-.
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip" | Case_aux c "E_Ass"
- | Case_aux c "E_Seq"
- | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
- | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop"
- | Case_aux c "E_RepeatEnd" | Case_aux c "E_RepeatLoop"
-].
-
-Notation "c1 '/' st '||' st'" := (ceval st c1 st')
- (at level 40, st at level 39).
-
-
-Theorem ceval_deterministic: forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; try find_rwinv; repeat find_eqn; auto.
- Case "E_RepeatEnd".
- SCase "b evaluates to false (contradiction)".
- find_rwinv.
- (* oops: why didn't [find_rwinv] solve this for us already?
- answer: we did things in the wrong order. *)
- case "E_RepeatLoop".
- SCase "b evaluates to true (contradiction)".
- find_rwinv.
-Qed.
-
-Theorem ceval_deterministic': forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2;
- generalize dependent st2;
- ceval_cases (induction E1) Case;
- intros st2 E2; inv E2; repeat find_eqn; try find_rwinv; auto.
-Qed.
-
-End Repeat.
-
-(** These examples just give a flavor of what "hyper-automation" can do...
-
- The details of using [match goal] are tricky, and debugging is
- not pleasant at all. But it is well worth adding at least simple
- uses to your proofs to avoid tedium and "future proof" your scripts.
-
-*)
-
-(* $Date: 2013-07-30 12:24:33 -0400 (Tue, 30 Jul 2013) $ *)
diff --git a/Basics.html b/Basics.html
deleted file mode 100644
index 1f998a6..0000000
--- a/Basics.html
+++ /dev/null
@@ -1,1534 +0,0 @@
-
-
-
-
-
-Basics: Functional Programming in Coq
-
-
-
-
-
-
-
-
-
-
-
-
-
-
BasicsFunctional Programming in Coq
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Introduction
-
-
-
- The functional programming style brings programming closer to
- mathematics: If a procedure or method has no side effects, then
- pretty much all you need to understand about it is how it maps
- inputs to outputs — that is, you can think of its behavior as
- just computing a mathematical function. This is one reason for
- the word "functional" in "functional programming." This direct
- connection between programs and simple mathematical objects
- supports both sound informal reasoning and formal proofs of
- correctness.
-
-
-
- The other sense in which functional programming is "functional" is
- that it emphasizes the use of functions (or methods) as
- first-class values — i.e., values that can be passed as
- arguments to other functions, returned as results, stored in data
- structures, etc. The recognition that functions can be treated as
- data in this way enables a host of useful idioms, as we will see.
-
-
-
- Other common features of functional languages include algebraic
- data types and pattern matching, which make it easy to construct
- and manipulate rich data structures, and sophisticated
- polymorphic type systems that support abstraction and code
- reuse. Coq shares all of these features.
-
-
-
-
-
-
-
-
-
Enumerated Types
-
-
-
- One unusual aspect of Coq is that its set of built-in
- features is extremely small. For example, instead of providing
- the usual palette of atomic data types (booleans, integers,
- strings, etc.), Coq offers an extremely powerful mechanism for
- defining new data types from scratch — so powerful that all these
- familiar types arise as instances.
-
-
-
- Naturally, the Coq distribution comes with an extensive standard
- library providing definitions of booleans, numbers, and many
- common data structures like lists and hash tables. But there is
- nothing magic or primitive about these library definitions: they
- are ordinary user code.
-
-
-
- To see how this works, let's start with a very simple example.
-
-
-
-
-
-
-
-
Days of the Week
-
-
-
- The following declaration tells Coq that we are defining
- a new set of data values — a type.
-
-
-
-
-Inductiveday : Type :=
- | monday : day
- | tuesday : day
- | wednesday : day
- | thursday : day
- | friday : day
- | saturday : day
- | sunday : day.
-
-
-
-
-
-The type is called day, and its members are monday,
- tuesday, etc. The second through eighth lines of the definition
- can be read "monday is a day, tuesday is a day, etc."
-
-
-
- Having defined day, we can write functions that operate on
- days.
-
-One thing to note is that the argument and return types of
- this function are explicitly declared. Like most functional
- programming languages, Coq can often work out these types even if
- they are not given explicitly — i.e., it performs some type
- inference — but we'll always include them to make reading
- easier.
-
-
- Having defined a function, we should check that it works on
- some examples. There are actually three different ways to do this
- in Coq. First, we can use the command Evalcompute to evaluate a
- compound expression involving next_weekday.
-
-If you have a computer handy, now would be an excellent
- moment to fire up the Coq interpreter under your favorite IDE —
- either CoqIde or Proof General — and try this for yourself. Load
- this file (Basics.v) from the book's accompanying Coq sources,
- find the above example, submit it to Coq, and observe the
- result.
-
-
- The keyword compute tells Coq precisely how to
- evaluate the expression we give it. For the moment, compute is
- the only one we'll need; later on we'll see some alternatives that
- are sometimes useful.
-
-
- Second, we can record what we expect the result to be in
- the form of a Coq example:
-
-This declaration does two things: it makes an
- assertion (that the second weekday after saturday is tuesday),
- and it gives the assertion a name that can be used to refer to it
- later. Having made the assertion, we can also ask Coq to verify it,
- like this:
-
-
-
-
-Proof. simpl. reflexivity. Qed.
-
-
-
-
-
-The details are not important for now (we'll come back to
- them in a bit), but essentially this can be read as "The assertion
- we've just made can be proved by observing that both sides of the
- equality evaluate to the same thing, after some simplification."
-
-
- Third, we can ask Coq to "extract," from a Definition, a
- program in some other, more conventional, programming
- language (OCaml, Scheme, or Haskell) with a high-performance
- compiler. This facility is very interesting, since it gives us a
- way to construct fully certified programs in mainstream
- languages. Indeed, this is one of the main uses for which Coq was
- developed. We'll come back to this topic in later chapters.
- More information can also be found in the Coq'Art book by Bertot
- and Casteran, as well as the Coq reference manual.
-
-
-
-
-
-
-
-
Booleans
-
-
-
- In a similar way, we can define the type bool of booleans,
- with members true and false.
-
-Although we are rolling our own booleans here for the sake
- of building up everything from scratch, Coq does, of course,
- provide a default implementation of the booleans in its standard
- library, together with a multitude of useful functions and
- lemmas. (Take a look at Coq.Init.Datatypes in the Coq library
- documentation if you're interested.) Whenever possible, we'll
- name our own definitions and theorems so that they exactly
- coincide with the ones in the standard library.
-
-
- Functions over booleans can be defined in the same way as
- above:
-
-(Note that we've dropped the simpl in the proofs. It's not
- actually needed because reflexivity will automatically perform
- simplification.)
-
-
- A note on notation: We use square brackets to delimit
- fragments of Coq code in comments in .v files; this convention,
- also used by the coqdoc documentation tool, keeps them visually
- separate from the surrounding text. In the html version of the
- files, these pieces of text appear in a differentfont.
-
-
- The values Admitted and admit can be used to fill
- a hole in an incomplete definition or proof. We'll use them in the
- following exercises. In general, your job in the exercises is
- to replace admit or Admitted with real definitions or proofs.
-
-
-
Exercise: 1 star (nandb)
- Complete the definition of the following function, then make
- sure that the Example assertions below can each be verified by
- Coq.
-
-
- This function should return true if either or both of
- its inputs are false.
-
-
-
-
-Definitionnandb (b1:bool) (b2:bool) : bool :=
- (* FILL IN HERE *)admit.
-
-
-
-
-
-Remove "Admitted." and fill in each proof with
- "Proof.reflexivity.Qed."
-
-
-
-
-Exampletest_nandb1: (nandbtruefalse) = true.
-(* FILL IN HERE *)Admitted.
-Exampletest_nandb2: (nandbfalsefalse) = true.
-(* FILL IN HERE *)Admitted.
-Exampletest_nandb3: (nandbfalsetrue) = true.
-(* FILL IN HERE *)Admitted.
-Exampletest_nandb4: (nandbtruetrue) = false.
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star (andb3)
- Do the same for the andb3 function below. This function should
- return true when all of its inputs are true, and false
- otherwise.
-
-
-
-
-Definitionandb3 (b1:bool) (b2:bool) (b3:bool) : bool :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_andb31: (andb3truetruetrue) = true.
-(* FILL IN HERE *)Admitted.
-Exampletest_andb32: (andb3falsetruetrue) = false.
-(* FILL IN HERE *)Admitted.
-Exampletest_andb33: (andb3truefalsetrue) = false.
-(* FILL IN HERE *)Admitted.
-Exampletest_andb34: (andb3truetruefalse) = false.
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Function Types
-
-
-
- The Check command causes Coq to print the type of an
- expression. For example, the type of negbtrue is bool.
-
-Functions like negb itself are also data values, just like
- true and false. Their types are called function types, and
- they are written with arrows.
-
-The type of negb, written bool→bool and pronounced
- "bool arrow bool," can be read, "Given an input of type
- bool, this function produces an output of type bool."
- Similarly, the type of andb, written bool→bool→bool, can
- be read, "Given two inputs, both of type bool, this function
- produces an output of type bool."
-
-
-
-
-
-
-
-
Numbers
-
-
-
- Technical digression: Coq provides a fairly sophisticated
- module system, to aid in organizing large developments. In this
- course we won't need most of its features, but one is useful: If
- we enclose a collection of declarations between ModuleX and
- EndX markers, then, in the remainder of the file after the
- End, these definitions will be referred to by names like X.foo
- instead of just foo. Here, we use this feature to introduce the
- definition of the type nat in an inner module so that it does
- not shadow the one from the standard library.
-
-
-
-
-ModulePlayground1.
-
-
-
-
-
-The types we have defined so far are examples of "enumerated
- types": their definitions explicitly enumerate a finite set of
- elements. A more interesting way of defining a type is to give a
- collection of "inductive rules" describing its elements. For
- example, we can define the natural numbers as follows:
-
-
-
-
-Inductivenat : Type :=
- | O : nat
- | S : nat→nat.
-
-
-
-
-
-The clauses of this definition can be read:
-
-
-
-
-
O is a natural number (note that this is the letter "O," not
- the numeral "0").
-
-
-
S is a "constructor" that takes a natural number and yields
- another one — that is, if n is a natural number, then Sn
- is too.
-
-
-
-
-
-
- Let's look at this in a little more detail.
-
-
-
- Every inductively defined set (day, nat, bool, etc.) is
- actually a set of expressions. The definition of nat says how
- expressions in the set nat can be constructed:
-
-
-
-
-
the expression O belongs to the set nat;
-
-
-
if n is an expression belonging to the set nat, then Sn
- is also an expression belonging to the set nat; and
-
-
-
expressions formed in these two ways are the only ones belonging
- to the set nat.
-
-
-
- The same rules apply for our definitions of day and bool. The
- annotations we used for their constructors are analogous to the
- one for the O constructor, and indicate that each of those
- constructors doesn't take any arguments.
-
-
- These three conditions are the precise force of the
- Inductive declaration. They imply that the expression O, the
- expression SO, the expression S(SO), the expression
- S(S(SO)), and so on all belong to the set nat, while other
- expressions like true, andbtruefalse, and S(Sfalse) do
- not.
-
-
-
- We can write simple functions that pattern match on natural
- numbers just as we did above — for example, the predecessor
- function:
-
-
-
-
-Definitionpred (n : nat) : nat :=
- matchnwith
- | O ⇒ O
- | Sn' ⇒ n'
- end.
-
-
-
-
-
-The second branch can be read: "if n has the form Sn'
- for some n', then return n'."
-
-
-
-
-EndPlayground1.
-
-
-Definitionminustwo (n : nat) : nat :=
- matchnwith
- | O ⇒ O
- | SO ⇒ O
- | S (Sn') ⇒ n'
- end.
-
-
-
-
-
-Because natural numbers are such a pervasive form of data,
- Coq provides a tiny bit of built-in magic for parsing and printing
- them: ordinary arabic numerals can be used as an alternative to
- the "unary" notation defined by the constructors S and O. Coq
- prints numbers in arabic form by default:
-
-
-
-
-Check (S (S (S (SO)))).
-Evalcomputein (minustwo 4).
-
-
-
-
-
-The constructor S has the type nat→nat, just like the
- functions minustwo and pred:
-
-
-
-
-CheckS.
-Checkpred.
-Checkminustwo.
-
-
-
-
-
-These are all things that can be applied to a number to yield a
- number. However, there is a fundamental difference: functions
- like pred and minustwo come with computation rules — e.g.,
- the definition of pred says that pred2 can be simplified to
- 1 — while the definition of S has no such behavior attached.
- Although it is like a function in the sense that it can be applied
- to an argument, it does not do anything at all!
-
-
- For most function definitions over numbers, pure pattern
- matching is not enough: we also need recursion. For example, to
- check that a number n is even, we may need to recursively check
- whether n-2 is even. To write such functions, we use the
- keyword Fixpoint.
-
-
-
-
-Fixpointevenb (n:nat) : bool :=
- matchnwith
- | O ⇒ true
- | SO ⇒ false
- | S (Sn') ⇒ evenbn'
- end.
-
-
-
-
-
-We can define oddb by a similar Fixpoint declaration, but here
- is a simpler definition that will be a bit easier to work with:
-
-Adding three to two now gives us five, as we'd expect.
-
-
-
-
-Evalcomputein (plus (S (S (SO))) (S (SO))).
-
-
-
-
-
-The simplification that Coq performs to reach this conclusion can
- be visualized as follows:
-
-
-
-
-(* plus(S(S(SO)))(S(SO))
-==> S(plus(S(SO))(S(SO))) by the second clause of the match
-==> S(S(plus(SO)(S(SO)))) by the second clause of the match
-==> S(S(S(plusO(S(SO))))) by the second clause of the match
-==> S(S(S(S(SO)))) by the first clause of the match
-*)
-
-
-
-
-
-As a notational convenience, if two or more arguments have
- the same type, they can be written together. In the following
- definition, (nm:nat) means just the same as if we had written
- (n:nat)(m:nat).
-
-You can match two expressions at once by putting a comma
- between them:
-
-
-
-
-Fixpointminus (nm:nat) : nat :=
- matchn, mwith
- | O , _ ⇒ O
- | S_ , O ⇒ n
- | Sn', Sm' ⇒ minusn'm'
- end.
-
-
-
-
-
-The _ in the first line is a wildcard pattern. Writing _ in a
- pattern is the same as writing some variable that doesn't get used
- on the right-hand side. This avoids the need to invent a bogus
- variable name.
-
-
-
-Fixpointfactorial (n:nat) : nat :=
-(* FILL IN HERE *)admit.
-
-
-Exampletest_factorial1: (factorial 3) = 6.
-(* FILL IN HERE *)Admitted.
-Exampletest_factorial2: (factorial 5) = (mult 10 12).
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- We can make numerical expressions a little easier to read and
- write by introducing "notations" for addition, multiplication, and
- subtraction.
-
-(The level, associativity, and nat_scope annotations
- control how these notations are treated by Coq's parser. The
- details are not important, but interested readers can refer to the
- "More on Notation" subsection in the "Optional Material" section at
- the end of this chapter.)
-
-
- Note that these do not change the definitions we've already
- made: they are simply instructions to the Coq parser to accept x
- +y in place of plusxy and, conversely, to the Coq
- pretty-printer to display plusxy as x+y.
-
-
- When we say that Coq comes with nothing built-in, we really
- mean it: even equality testing for numbers is a user-defined
- operation! The beq_nat function tests natural numbers for equality,
- yielding a boolean. Note the use of nested matches (we could
- also have used a simultaneous match, as we did in minus.)
-
-
-
-
-Fixpointbeq_nat (nm : nat) : bool :=
- matchnwith
- | O ⇒ matchmwith
- | O ⇒ true
- | Sm' ⇒ false
- end
- | Sn' ⇒ matchmwith
- | O ⇒ false
- | Sm' ⇒ beq_natn'm'
- end
- end.
-
-
-
-
-
-Similarly, the ble_nat function tests natural numbers for
- less-or-equal, yielding a boolean.
-
- The blt_nat function tests natural numbers for less-than,
- yielding a boolean. Instead of making up a new Fixpoint for
- this one, define it in terms of a previously defined function.
-
-
-
- Note: If you have trouble with the simpl tactic, try using
- compute, which is like simpl on steroids. However, there is a
- simple, elegant solution for which simpl suffices.
-
-
-
-
-Definitionblt_nat (nm : nat) : bool :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_blt_nat1: (blt_nat 2 2) = false.
-(* FILL IN HERE *)Admitted.
-Exampletest_blt_nat2: (blt_nat 2 4) = true.
-(* FILL IN HERE *)Admitted.
-Exampletest_blt_nat3: (blt_nat 4 2) = false.
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Proof by Simplification
-
-
-
- Now that we've defined a few datatypes and functions, let's
- turn to the question of how to state and prove properties of their
- behavior. Actually, in a sense, we've already started doing this:
- each Example in the previous sections makes a precise claim
- about the behavior of some function on some particular inputs.
- The proofs of these claims were always the same: use reflexivity
- to check that both sides of the = simplify to identical values.
-
-
-
- (By the way, it will be useful later to know that
- reflexivity actually does somewhat more simplification than simpl
- does — for example, it tries "unfolding" defined terms, replacing them with
- their right-hand sides. The reason for this difference is that,
- when reflexivity succeeds, the whole goal is finished and we don't
- need to look at whatever expanded expressions reflexivity has
- found; by contrast, simpl is used in situations where we may
- have to read and understand the new goal, so we would not want it
- blindly expanding definitions.)
-
-
-
- The same sort of "proof by simplification" can be used to prove
- more interesting properties as well. For example, the fact that
- 0 is a "neutral element" for + on the left can be proved
- just by observing that 0+n reduces to n no matter what
- n is, a fact that can be read directly off the definition of plus.
-
-
-
-
-Theoremplus_O_n : ∀n : nat, 0 + n = n.
-Proof.
- introsn. reflexivity. Qed.
-
-
-
-
-
-(Note: You may notice that the above statement looks
- different in the original source file and the final html output. In Coq
- files, we write the ∀ universal quantifier using the
- "forall" reserved identifier. This gets printed as an
- upside-down "A", the familiar symbol used in logic.)
-
-
- The form of this theorem and proof are almost exactly the
- same as the examples above; there are just a few differences.
-
-
-
- First, we've used the keyword Theorem instead of
- Example. Indeed, the difference is purely a matter of
- style; the keywords Example and Theorem (and a few others,
- including Lemma, Fact, and Remark) mean exactly the same
- thing to Coq.
-
-
-
- Secondly, we've added the quantifier ∀n:nat, so that our
- theorem talks about all natural numbers n. In order to prove
- theorems of this form, we need to to be able to reason by
- assuming the existence of an arbitrary natural number n. This
- is achieved in the proof by introsn, which moves the quantifier
- from the goal to a "context" of current assumptions. In effect, we
- start the proof by saying "OK, suppose n is some arbitrary number."
-
-
-
- The keywords intros, simpl, and reflexivity are examples of
- tactics. A tactic is a command that is used between Proof and
- Qed to tell Coq how it should check the correctness of some
- claim we are making. We will see several more tactics in the rest
- of this lecture, and yet more in future lectures.
-
-
- Step through these proofs in Coq and notice how the goal and
- context change.
-
-The _l suffix in the names of these theorems is
- pronounced "on the left."
-
-
-
-
-
-
-
-
Proof by Rewriting
-
-
-
- Here is a slightly more interesting theorem:
-
-
-
-
-Theoremplus_id_example : ∀nm:nat,
- n = m→
- n + n = m + m.
-
-
-
-
-
-Instead of making a completely universal claim about all numbers
- n and m, this theorem talks about a more specialized property
- that only holds when n=m. The arrow symbol is pronounced
- "implies."
-
-
-
- As before, we need to be able to reason by assuming the existence
- of some numbers n and m. We also need to assume the hypothesis
- n=m. The intros tactic will serve to move all three of these
- from the goal into assumptions in the current context.
-
-
-
- Since n and m are arbitrary numbers, we can't just use
- simplification to prove this theorem. Instead, we prove it by
- observing that, if we are assuming n=m, then we can replace
- n with m in the goal statement and obtain an equality with the
- same expression on both sides. The tactic that tells Coq to
- perform this replacement is called rewrite.
-
-
-
-
-Proof.
- introsnm. (* move both quantifiers into the context *)
- introsH. (* move the hypothesis into the context *)
- rewrite→H. (* Rewrite the goal using the hypothesis *)
- reflexivity. Qed.
-
-
-
-
-
-The first line of the proof moves the universally quantified
- variables n and m into the context. The second moves the
- hypothesis n=m into the context and gives it the (arbitrary)
- name H. The third tells Coq to rewrite the current goal (n+n
- =m+m) by replacing the left side of the equality hypothesis
- H with the right side.
-
-
-
- (The arrow symbol in the rewrite has nothing to do with
- implication: it tells Coq to apply the rewrite from left to right.
- To rewrite from right to left, you can use rewrite←. Try
- making this change in the above proof and see what difference it
- makes in Coq's behavior.)
-
-
-
Exercise: 1 star (plus_id_exercise)
- Remove "Admitted." and fill in the proof.
-
-
-
-
-Theoremplus_id_exercise : ∀nmo : nat,
- n = m→m = o→n + m = m + o.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- As we've seen in earlier examples, the Admitted command
- tells Coq that we want to skip trying to prove this theorem and
- just accept it as a given. This can be useful for developing
- longer proofs, since we can state subsidiary facts that we believe
- will be useful for making some larger argument, use Admitted to
- accept them on faith for the moment, and continue thinking about
- the larger argument until we are sure it makes sense; then we can
- go back and fill in the proofs we skipped. Be careful, though:
- every time you say Admitted (or admit) you are leaving a door
- open for total nonsense to enter Coq's nice, rigorous, formally
- checked world!
-
-
- We can also use the rewrite tactic with a previously proved
- theorem instead of a hypothesis from the context.
-
-
-
-
-Theoremmult_0_plus : ∀nm : nat,
- (0 + n) × m = n × m.
-Proof.
- introsnm.
- rewrite→plus_O_n.
- reflexivity. Qed.
-
-
-
-
-
-
Exercise: 2 stars (mult_S_1)
-
-
-
-Theoremmult_S_1 : ∀nm : nat,
- m = Sn→
- m × (1 + n) = m × m.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Proof by Case Analysis
-
-
-
- Of course, not everything can be proved by simple
- calculation: In general, unknown, hypothetical values (arbitrary
- numbers, booleans, lists, etc.) can block the calculation.
- For example, if we try to prove the following fact using the
- simpl tactic as above, we get stuck.
-
-The reason for this is that the definitions of both
- beq_nat and + begin by performing a match on their first
- argument. But here, the first argument to + is the unknown
- number n and the argument to beq_nat is the compound
- expression n+1; neither can be simplified.
-
-
-
- What we need is to be able to consider the possible forms of n
- separately. If n is O, then we can calculate the final result
- of beq_nat(n+1)0 and check that it is, indeed, false.
- And if n=Sn' for some n', then, although we don't know
- exactly what number n+1 yields, we can calculate that, at
- least, it will begin with one S, and this is enough to calculate
- that, again, beq_nat(n+1)0 will yield false.
-
-
-
- The tactic that tells Coq to consider, separately, the cases where
- n=O and where n=Sn' is called destruct.
-
-The destruct generates two subgoals, which we must then
- prove, separately, in order to get Coq to accept the theorem as
- proved. (No special command is needed for moving from one subgoal
- to the other. When the first subgoal has been proved, it just
- disappears and we are left with the other "in focus.") In this
- proof, each of the subgoals is easily proved by a single use of
- reflexivity.
-
-
-
- The annotation "as[|n']" is called an intro pattern. It
- tells Coq what variable names to introduce in each subgoal. In
- general, what goes between the square brackets is a list of
- lists of names, separated by |. Here, the first component is
- empty, since the O constructor is nullary (it doesn't carry any
- data). The second component gives a single name, n', since S
- is a unary constructor.
-
-
-
- The destruct tactic can be used with any inductively defined
- datatype. For example, we use it here to prove that boolean
- negation is involutive — i.e., that negation is its own
- inverse.
-
-Note that the destruct here has no as clause because
- none of the subcases of the destruct need to bind any variables,
- so there is no need to specify any names. (We could also have
- written as[|], or as[].) In fact, we can omit the as
- clause from anydestruct and Coq will fill in variable names
- automatically. Although this is convenient, it is arguably bad
- style, since Coq often makes confusing choices of names when left
- to its own devices.
-
-
-
Exercise: 1 star (zero_nbeq_plus_1)
-
-
-
-Theoremzero_nbeq_plus_1 : ∀n : nat,
- beq_nat 0 (n + 1) = false.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
More Exercises
-
-
-
-
Exercise: 2 stars (boolean functions)
- Use the tactics you have learned so far to prove the following
- theorem about boolean functions.
-
-
-
-
-Theoremidentity_fn_applied_twice :
- ∀(f : bool→bool),
- (∀(x : bool), fx = x) →
- ∀(b : bool), f (fb) = b.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Now state and prove a theorem negation_fn_applied_twice similar
- to the previous one but where the second hypothesis says that the
- function f has the property that fx=negbx.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-
Exercise: 2 stars (andb_eq_orb)
- Prove the following theorem. (You may want to first prove a
- subsidiary lemma or two. Alternatively, remember that you do
- not have to introduce all hypotheses at the same time.)
-
-
-
-
-Theoremandb_eq_orb :
- ∀(bc : bool),
- (andbbc = orbbc) →
- b = c.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-
Exercise: 3 stars (binary)
- Consider a different, more efficient representation of natural
- numbers using a binary rather than unary system. That is, instead
- of saying that each natural number is either zero or the successor
- of a natural number, we can say that each binary number is either
-
-
-
-
-
zero,
-
-
-
twice a binary number, or
-
-
-
one more than twice a binary number.
-
-
-
-
-
-
- (a) First, write an inductive definition of the type bin
- corresponding to this description of binary numbers.
-
-
-
- (Hint: Recall that the definition of nat from class,
-
-
-
-
- Inductivenat : Type :=
- | O : nat
- | S : nat→nat.
-
-
-
- says nothing about what O and S "mean." It just says "O is
- in the set called nat, and if n is in the set then so is S
- n." The interpretation of O as zero and S as successor/plus
- one comes from the way that we usenat values, by writing
- functions to do things with them, proving things about them, and
- so on. Your definition of bin should be correspondingly simple;
- it is the functions you will write next that will give it
- mathematical meaning.)
-
-
-
- (b) Next, write an increment function for binary numbers, and a
- function to convert binary numbers to unary numbers.
-
-
-
- (c) Write some unit tests for your increment and binary-to-unary
- functions. Notice that incrementing a binary number and
- then converting it to unary should yield the same result as first
- converting it to unary and then incrementing.
-
-
-For each notation-symbol in Coq we can specify its precedence level
- and its associativity. The precedence level n can be specified by the
- keywords atleveln and it is helpful to disambiguate
- expressions containing different symbols. The associativity is helpful
- to disambiguate expressions containing more occurrences of the same
- symbol. For example, the parameters specified above for + and ×
- say that the expression 1+2×3×4 is a shorthand for the expression
- (1+((2×3)×4)). Coq uses precedence levels from 0 to 100, and
- left, right, or no associativity.
-
-
-
- Each notation-symbol in Coq is also active in a notation scope.
- Coq tries to guess what scope you mean, so when you write S(O×O)
- it guesses nat_scope, but when you write the cartesian
- product (tuple) type bool×bool it guesses type_scope.
- Occasionally you have to help it out with percent-notation by
- writing (x×y)%nat, and sometimes in Coq's feedback to you it
- will use %nat to indicate what scope a notation is in.
-
-
-
- Notation scopes also apply to numeral notation (3,4,5, etc.), so you
- may sometimes see 0%nat which means O, or 0%Z which means the
- Integer zero.
-
-
-
-
Fixpoints and Structural Recursion
-
-
-
-
-
-Fixpointplus' (n : nat) (m : nat) : nat :=
- matchnwith
- | O ⇒ m
- | Sn' ⇒ S (plus'n'm)
- end.
-
-
-
-
-
-When Coq checks this definition, it notes that plus' is
- "decreasing on 1st argument." What this means is that we are
- performing a structural recursion over the argument n — i.e.,
- that we make recursive calls only on strictly smaller values of
- n. This implies that all calls to plus' will eventually
- terminate. Coq demands that some argument of everyFixpoint
- definition is "decreasing".
-
-
-
- This requirement is a fundamental feature of Coq's design: In
- particular, it guarantees that every function that can be defined
- in Coq will terminate on all inputs. However, because Coq's
- "decreasing analysis" is not very sophisticated, it is sometimes
- necessary to write functions in slightly unnatural ways.
-
-
-
Exercise: 2 stars, optional (decreasing)
- To get a concrete sense of this, find a way to write a sensible
- Fixpoint definition (of a simple function on numbers, say) that
- does terminate on all inputs, but that Coq will not accept
- because of this restriction.
-
-
-
-
\ No newline at end of file
diff --git a/Basics.v b/Basics.v
deleted file mode 100644
index 2fc2a68..0000000
--- a/Basics.v
+++ /dev/null
@@ -1,967 +0,0 @@
-(** * Basics: Functional Programming in Coq *)
-
-(*
- [Admitted] is Coq's "escape hatch" that says accept this definition
- without proof. We use it to mark the 'holes' in the development
- that should be completed as part of your homework exercises. In
- practice, [Admitted] is useful when you're incrementally developing
- large proofs.
-
- As of Coq 8.4 [admit] is in the standard library, but we include
- it here for backwards compatibility.
-*)
-Definition admit {T: Type} : T. Admitted.
-
-(* ###################################################################### *)
-(** * Introduction *)
-
-(** The functional programming style brings programming closer to
- mathematics: If a procedure or method has no side effects, then
- pretty much all you need to understand about it is how it maps
- inputs to outputs -- that is, you can think of its behavior as
- just computing a mathematical function. This is one reason for
- the word "functional" in "functional programming." This direct
- connection between programs and simple mathematical objects
- supports both sound informal reasoning and formal proofs of
- correctness.
-
- The other sense in which functional programming is "functional" is
- that it emphasizes the use of functions (or methods) as
- _first-class_ values -- i.e., values that can be passed as
- arguments to other functions, returned as results, stored in data
- structures, etc. The recognition that functions can be treated as
- data in this way enables a host of useful idioms, as we will see.
-
- Other common features of functional languages include _algebraic
- data types_ and _pattern matching_, which make it easy to construct
- and manipulate rich data structures, and sophisticated
- _polymorphic type systems_ that support abstraction and code
- reuse. Coq shares all of these features.
-*)
-
-
-(* ###################################################################### *)
-(** * Enumerated Types *)
-
-(** One unusual aspect of Coq is that its set of built-in
- features is _extremely_ small. For example, instead of providing
- the usual palette of atomic data types (booleans, integers,
- strings, etc.), Coq offers an extremely powerful mechanism for
- defining new data types from scratch -- so powerful that all these
- familiar types arise as instances.
-
- Naturally, the Coq distribution comes with an extensive standard
- library providing definitions of booleans, numbers, and many
- common data structures like lists and hash tables. But there is
- nothing magic or primitive about these library definitions: they
- are ordinary user code.
-
- To see how this works, let's start with a very simple example. *)
-
-(* ###################################################################### *)
-(** ** Days of the Week *)
-
-(** The following declaration tells Coq that we are defining
- a new set of data values -- a _type_. *)
-
-Inductive day : Type :=
- | monday : day
- | tuesday : day
- | wednesday : day
- | thursday : day
- | friday : day
- | saturday : day
- | sunday : day.
-
-(** The type is called [day], and its members are [monday],
- [tuesday], etc. The second through eighth lines of the definition
- can be read "[monday] is a [day], [tuesday] is a [day], etc."
-
- Having defined [day], we can write functions that operate on
- days. *)
-
-Definition next_weekday (d:day) : day :=
- match d with
- | monday => tuesday
- | tuesday => wednesday
- | wednesday => thursday
- | thursday => friday
- | friday => monday
- | saturday => monday
- | sunday => monday
- end.
-
-(** One thing to note is that the argument and return types of
- this function are explicitly declared. Like most functional
- programming languages, Coq can often work out these types even if
- they are not given explicitly -- i.e., it performs some _type
- inference_ -- but we'll always include them to make reading
- easier. *)
-
-(** Having defined a function, we should check that it works on
- some examples. There are actually three different ways to do this
- in Coq. First, we can use the command [Eval compute] to evaluate a
- compound expression involving [next_weekday]. *)
-
-Eval compute in (next_weekday friday).
- (* ==> monday : day *)
-Eval compute in (next_weekday (next_weekday saturday)).
- (* ==> tuesday : day *)
-
-(** If you have a computer handy, now would be an excellent
- moment to fire up the Coq interpreter under your favorite IDE --
- either CoqIde or Proof General -- and try this for yourself. Load
- this file ([Basics.v]) from the book's accompanying Coq sources,
- find the above example, submit it to Coq, and observe the
- result. *)
-
-(** The keyword [compute] tells Coq precisely how to
- evaluate the expression we give it. For the moment, [compute] is
- the only one we'll need; later on we'll see some alternatives that
- are sometimes useful. *)
-
-(** Second, we can record what we _expect_ the result to be in
- the form of a Coq example: *)
-
-Example test_next_weekday:
- (next_weekday (next_weekday saturday)) = tuesday.
-
-(** This declaration does two things: it makes an
- assertion (that the second weekday after [saturday] is [tuesday]),
- and it gives the assertion a name that can be used to refer to it
- later. *)
-(** Having made the assertion, we can also ask Coq to verify it,
- like this: *)
-
-Proof. simpl. reflexivity. Qed.
-
-
-(** The details are not important for now (we'll come back to
- them in a bit), but essentially this can be read as "The assertion
- we've just made can be proved by observing that both sides of the
- equality evaluate to the same thing, after some simplification." *)
-
-(** Third, we can ask Coq to "extract," from a [Definition], a
- program in some other, more conventional, programming
- language (OCaml, Scheme, or Haskell) with a high-performance
- compiler. This facility is very interesting, since it gives us a
- way to construct _fully certified_ programs in mainstream
- languages. Indeed, this is one of the main uses for which Coq was
- developed. We'll come back to this topic in later chapters.
- More information can also be found in the Coq'Art book by Bertot
- and Casteran, as well as the Coq reference manual. *)
-
-
-(* ###################################################################### *)
-(** ** Booleans *)
-
-(** In a similar way, we can define the type [bool] of booleans,
- with members [true] and [false]. *)
-
-Inductive bool : Type :=
- | true : bool
- | false : bool.
-
-(** Although we are rolling our own booleans here for the sake
- of building up everything from scratch, Coq does, of course,
- provide a default implementation of the booleans in its standard
- library, together with a multitude of useful functions and
- lemmas. (Take a look at [Coq.Init.Datatypes] in the Coq library
- documentation if you're interested.) Whenever possible, we'll
- name our own definitions and theorems so that they exactly
- coincide with the ones in the standard library. *)
-
-(** Functions over booleans can be defined in the same way as
- above: *)
-
-Definition negb (b:bool) : bool :=
- match b with
- | true => false
- | false => true
- end.
-
-Definition andb (b1:bool) (b2:bool) : bool :=
- match b1 with
- | true => b2
- | false => false
- end.
-
-Definition orb (b1:bool) (b2:bool) : bool :=
- match b1 with
- | true => true
- | false => b2
- end.
-
-(** The last two illustrate the syntax for multi-argument
- function definitions. *)
-
-(** The following four "unit tests" constitute a complete
- specification -- a truth table -- for the [orb] function: *)
-
-Example test_orb1: (orb true false) = true.
-Proof. reflexivity. Qed.
-Example test_orb2: (orb false false) = false.
-Proof. reflexivity. Qed.
-Example test_orb3: (orb false true) = true.
-Proof. reflexivity. Qed.
-Example test_orb4: (orb true true) = true.
-Proof. reflexivity. Qed.
-
-(** (Note that we've dropped the [simpl] in the proofs. It's not
- actually needed because [reflexivity] will automatically perform
- simplification.) *)
-
-(** _A note on notation_: We use square brackets to delimit
- fragments of Coq code in comments in .v files; this convention,
- also used by the [coqdoc] documentation tool, keeps them visually
- separate from the surrounding text. In the html version of the
- files, these pieces of text appear in a [different font]. *)
-
-(** The values [Admitted] and [admit] can be used to fill
- a hole in an incomplete definition or proof. We'll use them in the
- following exercises. In general, your job in the exercises is
- to replace [admit] or [Admitted] with real definitions or proofs. *)
-
-(** **** Exercise: 1 star (nandb) *)
-(** Complete the definition of the following function, then make
- sure that the [Example] assertions below can each be verified by
- Coq. *)
-
-(** This function should return [true] if either or both of
- its inputs are [false]. *)
-
-Definition nandb (b1:bool) (b2:bool) : bool :=
- (* FILL IN HERE *) admit.
-
-(** Remove "[Admitted.]" and fill in each proof with
- "[Proof. reflexivity. Qed.]" *)
-
-Example test_nandb1: (nandb true false) = true.
-(* FILL IN HERE *) Admitted.
-Example test_nandb2: (nandb false false) = true.
-(* FILL IN HERE *) Admitted.
-Example test_nandb3: (nandb false true) = true.
-(* FILL IN HERE *) Admitted.
-Example test_nandb4: (nandb true true) = false.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star (andb3) *)
-(** Do the same for the [andb3] function below. This function should
- return [true] when all of its inputs are [true], and [false]
- otherwise. *)
-
-Definition andb3 (b1:bool) (b2:bool) (b3:bool) : bool :=
- (* FILL IN HERE *) admit.
-
-Example test_andb31: (andb3 true true true) = true.
-(* FILL IN HERE *) Admitted.
-Example test_andb32: (andb3 false true true) = false.
-(* FILL IN HERE *) Admitted.
-Example test_andb33: (andb3 true false true) = false.
-(* FILL IN HERE *) Admitted.
-Example test_andb34: (andb3 true true false) = false.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** ** Function Types *)
-
-(** The [Check] command causes Coq to print the type of an
- expression. For example, the type of [negb true] is [bool]. *)
-
-Check true.
-(* ===> true : bool *)
-Check (negb true).
-(* ===> negb true : bool *)
-
-(** Functions like [negb] itself are also data values, just like
- [true] and [false]. Their types are called _function types_, and
- they are written with arrows. *)
-
-Check negb.
-(* ===> negb : bool -> bool *)
-
-(** The type of [negb], written [bool -> bool] and pronounced
- "[bool] arrow [bool]," can be read, "Given an input of type
- [bool], this function produces an output of type [bool]."
- Similarly, the type of [andb], written [bool -> bool -> bool], can
- be read, "Given two inputs, both of type [bool], this function
- produces an output of type [bool]." *)
-
-(* ###################################################################### *)
-(** ** Numbers *)
-
-(** _Technical digression_: Coq provides a fairly sophisticated
- _module system_, to aid in organizing large developments. In this
- course we won't need most of its features, but one is useful: If
- we enclose a collection of declarations between [Module X] and
- [End X] markers, then, in the remainder of the file after the
- [End], these definitions will be referred to by names like [X.foo]
- instead of just [foo]. Here, we use this feature to introduce the
- definition of the type [nat] in an inner module so that it does
- not shadow the one from the standard library. *)
-
-Module Playground1.
-
-(** The types we have defined so far are examples of "enumerated
- types": their definitions explicitly enumerate a finite set of
- elements. A more interesting way of defining a type is to give a
- collection of "inductive rules" describing its elements. For
- example, we can define the natural numbers as follows: *)
-
-Inductive nat : Type :=
- | O : nat
- | S : nat -> nat.
-
-(** The clauses of this definition can be read:
- - [O] is a natural number (note that this is the letter "[O]," not
- the numeral "[0]").
- - [S] is a "constructor" that takes a natural number and yields
- another one -- that is, if [n] is a natural number, then [S n]
- is too.
-
- Let's look at this in a little more detail.
-
- Every inductively defined set ([day], [nat], [bool], etc.) is
- actually a set of _expressions_. The definition of [nat] says how
- expressions in the set [nat] can be constructed:
-
- - the expression [O] belongs to the set [nat];
- - if [n] is an expression belonging to the set [nat], then [S n]
- is also an expression belonging to the set [nat]; and
- - expressions formed in these two ways are the only ones belonging
- to the set [nat].
-
- The same rules apply for our definitions of [day] and [bool]. The
- annotations we used for their constructors are analogous to the
- one for the [O] constructor, and indicate that each of those
- constructors doesn't take any arguments. *)
-
-(** These three conditions are the precise force of the
- [Inductive] declaration. They imply that the expression [O], the
- expression [S O], the expression [S (S O)], the expression
- [S (S (S O))], and so on all belong to the set [nat], while other
- expressions like [true], [andb true false], and [S (S false)] do
- not.
-
- We can write simple functions that pattern match on natural
- numbers just as we did above -- for example, the predecessor
- function: *)
-
-Definition pred (n : nat) : nat :=
- match n with
- | O => O
- | S n' => n'
- end.
-
-(** The second branch can be read: "if [n] has the form [S n']
- for some [n'], then return [n']." *)
-
-End Playground1.
-
-Definition minustwo (n : nat) : nat :=
- match n with
- | O => O
- | S O => O
- | S (S n') => n'
- end.
-
-(** Because natural numbers are such a pervasive form of data,
- Coq provides a tiny bit of built-in magic for parsing and printing
- them: ordinary arabic numerals can be used as an alternative to
- the "unary" notation defined by the constructors [S] and [O]. Coq
- prints numbers in arabic form by default: *)
-
-Check (S (S (S (S O)))).
-Eval compute in (minustwo 4).
-
-(** The constructor [S] has the type [nat -> nat], just like the
- functions [minustwo] and [pred]: *)
-
-Check S.
-Check pred.
-Check minustwo.
-
-(** These are all things that can be applied to a number to yield a
- number. However, there is a fundamental difference: functions
- like [pred] and [minustwo] come with _computation rules_ -- e.g.,
- the definition of [pred] says that [pred 2] can be simplified to
- [1] -- while the definition of [S] has no such behavior attached.
- Although it is like a function in the sense that it can be applied
- to an argument, it does not _do_ anything at all! *)
-
-(** For most function definitions over numbers, pure pattern
- matching is not enough: we also need recursion. For example, to
- check that a number [n] is even, we may need to recursively check
- whether [n-2] is even. To write such functions, we use the
- keyword [Fixpoint]. *)
-
-Fixpoint evenb (n:nat) : bool :=
- match n with
- | O => true
- | S O => false
- | S (S n') => evenb n'
- end.
-
-(** We can define [oddb] by a similar [Fixpoint] declaration, but here
- is a simpler definition that will be a bit easier to work with: *)
-
-Definition oddb (n:nat) : bool := negb (evenb n).
-
-Example test_oddb1: (oddb (S O)) = true.
-Proof. reflexivity. Qed.
-Example test_oddb2: (oddb (S (S (S (S O))))) = false.
-Proof. reflexivity. Qed.
-
-(** Naturally, we can also define multi-argument functions by
- recursion. (Once again, we use a module to avoid polluting the
- namespace.) *)
-
-Module Playground2.
-
-Fixpoint plus (n : nat) (m : nat) : nat :=
- match n with
- | O => m
- | S n' => S (plus n' m)
- end.
-
-(** Adding three to two now gives us five, as we'd expect. *)
-
-Eval compute in (plus (S (S (S O))) (S (S O))).
-
-(** The simplification that Coq performs to reach this conclusion can
- be visualized as follows: *)
-
-(* [plus (S (S (S O))) (S (S O))]
-==> [S (plus (S (S O)) (S (S O)))] by the second clause of the [match]
-==> [S (S (plus (S O) (S (S O))))] by the second clause of the [match]
-==> [S (S (S (plus O (S (S O)))))] by the second clause of the [match]
-==> [S (S (S (S (S O))))] by the first clause of the [match]
-*)
-
-(** As a notational convenience, if two or more arguments have
- the same type, they can be written together. In the following
- definition, [(n m : nat)] means just the same as if we had written
- [(n : nat) (m : nat)]. *)
-
-Fixpoint mult (n m : nat) : nat :=
- match n with
- | O => O
- | S n' => plus m (mult n' m)
- end.
-
-Example test_mult1: (mult 3 3) = 9.
-Proof. reflexivity. Qed.
-
-(** You can match two expressions at once by putting a comma
- between them: *)
-
-Fixpoint minus (n m:nat) : nat :=
- match n, m with
- | O , _ => O
- | S _ , O => n
- | S n', S m' => minus n' m'
- end.
-
-(** The _ in the first line is a _wildcard pattern_. Writing _ in a
- pattern is the same as writing some variable that doesn't get used
- on the right-hand side. This avoids the need to invent a bogus
- variable name. *)
-
-End Playground2.
-
-Fixpoint exp (base power : nat) : nat :=
- match power with
- | O => S O
- | S p => mult base (exp base p)
- end.
-
-(** **** Exercise: 1 star (factorial) *)
-(** Recall the standard factorial function:
-<<
- factorial(0) = 1
- factorial(n) = n * factorial(n-1) (if n>0)
->>
- Translate this into Coq. *)
-
-Fixpoint factorial (n:nat) : nat :=
-(* FILL IN HERE *) admit.
-
-Example test_factorial1: (factorial 3) = 6.
-(* FILL IN HERE *) Admitted.
-Example test_factorial2: (factorial 5) = (mult 10 12).
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** We can make numerical expressions a little easier to read and
- write by introducing "notations" for addition, multiplication, and
- subtraction. *)
-
-Notation "x + y" := (plus x y)
- (at level 50, left associativity)
- : nat_scope.
-Notation "x - y" := (minus x y)
- (at level 50, left associativity)
- : nat_scope.
-Notation "x * y" := (mult x y)
- (at level 40, left associativity)
- : nat_scope.
-
-Check ((0 + 1) + 1).
-
-(** (The [level], [associativity], and [nat_scope] annotations
- control how these notations are treated by Coq's parser. The
- details are not important, but interested readers can refer to the
- "More on Notation" subsection in the "Optional Material" section at
- the end of this chapter.) *)
-
-(** Note that these do not change the definitions we've already
- made: they are simply instructions to the Coq parser to accept [x
- + y] in place of [plus x y] and, conversely, to the Coq
- pretty-printer to display [plus x y] as [x + y]. *)
-
-(** When we say that Coq comes with nothing built-in, we really
- mean it: even equality testing for numbers is a user-defined
- operation! *)
-(** The [beq_nat] function tests [nat]ural numbers for [eq]uality,
- yielding a [b]oolean. Note the use of nested [match]es (we could
- also have used a simultaneous match, as we did in [minus].) *)
-
-Fixpoint beq_nat (n m : nat) : bool :=
- match n with
- | O => match m with
- | O => true
- | S m' => false
- end
- | S n' => match m with
- | O => false
- | S m' => beq_nat n' m'
- end
- end.
-
-(** Similarly, the [ble_nat] function tests [nat]ural numbers for
- [l]ess-or-[e]qual, yielding a [b]oolean. *)
-
-Fixpoint ble_nat (n m : nat) : bool :=
- match n with
- | O => true
- | S n' =>
- match m with
- | O => false
- | S m' => ble_nat n' m'
- end
- end.
-
-Example test_ble_nat1: (ble_nat 2 2) = true.
-Proof. reflexivity. Qed.
-Example test_ble_nat2: (ble_nat 2 4) = true.
-Proof. reflexivity. Qed.
-Example test_ble_nat3: (ble_nat 4 2) = false.
-Proof. reflexivity. Qed.
-
-(** **** Exercise: 2 stars (blt_nat) *)
-(** The [blt_nat] function tests [nat]ural numbers for [l]ess-[t]han,
- yielding a [b]oolean. Instead of making up a new [Fixpoint] for
- this one, define it in terms of a previously defined function.
-
- Note: If you have trouble with the [simpl] tactic, try using
- [compute], which is like [simpl] on steroids. However, there is a
- simple, elegant solution for which [simpl] suffices. *)
-
-Definition blt_nat (n m : nat) : bool :=
- (* FILL IN HERE *) admit.
-
-Example test_blt_nat1: (blt_nat 2 2) = false.
-(* FILL IN HERE *) Admitted.
-Example test_blt_nat2: (blt_nat 2 4) = true.
-(* FILL IN HERE *) Admitted.
-Example test_blt_nat3: (blt_nat 4 2) = false.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** * Proof by Simplification *)
-
-(** Now that we've defined a few datatypes and functions, let's
- turn to the question of how to state and prove properties of their
- behavior. Actually, in a sense, we've already started doing this:
- each [Example] in the previous sections makes a precise claim
- about the behavior of some function on some particular inputs.
- The proofs of these claims were always the same: use [reflexivity]
- to check that both sides of the [=] simplify to identical values.
-
- (By the way, it will be useful later to know that
- [reflexivity] actually does somewhat more simplification than [simpl]
- does -- for example, it tries "unfolding" defined terms, replacing them with
- their right-hand sides. The reason for this difference is that,
- when reflexivity succeeds, the whole goal is finished and we don't
- need to look at whatever expanded expressions [reflexivity] has
- found; by contrast, [simpl] is used in situations where we may
- have to read and understand the new goal, so we would not want it
- blindly expanding definitions.)
-
- The same sort of "proof by simplification" can be used to prove
- more interesting properties as well. For example, the fact that
- [0] is a "neutral element" for [+] on the left can be proved
- just by observing that [0 + n] reduces to [n] no matter what
- [n] is, a fact that can be read directly off the definition of [plus].*)
-
-Theorem plus_O_n : forall n : nat, 0 + n = n.
-Proof.
- intros n. reflexivity. Qed.
-
-
-(** (_Note_: You may notice that the above statement looks
- different in the original source file and the final html output. In Coq
- files, we write the [forall] universal quantifier using the
- "_forall_" reserved identifier. This gets printed as an
- upside-down "A", the familiar symbol used in logic.) *)
-
-(** The form of this theorem and proof are almost exactly the
- same as the examples above; there are just a few differences.
-
- First, we've used the keyword [Theorem] instead of
- [Example]. Indeed, the difference is purely a matter of
- style; the keywords [Example] and [Theorem] (and a few others,
- including [Lemma], [Fact], and [Remark]) mean exactly the same
- thing to Coq.
-
- Secondly, we've added the quantifier [forall n:nat], so that our
- theorem talks about _all_ natural numbers [n]. In order to prove
- theorems of this form, we need to to be able to reason by
- _assuming_ the existence of an arbitrary natural number [n]. This
- is achieved in the proof by [intros n], which moves the quantifier
- from the goal to a "context" of current assumptions. In effect, we
- start the proof by saying "OK, suppose [n] is some arbitrary number."
-
- The keywords [intros], [simpl], and [reflexivity] are examples of
- _tactics_. A tactic is a command that is used between [Proof] and
- [Qed] to tell Coq how it should check the correctness of some
- claim we are making. We will see several more tactics in the rest
- of this lecture, and yet more in future lectures. *)
-
-
-(** Step through these proofs in Coq and notice how the goal and
- context change. *)
-
-Theorem plus_1_l : forall n:nat, 1 + n = S n.
-Proof.
- intros n. reflexivity. Qed.
-
-Theorem mult_0_l : forall n:nat, 0 * n = 0.
-Proof.
- intros n. reflexivity. Qed.
-
-(** The [_l] suffix in the names of these theorems is
- pronounced "on the left." *)
-
-
-(* ###################################################################### *)
-(** * Proof by Rewriting *)
-
-(** Here is a slightly more interesting theorem: *)
-
-Theorem plus_id_example : forall n m:nat,
- n = m ->
- n + n = m + m.
-
-(** Instead of making a completely universal claim about all numbers
- [n] and [m], this theorem talks about a more specialized property
- that only holds when [n = m]. The arrow symbol is pronounced
- "implies."
-
- As before, we need to be able to reason by assuming the existence
- of some numbers [n] and [m]. We also need to assume the hypothesis
- [n = m]. The [intros] tactic will serve to move all three of these
- from the goal into assumptions in the current context.
-
- Since [n] and [m] are arbitrary numbers, we can't just use
- simplification to prove this theorem. Instead, we prove it by
- observing that, if we are assuming [n = m], then we can replace
- [n] with [m] in the goal statement and obtain an equality with the
- same expression on both sides. The tactic that tells Coq to
- perform this replacement is called [rewrite]. *)
-
-Proof.
- intros n m. (* move both quantifiers into the context *)
- intros H. (* move the hypothesis into the context *)
- rewrite -> H. (* Rewrite the goal using the hypothesis *)
- reflexivity. Qed.
-
-(** The first line of the proof moves the universally quantified
- variables [n] and [m] into the context. The second moves the
- hypothesis [n = m] into the context and gives it the (arbitrary)
- name [H]. The third tells Coq to rewrite the current goal ([n + n
- = m + m]) by replacing the left side of the equality hypothesis
- [H] with the right side.
-
- (The arrow symbol in the [rewrite] has nothing to do with
- implication: it tells Coq to apply the rewrite from left to right.
- To rewrite from right to left, you can use [rewrite <-]. Try
- making this change in the above proof and see what difference it
- makes in Coq's behavior.) *)
-
-(** **** Exercise: 1 star (plus_id_exercise) *)
-(** Remove "[Admitted.]" and fill in the proof. *)
-
-Theorem plus_id_exercise : forall n m o : nat,
- n = m -> m = o -> n + m = m + o.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** As we've seen in earlier examples, the [Admitted] command
- tells Coq that we want to skip trying to prove this theorem and
- just accept it as a given. This can be useful for developing
- longer proofs, since we can state subsidiary facts that we believe
- will be useful for making some larger argument, use [Admitted] to
- accept them on faith for the moment, and continue thinking about
- the larger argument until we are sure it makes sense; then we can
- go back and fill in the proofs we skipped. Be careful, though:
- every time you say [Admitted] (or [admit]) you are leaving a door
- open for total nonsense to enter Coq's nice, rigorous, formally
- checked world! *)
-
-(** We can also use the [rewrite] tactic with a previously proved
- theorem instead of a hypothesis from the context. *)
-
-Theorem mult_0_plus : forall n m : nat,
- (0 + n) * m = n * m.
-Proof.
- intros n m.
- rewrite -> plus_O_n.
- reflexivity. Qed.
-
-(** **** Exercise: 2 stars (mult_S_1) *)
-Theorem mult_S_1 : forall n m : nat,
- m = S n ->
- m * (1 + n) = m * m.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-(* ###################################################################### *)
-(** * Proof by Case Analysis *)
-
-(** Of course, not everything can be proved by simple
- calculation: In general, unknown, hypothetical values (arbitrary
- numbers, booleans, lists, etc.) can block the calculation.
- For example, if we try to prove the following fact using the
- [simpl] tactic as above, we get stuck. *)
-
-Theorem plus_1_neq_0_firsttry : forall n : nat,
- beq_nat (n + 1) 0 = false.
-Proof.
- intros n.
- simpl. (* does nothing! *)
-Abort.
-
-(** The reason for this is that the definitions of both
- [beq_nat] and [+] begin by performing a [match] on their first
- argument. But here, the first argument to [+] is the unknown
- number [n] and the argument to [beq_nat] is the compound
- expression [n + 1]; neither can be simplified.
-
- What we need is to be able to consider the possible forms of [n]
- separately. If [n] is [O], then we can calculate the final result
- of [beq_nat (n + 1) 0] and check that it is, indeed, [false].
- And if [n = S n'] for some [n'], then, although we don't know
- exactly what number [n + 1] yields, we can calculate that, at
- least, it will begin with one [S], and this is enough to calculate
- that, again, [beq_nat (n + 1) 0] will yield [false].
-
- The tactic that tells Coq to consider, separately, the cases where
- [n = O] and where [n = S n'] is called [destruct]. *)
-
-Theorem plus_1_neq_0 : forall n : nat,
- beq_nat (n + 1) 0 = false.
-Proof.
- intros n. destruct n as [| n'].
- reflexivity.
- reflexivity. Qed.
-
-(** The [destruct] generates _two_ subgoals, which we must then
- prove, separately, in order to get Coq to accept the theorem as
- proved. (No special command is needed for moving from one subgoal
- to the other. When the first subgoal has been proved, it just
- disappears and we are left with the other "in focus.") In this
- proof, each of the subgoals is easily proved by a single use of
- [reflexivity].
-
- The annotation "[as [| n']]" is called an _intro pattern_. It
- tells Coq what variable names to introduce in each subgoal. In
- general, what goes between the square brackets is a _list_ of
- lists of names, separated by [|]. Here, the first component is
- empty, since the [O] constructor is nullary (it doesn't carry any
- data). The second component gives a single name, [n'], since [S]
- is a unary constructor.
-
- The [destruct] tactic can be used with any inductively defined
- datatype. For example, we use it here to prove that boolean
- negation is involutive -- i.e., that negation is its own
- inverse. *)
-
-Theorem negb_involutive : forall b : bool,
- negb (negb b) = b.
-Proof.
- intros b. destruct b.
- reflexivity.
- reflexivity. Qed.
-
-(** Note that the [destruct] here has no [as] clause because
- none of the subcases of the [destruct] need to bind any variables,
- so there is no need to specify any names. (We could also have
- written [as [|]], or [as []].) In fact, we can omit the [as]
- clause from _any_ [destruct] and Coq will fill in variable names
- automatically. Although this is convenient, it is arguably bad
- style, since Coq often makes confusing choices of names when left
- to its own devices. *)
-
-(** **** Exercise: 1 star (zero_nbeq_plus_1) *)
-Theorem zero_nbeq_plus_1 : forall n : nat,
- beq_nat 0 (n + 1) = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** * More Exercises *)
-
-(** **** Exercise: 2 stars (boolean functions) *)
-(** Use the tactics you have learned so far to prove the following
- theorem about boolean functions. *)
-
-Theorem identity_fn_applied_twice :
- forall (f : bool -> bool),
- (forall (x : bool), f x = x) ->
- forall (b : bool), f (f b) = b.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Now state and prove a theorem [negation_fn_applied_twice] similar
- to the previous one but where the second hypothesis says that the
- function [f] has the property that [f x = negb x].*)
-
-(* FILL IN HERE *)
-
-(** **** Exercise: 2 stars (andb_eq_orb) *)
-(** Prove the following theorem. (You may want to first prove a
- subsidiary lemma or two. Alternatively, remember that you do
- not have to introduce all hypotheses at the same time.) *)
-
-Theorem andb_eq_orb :
- forall (b c : bool),
- (andb b c = orb b c) ->
- b = c.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 3 stars (binary) *)
-(** Consider a different, more efficient representation of natural
- numbers using a binary rather than unary system. That is, instead
- of saying that each natural number is either zero or the successor
- of a natural number, we can say that each binary number is either
-
- - zero,
- - twice a binary number, or
- - one more than twice a binary number.
-
- (a) First, write an inductive definition of the type [bin]
- corresponding to this description of binary numbers.
-
- (Hint: Recall that the definition of [nat] from class,
- Inductive nat : Type :=
- | O : nat
- | S : nat -> nat.
- says nothing about what [O] and [S] "mean." It just says "[O] is
- in the set called [nat], and if [n] is in the set then so is [S
- n]." The interpretation of [O] as zero and [S] as successor/plus
- one comes from the way that we _use_ [nat] values, by writing
- functions to do things with them, proving things about them, and
- so on. Your definition of [bin] should be correspondingly simple;
- it is the functions you will write next that will give it
- mathematical meaning.)
-
- (b) Next, write an increment function for binary numbers, and a
- function to convert binary numbers to unary numbers.
-
- (c) Write some unit tests for your increment and binary-to-unary
- functions. Notice that incrementing a binary number and
- then converting it to unary should yield the same result as first
- converting it to unary and then incrementing.
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ###################################################################### *)
-(** * Optional Material *)
-
-(** ** More on Notation *)
-
-Notation "x + y" := (plus x y)
- (at level 50, left associativity)
- : nat_scope.
-Notation "x * y" := (mult x y)
- (at level 40, left associativity)
- : nat_scope.
-
-(** For each notation-symbol in Coq we can specify its _precedence level_
- and its _associativity_. The precedence level n can be specified by the
- keywords [at level n] and it is helpful to disambiguate
- expressions containing different symbols. The associativity is helpful
- to disambiguate expressions containing more occurrences of the same
- symbol. For example, the parameters specified above for [+] and [*]
- say that the expression [1+2*3*4] is a shorthand for the expression
- [(1+((2*3)*4))]. Coq uses precedence levels from 0 to 100, and
- _left_, _right_, or _no_ associativity.
-
- Each notation-symbol in Coq is also active in a _notation scope_.
- Coq tries to guess what scope you mean, so when you write [S(O*O)]
- it guesses [nat_scope], but when you write the cartesian
- product (tuple) type [bool*bool] it guesses [type_scope].
- Occasionally you have to help it out with percent-notation by
- writing [(x*y)%nat], and sometimes in Coq's feedback to you it
- will use [%nat] to indicate what scope a notation is in.
-
- Notation scopes also apply to numeral notation (3,4,5, etc.), so you
- may sometimes see [0%nat] which means [O], or [0%Z] which means the
- Integer zero.
-*)
-
-(** ** [Fixpoint]s and Structural Recursion *)
-
-Fixpoint plus' (n : nat) (m : nat) : nat :=
- match n with
- | O => m
- | S n' => S (plus' n' m)
- end.
-
-(** When Coq checks this definition, it notes that [plus'] is
- "decreasing on 1st argument." What this means is that we are
- performing a _structural recursion_ over the argument [n] -- i.e.,
- that we make recursive calls only on strictly smaller values of
- [n]. This implies that all calls to [plus'] will eventually
- terminate. Coq demands that some argument of _every_ [Fixpoint]
- definition is "decreasing".
-
- This requirement is a fundamental feature of Coq's design: In
- particular, it guarantees that every function that can be defined
- in Coq will terminate on all inputs. However, because Coq's
- "decreasing analysis" is not very sophisticated, it is sometimes
- necessary to write functions in slightly unnatural ways. *)
-
-(** **** Exercise: 2 stars, optional (decreasing) *)
-(** To get a concrete sense of this, find a way to write a sensible
- [Fixpoint] definition (of a simple function on numbers, say) that
- _does_ terminate on all inputs, but that Coq will _not_ accept
- because of this restriction. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* $Date: 2013-12-03 07:45:41 -0500 (Tue, 03 Dec 2013) $ *)
-
diff --git a/Equiv.html b/Equiv.html
deleted file mode 100644
index 49be689..0000000
--- a/Equiv.html
+++ /dev/null
@@ -1,2911 +0,0 @@
-
-
-
-
-
-Equiv: Program Equivalence
-
-
-
-
-
-
-
-
-
-
-
-
-
-
EquivProgram Equivalence
-
-
-
-
-
-
-
-
-
-
-RequireExportImp.
-
-
-
-
-
-
Some general advice for working on exercises:
-
-
-
-
-
-
Most of the Coq proofs we ask you to do are similar to proofs
- that we've provided. Before starting to work on the homework
- problems, take the time to work through our proofs (both
- informally, on paper, and in Coq) and make sure you understand
- them in detail. This will save you a lot of time.
-
-
-
-
-
-
The Coq proofs we're doing now are sufficiently complicated that
- it is more or less impossible to complete them simply by random
- experimentation or "following your nose." You need to start
- with an idea about why the property is true and how the proof is
- going to go. The best way to do this is to write out at least a
- sketch of an informal proof on paper — one that intuitively
- convinces you of the truth of the theorem — before starting to
- work on the formal one. Alternately, grab a friend and try to
- convince them that the theorem is true; then try to formalize
- your explanation.
-
-
-
-
-
-
Use automation to save work! Some of the proofs in this
- chapter's exercises are pretty long if you try to write out all
- the cases explicitly.
-
-
-
-
-
-
-
-
-
-
-
Behavioral Equivalence
-
-
-
- In the last chapter, we investigated the correctness of a very
- simple program transformation: the optimize_0plus function. The
- programming language we were considering was the first version of
- the language of arithmetic expressions — with no variables — so
- in that setting it was very easy to define what it means for a
- program transformation to be correct: it should always yield a
- program that evaluates to the same number as the original.
-
-
-
- To go further and talk about the correctness of program
- transformations in the full Imp language, we need to consider the
- role of variables and state.
-
-
-
-
-
-
-
-
Definitions
-
-
-
- For aexps and bexps with variables, the definition we want is
- clear. We say
- that two aexps or bexps are behaviorally equivalent if they
- evaluate to the same result in every state.
-
-For commands, the situation is a little more subtle. We can't
- simply say "two commands are behaviorally equivalent if they
- evaluate to the same ending state whenever they are started in the
- same initial state," because some commands (in some starting
- states) don't terminate in any final state at all! What we need
- instead is this: two commands are behaviorally equivalent if, for
- any given starting state, they either both diverge or both
- terminate in the same final state. A compact way to express this
- is "if the first one terminates in a particular state then so does
- the second, and vice versa."
-
-
- Given the following programs, group together those that are
- equivalent in Imp. For example, if you think programs (a)
- through (h) are all equivalent to each other, but not to (i), your
- answer should look like this: {a,b,c,d,e,f,g,h} {i}.
-
-
-
-(a)
-
-
-
-
- WHILEX > 0 DO
- X ::= X + 1
- END
-
-
-
-
-
-
-(b)
-
-
-
-
- IFBX = 0 THEN
- X ::= X + 1;;
- Y ::= 1
- ELSE
- Y ::= 0
- FI;;
- X ::= X - Y;;
- Y ::= 0
-
-
-
-
-
-
-(c)
-
-
-
-
- SKIP
-
-
-
-
-
-
-(d)
-
-
-
-
- WHILEX ≠ 0 DO
- X ::= X × Y + 1
- END
-
-
-
-
-
-
-(e)
-
-
-
-
- Y ::= 0
-
-
-
-
-
-
-(f)
-
-
-
-
- Y ::= X + 1;;
- WHILEX ≠ YDO
- Y ::= X + 1
- END
-
-
-
-
-
-
-(g)
-
-
-
-
- WHILETRUEDO
- SKIP
- END
-
-
-
-
-
-
-(h)
-
-
-
-
- WHILEX ≠ XDO
- X ::= X + 1
- END
-
-
-
-
-
-
-(i)
-
-
-
-
- WHILEX ≠ YDO
- X ::= Y + 1
- END
-
-
-
-
-
-
-(* FILL IN HERE *)
-☐
-
-
-
-
-
-
-
-
Examples
-
-
-
- Here are some simple examples of equivalences of arithmetic
- and boolean expressions.
-
-Of course, few programmers would be tempted to write a conditional
- whose guard is literally BTrue. A more interesting case is when
- the guard is equivalent to true:
-
-
-
- Theorem: If b is equivalent to BTrue, then IFBbTHENc1
- ELSEc2FI is equivalent to c1.
-
-
-
-
- Proof:
-
-
-
-
-
(→) We must show, for all st and st', that if IFBb
- THENc1ELSEc2FI/st⇓st' then c1/st⇓st'.
-
-
-
- Proceed by cases on the rules that could possibly have been
- used to show IFBbTHENc1ELSEc2FI/st⇓st', namely
- E_IfTrue and E_IfFalse.
-
-
-
-
-
Suppose the final rule rule in the derivation of IFBbTHEN
- c1ELSEc2FI/st⇓st' was E_IfTrue. We then have, by
- the premises of E_IfTrue, that c1/st⇓st'. This is
- exactly what we set out to prove.
-
-
-
-
-
-
On the other hand, suppose the final rule in the derivation
- of IFBbTHENc1ELSEc2FI/st⇓st' was E_IfFalse.
- We then know that bevalstb=false and c2/st⇓st'.
-
-
-
- Recall that b is equivalent to BTrue, i.e. forall st,
- bevalstb=bevalstBTrue. In particular, this means
- that bevalstb=true, since bevalstBTrue=true. But
- this is a contradiction, since E_IfFalse requires that
- bevalstb=false. Thus, the final rule could not have
- been E_IfFalse.
-
-
-
-
-
-
-
-
-
(←) We must show, for all st and st', that if c1/st
- ⇓st' then IFBbTHENc1ELSEc2FI/st⇓st'.
-
-
-
- Since b is equivalent to BTrue, we know that bevalstb =
- bevalstBTrue = true. Together with the assumption that
- c1/st⇓st', we can apply E_IfTrue to derive IFBbTHEN
- c1ELSEc2FI/st⇓st'. ☐
-
-
-TheoremIFB_false: ∀bc1c2,
- bequivbBFalse→
- cequiv
- (IFBbTHENc1ELSEc2FI)
- c2.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (swap_if_branches)
- Show that we can swap the branches of an IF by negating its
- condition
-
-
-
-
-Theoremswap_if_branches: ∀be1e2,
- cequiv
- (IFBbTHENe1ELSEe2FI)
- (IFBBNotbTHENe2ELSEe1FI).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
- For WHILE loops, we can give a similar pair of theorems. A loop
- whose guard is equivalent to BFalse is equivalent to SKIP,
- while a loop whose guard is equivalent to BTrue is equivalent to
- WHILEBTrueDOSKIPEND (or any other non-terminating program).
- The first of these facts is easy.
-
- To prove the second fact, we need an auxiliary lemma stating that
- WHILE loops whose guards are equivalent to BTrue never
- terminate:
-
-
-
- Lemma: If b is equivalent to BTrue, then it cannot be the
- case that (WHILEbDOcEND)/st⇓st'.
-
-
-
- Proof: Suppose that (WHILEbDOcEND)/st⇓st'. We show,
- by induction on a derivation of (WHILEbDOcEND)/st⇓st',
- that this assumption leads to a contradiction.
-
-
-
-
-
Suppose (WHILEbDOcEND)/st⇓st' is proved using rule
- E_WhileEnd. Then by assumption bevalstb=false. But
- this contradicts the assumption that b is equivalent to
- BTrue.
-
-
-
-
-
-
Suppose (WHILEbDOcEND)/st⇓st' is proved using rule
- E_WhileLoop. Then we are given the induction hypothesis
- that (WHILEbDOcEND)/st⇓st' is contradictory, which
- is exactly what we are trying to prove!
-
-
-
-
-
-
Since these are the only rules that could have been used to
- prove (WHILEbDOcEND)/st⇓st', the other cases of
- the induction are immediately contradictory. ☐
-
-
-
-
-
-
-
-LemmaWHILE_true_nonterm : ∀bcstst',
- bequivbBTrue→
- ~( (WHILEbDOcEND) / st⇓st' ).
-Proof.
- (* WORKED IN CLASS *)
- introsbcstst'Hb.
- introsH.
- remember (WHILEbDOcEND) ascweqn:Heqcw.
- ceval_cases (inductionH) Case;
- (* Most rules don't apply, and we can rule them out
- by inversion *)
- inversionHeqcw; subst; clearHeqcw.
- (* The two interesting cases are the ones for WHILE loops: *)
- Case "E_WhileEnd". (* contradictory -- b is always true! *)
- unfoldbequivinHb.
- (* rewrite is able to instantiate the quantifier in st *)
- rewriteHbinH. inversionH.
- Case "E_WhileLoop". (* immediate from the IH *)
- applyIHceval2. reflexivity. Qed.
-
-
-
-Theoremseq_assoc : ∀c1c2c3,
- cequiv ((c1;;c2);;c3) (c1;;(c2;;c3)).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
The Functional Equivalence Axiom
-
-
-
- Finally, let's look at simple equivalences involving assignments.
- For example, we might expect to be able to show that X::=AIdX
- is equivalent to SKIP. However, when we try to show it, we get
- stuck in an interesting way.
-
-Here we're stuck. The goal looks reasonable, but in fact it is not
- provable! If we look back at the set of lemmas we proved about
- update in the last chapter, we can see that lemma update_same
- almost does the job, but not quite: it says that the original and
- updated states agree at all values, but this is not the same thing
- as saying that they are = in Coq's sense!
-
-
- What is going on here? Recall that our states are just
- functions from identifiers to values. For Coq, functions are only
- equal when their definitions are syntactically the same, modulo
- simplification. (This is the only way we can legally apply the
- refl_equal constructor of the inductively defined proposition
- eq!) In practice, for functions built up by repeated uses of the
- update operation, this means that two functions can be proven
- equal only if they were constructed using the sameupdate
- operations, applied in the same order. In the theorem above, the
- sequence of updates on the first parameter cequiv is one longer
- than for the second parameter, so it is no wonder that the
- equality doesn't hold.
-
-
-
- This problem is actually quite general. If we try to prove other
- simple facts, such as
-
-
-
-
- cequiv (X ::= X + 1;;
- X ::= X + 1)
- (X ::= X + 2)
-
-
-
- or
-
-
-
-
- cequiv (X ::= 1;; Y ::= 2)
- (y ::= 2;; X ::= 1)
-
-
-
-
- we'll get stuck in the same way: we'll have two functions that
- behave the same way on all inputs, but cannot be proven to be eq
- to each other.
-
-
-
- The reasoning principle we would like to use in these situations
- is called functional extensionality:
-
-
-
∀x, f x = g x
-
-
-
-
-
-
-
-
f = g
-
-
-
Although this principle is not derivable in Coq's built-in logic,
- it is safe to add it as an additional axiom.
-
-It can be shown that adding this axiom doesn't introduce any
- inconsistencies into Coq. (In this way, it is similar to adding
- one of the classical logic axioms, such as excluded_middle.)
-
-
- With the benefit of this axiom we can prove our theorem.
-
-Theoremassign_aequiv : ∀Xe,
- aequiv (AIdX) e→
- cequivSKIP (X ::= e).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Properties of Behavioral Equivalence
-
-
-
- We now turn to developing some of the properties of the program
- equivalences we have defined.
-
-
-
-
-
-
-
-
Behavioral Equivalence is an Equivalence
-
-
-
- First, we verify that the equivalences on aexps, bexps, and
- coms really are equivalences — i.e., that they are reflexive,
- symmetric, and transitive. The proofs are all easy.
-
-
- Less obviously, behavioral equivalence is also a congruence.
- That is, the equivalence of two subprograms implies the
- equivalence of the larger programs in which they are embedded:
-
-
-
aequiv a1 a1'
-
-
-
-
-
-
-
-
cequiv (i ::= a1) (i ::= a1')
-
-
-
-
-
cequiv c1 c1'
-
-
-
-
cequiv c2 c2'
-
-
-
-
-
-
-
-
cequiv (c1;;c2) (c1';;c2')
-
-
-
...and so on.
-
-
-
- (Note that we are using the inference rule notation here not as
- part of a definition, but simply to write down some valid
- implications in a readable format. We prove these implications
- below.)
-
-
- We will see a concrete example of why these congruence
- properties are important in the following section (in the proof of
- fold_constants_com_sound), but the main idea is that they allow
- us to replace a small part of a large program with an equivalent
- small part and know that the whole large programs are equivalent
- without doing an explicit proof about the non-varying parts —
- i.e., the "proof burden" of a small change to a large program is
- proportional to the size of the change, not the program.
-
-The congruence property for loops is a little more interesting,
- since it requires induction.
-
-
-
- Theorem: Equivalence is a congruence for WHILE — that is, if
- b1 is equivalent to b1' and c1 is equivalent to c1', then
- WHILEb1DOc1END is equivalent to WHILEb1'DOc1'END.
-
-
-
- Proof: Suppose b1 is equivalent to b1' and c1 is
- equivalent to c1'. We must show, for every st and st', that
- WHILEb1DOc1END/st⇓st' iff WHILEb1'DOc1'END/st
- ⇓st'. We consider the two directions separately.
-
-
-
-
-
(→) We show that WHILEb1DOc1END/st⇓st' implies
- WHILEb1'DOc1'END/st⇓st', by induction on a
- derivation of WHILEb1DOc1END/st⇓st'. The only
- nontrivial cases are when the final rule in the derivation is
- E_WhileEnd or E_WhileLoop.
-
-
-
-
-
E_WhileEnd: In this case, the form of the rule gives us
- bevalstb1=false and st=st'. But then, since
- b1 and b1' are equivalent, we have bevalstb1'=
- false, and E-WhileEnd applies, giving us WHILEb1'DO
- c1'END/st⇓st', as required.
-
-
-
-
-
-
E_WhileLoop: The form of the rule now gives us bevalst
- b1=true, with c1/st⇓st'0 and WHILEb1DOc1
- END/st'0⇓st' for some state st'0, with the
- induction hypothesis WHILEb1'DOc1'END/st'0⇓
- st'.
-
-
-
- Since c1 and c1' are equivalent, we know that c1'/
- st⇓st'0. And since b1 and b1' are equivalent, we
- have bevalstb1'=true. Now E-WhileLoop applies,
- giving us WHILEb1'DOc1'END/st⇓st', as
- required.
-
-
-
- A program transformation is a function that takes a program
- as input and produces some variant of the program as its
- output. Compiler optimizations such as constant folding are
- a canonical example, but there are many others.
-
-
- A program transformation is sound if it preserves the
- behavior of the original program.
-
-
-
- We can define a notion of soundness for translations of
- aexps, bexps, and coms.
-
-Note that this version of constant folding doesn't eliminate
- trivial additions, etc. — we are focusing attention on a single
- optimization for the sake of simplicity. It is not hard to
- incorporate other ways of simplifying expressions; the definitions
- and proofs just get longer.
-
- Not only can we lift fold_constants_aexp to bexps (in the
- BEq and BLe cases), we can also find constant boolean
- expressions and reduce them in-place.
-
-Proof.
- unfoldatrans_sound. introsa. unfoldaequiv. introsst.
- aexp_cases (inductiona) Case; simpl;
- (* ANum and AId follow immediately *)
- tryreflexivity;
- (* APlus, AMinus, and AMult follow from the IH
- and the observation that
- aeval st (APlus a1 a2)
- = ANum ((aeval st a1) + (aeval st a2))
- = aeval st (ANum ((aeval st a1) + (aeval st a2)))
- (and similarly for AMinus/minus and AMult/mult) *)
- try (destruct (fold_constants_aexpa1);
- destruct (fold_constants_aexpa2);
- rewriteIHa1; rewriteIHa2; reflexivity). Qed.
-
- Here is an informal proof of the BEq case of the soundness
- argument for boolean expression constant folding. Read it
- carefully and compare it to the formal proof that follows. Then
- fill in the BLe case of the formal proof (without looking at the
- BEq case, if possible).
-
-
-
- Theorem: The constant folding function for booleans,
- fold_constants_bexp, is sound.
-
-
-
- Proof: We must show that b is equivalent to fold_constants_bexp,
- for all boolean expressions b. Proceed by induction on b. We
- show just the case where b has the form BEqa1a2.
-
-
-
-
-Theoremfold_constants_bexp_sound:
- btrans_soundfold_constants_bexp.
-Proof.
- unfoldbtrans_sound. introsb. unfoldbequiv. introsst.
- bexp_cases (inductionb) Case;
- (* BTrue and BFalse are immediate *)
- tryreflexivity.
- Case "BEq".
- (* Doing induction when there are a lot of constructors makes
- specifying variable names a chore, but Coq doesn't always
- choose nice variable names. We can rename entries in the
- context with the rename tactic: renameaintoa1 will
- change a to a1 in the current goal and context. *)
- renameaintoa1. renamea0intoa2. simpl.
- remember (fold_constants_aexpa1) asa1'eqn:Heqa1'.
- remember (fold_constants_aexpa2) asa2'eqn:Heqa2'.
- replace (aevalsta1) with (aevalsta1') by
- (substa1'; rewrite←fold_constants_aexp_sound; reflexivity).
- replace (aevalsta2) with (aevalsta2') by
- (substa2'; rewrite←fold_constants_aexp_sound; reflexivity).
- destructa1'; destructa2'; tryreflexivity.
- (* The only interesting case is when both a1 and a2
- become constants after folding *)
- simpl. destruct (beq_natnn0); reflexivity.
- Case "BLe".
- (* FILL IN HERE *)admit.
- Case "BNot".
- simpl. remember (fold_constants_bexpb) asb'eqn:Heqb'.
- rewriteIHb.
- destructb'; reflexivity.
- Case "BAnd".
- simpl.
- remember (fold_constants_bexpb1) asb1'eqn:Heqb1'.
- remember (fold_constants_bexpb2) asb2'eqn:Heqb2'.
- rewriteIHb1. rewriteIHb2.
- destructb1'; destructb2'; reflexivity. Qed.
-
-
-
-☐
-
-
-
Exercise: 3 stars (fold_constants_com_sound)
- Complete the WHILE case of the following proof.
-
-
-
-
-Theoremfold_constants_com_sound :
- ctrans_soundfold_constants_com.
-Proof.
- unfoldctrans_sound. introsc.
- com_cases (inductionc) Case; simpl.
- Case "SKIP". applyrefl_cequiv.
- Case "::=". applyCAss_congruence. applyfold_constants_aexp_sound.
- Case ";;". applyCSeq_congruence; assumption.
- Case "IFB".
- assert (bequivb (fold_constants_bexpb)).
- SCase "Pf of assertion". applyfold_constants_bexp_sound.
- destruct (fold_constants_bexpb) eqn:Heqb;
- (* If the optimization doesn't eliminate the if, then the result
- is easy to prove from the IH and fold_constants_bexp_sound *)
- try (applyCIf_congruence; assumption).
- SCase "b always true".
- applytrans_cequivwithc1; tryassumption.
- applyIFB_true; assumption.
- SCase "b always false".
- applytrans_cequivwithc2; tryassumption.
- applyIFB_false; assumption.
- Case "WHILE".
- (* FILL IN HERE *)Admitted.
-
- Prove that these three functions are sound, as we did for
- fold_constants_×. (Make sure you use the congruence lemmas in
- the proof of optimize_0plus_com — otherwise it will be long!)
-
-
-
- Then define an optimizer on commands that first folds
- constants (using fold_constants_com) and then eliminates 0+n
- terms (using optimize_0plus_com).
-
-
-
-
-
Give a meaningful example of this optimizer's output.
-
-
-
-
-
-
Prove that the optimizer is sound. (This part should be very
- easy.)
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Proving That Programs Are Not Equivalent
-
-
-
- Suppose that c1 is a command of the form X::=a1;;Y::=a2
- and c2 is the command X::=a1;;Y::=a2', where a2' is
- formed by substituting a1 for all occurrences of X in a2.
- For example, c1 and c2 might be:
-
-
-
-
- c1 = (X ::= 42 + 53;;
- Y ::= Y + X)
- c2 = (X ::= 42 + 53;;
- Y ::= Y + (42 + 53))
-
-
-
- Clearly, this particularc1 and c2 are equivalent. Is this
- true in general?
-
-
- We will see in a moment that it is not, but it is worthwhile
- to pause, now, and see if you can find a counter-example on your
- own.
-
-
- Here, formally, is the function that substitutes an arithmetic
- expression for each occurrence of a given variable in another
- expression:
-
-Proof.
- unfoldsubst_equiv_property.
- introsContra.
-
-
- (* Here is the counterexample: assuming that subst_equiv_property
- holds allows us to prove that these two programs are
- equivalent... *)
- remember (X ::= APlus (AIdX) (ANum 1);;
- Y ::= AIdX)
- asc1.
- remember (X ::= APlus (AIdX) (ANum 1);;
- Y ::= APlus (AIdX) (ANum 1))
- asc2.
- assert (cequivc1c2) by (subst; applyContra).
-
-
- (* ... allows us to show that the command c2 can terminate
- in two different final states:
- st1 = {X |-> 1, Y |-> 1}
- st2 = {X |-> 1, Y |-> 2}. *)
- remember (update (updateempty_stateX 1) Y 1) asst1.
- remember (update (updateempty_stateX 1) Y 2) asst2.
- assert (H1: c1 / empty_state⇓st1);
- assert (H2: c2 / empty_state⇓st2);
- try (subst;
- applyE_Seqwith (st' := (updateempty_stateX 1));
- applyE_Ass; reflexivity).
- applyHinH1.
-
-
- (* Finally, we use the fact that evaluation is deterministic
- to obtain a contradiction. *)
- assert (Hcontra: st1 = st2)
- by (apply (ceval_deterministicc2empty_state); assumption).
- assert (Hcontra': st1Y = st2Y)
- by (rewriteHcontra; reflexivity).
- subst. inversionHcontra'. Qed.
-
-
-
-
-
-
-
Exercise: 4 stars, optional (better_subst_equiv)
- The equivalence we had in mind above was not complete nonsense —
- it was actually almost right. To make it correct, we just need to
- exclude the case where the variable X occurs in the
- right-hand-side of the first assignment statement.
-
-Using var_not_used_in_aexp, formalize and prove a correct verson
- of subst_equiv_property.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (inequiv_exercise)
- Prove that an infinite loop is not equivalent to SKIP
-
-
-
-
-Theoreminequiv_exercise:
- ¬ cequiv (WHILEBTrueDOSKIPEND) SKIP.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Extended exercise: Non-deterministic Imp
-
-
-
- As we have seen (in theorem ceval_deterministic in the Imp
- chapter), Imp's evaluation relation is deterministic. However,
- non-determinism is an important part of the definition of many
- real programming languages. For example, in many imperative
- languages (such as C and its relatives), the order in which
- function arguments are evaluated is unspecified. The program
- fragment
-
-
-
-
- x = 0;;
- f(++x, x)
-
-
-
- might call f with arguments (1,0) or (1,1), depending how
- the compiler chooses to order things. This can be a little
- confusing for programmers, but it gives the compiler writer useful
- freedom.
-
-
-
- In this exercise, we will extend Imp with a simple
- non-deterministic command and study how this change affects
- program equivalence. The new command has the syntax HAVOCX,
- where X is an identifier. The effect of executing HAVOCX is
- to assign an arbitrary number to the variable X,
- non-deterministically. For example, after executing the program:
-
-
-
-
- HAVOCY;;
- Z ::= Y × 2
-
-
-
- the value of Y can be any number, while the value of Z is
- twice that of Y (so Z is always even). Note that we are not
- saying anything about the probabilities of the outcomes — just
- that there are (infinitely) many different outcomes that can
- possibly happen after executing this non-deterministic code.
-
-
-
- In a sense a variable on which we do HAVOC roughly corresponds
- to an unitialized variable in the C programming language. After
- the HAVOC the variable holds a fixed but arbitrary number. Most
- sources of nondeterminism in language definitions are there
- precisely because programmers don't care which choice is made (and
- so it is good to leave it open to the compiler to choose whichever
- will run faster).
-
-
-
- We call this new language Himp (``Imp extended with HAVOC'').
-
-
-
-
-ModuleHimp.
-
-
-
-
-
-To formalize the language, we first add a clause to the definition of
- commands.
-
- Now, we must extend the operational semantics. We have provided
- a template for the ceval relation below, specifying the big-step
- semantics. What rule(s) must be added to the definition of ceval
- to formalize the behavior of the HAVOC command?
-
-This definition still makes perfect sense in the case of always
- terminating programs, so let's apply it to prove some
- non-deterministic programs equivalent or non-equivalent.
-
-If you think they are equivalent, then prove it. If you think they
- are not, then prove that. (Hint: You may find the assert tactic
- useful.)
-
-
-
-
-Theoremptwice_cequiv_pcopy :
- cequivptwicepcopy∨ ¬cequivptwicepcopy.
-Proof. (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- The definition of program equivalence we are using here has some
- subtle consequences on programs that may loop forever. What
- cequiv says is that the set of possible terminating outcomes
- of two equivalent programs is the same. However, in a language
- with non-determinism, like Himp, some programs always terminate,
- some programs always diverge, and some programs can
- non-deterministically terminate in some runs and diverge in
- others. The final part of the following exercise illustrates this
- phenomenon.
-
-
-
-
Exercise: 5 stars, advanced (p1_p2_equiv)
- Prove that p1 and p2 are equivalent. In this and the following
- exercises, try to understand why the cequiv definition has the
- behavior it has on these examples.
-
-
-
-
-Definitionp1 : com :=
- WHILE (BNot (BEq (AIdX) (ANum 0))) DO
- HAVOCY;;
- X ::= APlus (AIdX) (ANum 1)
- END.
-
-
-Definitionp2 : com :=
- WHILE (BNot (BEq (AIdX) (ANum 0))) DO
- SKIP
- END.
-
-
-
-
-
-Intuitively, the programs have the same termination
- behavior: either they loop forever, or they terminate in the
- same state they started in. We can capture the termination
- behavior of p1 and p2 individually with these lemmas:
-
-
-
-
-Lemmap1_may_diverge : ∀stst', stX ≠ 0 →
- ¬ p1 / st⇓st'.
-Proof. (* FILL IN HERE *)Admitted.
-
-
-Lemmap2_may_diverge : ∀stst', stX ≠ 0 →
- ¬ p2 / st⇓st'.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-
-
-
-You should use these lemmas to prove that p1 and p2 are actually
- equivalent.
-
-
-
-
-Theoremp1_p2_equiv : cequivp1p2.
-Proof. (* FILL IN HERE *)Admitted.
-
-
-
-
-
-
Exercise: 4 stars, advanced (p3_p4_inquiv)
-
-
-
- Prove that the following programs are not equivalent.
-
-
-
-
-Definitionp3 : com :=
- Z ::= ANum 1;;
- WHILE (BNot (BEq (AIdX) (ANum 0))) DO
- HAVOCX;;
- HAVOCZ
- END.
-
-
-Definitionp4 : com :=
- X ::= (ANum 0);;
- Z ::= (ANum 1).
-
-
-Theoremp3_p4_inequiv : ¬ cequivp3p4.
-Proof. (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Definitionp5 : com :=
- WHILE (BNot (BEq (AIdX) (ANum 1))) DO
- HAVOCX
- END.
-
-
-Definitionp6 : com :=
- X ::= ANum 1.
-
-
-Theoremp5_p6_equiv : cequivp5p6.
-Proof. (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-EndHimp.
-
-
-
-
-
-
Doing Without Extensionality (Optional)
-
-
-
- Purists might object to using the functional_extensionality
- axiom. In general, it can be quite dangerous to add axioms,
- particularly several at once (as they may be mutually
- inconsistent). In fact, functional_extensionality and
- excluded_middle can both be assumed without any problems, but
- some Coq users prefer to avoid such "heavyweight" general
- techniques, and instead craft solutions for specific problems that
- stay within Coq's standard logic.
-
-
-
- For our particular problem here, rather than extending the
- definition of equality to do what we want on functions
- representing states, we could instead give an explicit notion of
- equivalence on states. For example:
-
-It is easy to prove that stequiv is an equivalence (i.e., it
- is reflexive, symmetric, and transitive), so it partitions the set
- of all states into equivalence classes.
-
-
-
Exercise: 1 star, optional (stequiv_refl)
-
-
-
-Lemmastequiv_refl : ∀(st : state),
- st ¬ st.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (stequiv_sym)
-
-
-
-Lemmastequiv_sym : ∀(st1st2 : state),
- st1 ¬ st2→
- st2 ¬ st1.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (stequiv_trans)
-
-
-
-Lemmastequiv_trans : ∀(st1st2st3 : state),
- st1 ¬ st2→
- st2 ¬ st3→
- st1 ¬ st3.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- Another useful fact...
Exercise: 1 star, optional (stequiv_update)
-
-
-
-Lemmastequiv_update : ∀(st1st2 : state),
- st1 ¬ st2→
- ∀(X:id) (n:nat),
- updatest1Xn ¬ updatest2Xn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- It is then straightforward to show that aeval and beval behave
- uniformly on all members of an equivalence class:
-
-
-
Exercise: 2 stars, optional (stequiv_aeval)
-
-
-
-Lemmastequiv_aeval : ∀(st1st2 : state),
- st1 ¬ st2→
- ∀(a:aexp), aevalst1a = aevalst2a.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (stequiv_beval)
-
-
-
-Lemmastequiv_beval : ∀(st1st2 : state),
- st1 ¬ st2→
- ∀(b:bexp), bevalst1b = bevalst2b.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- We can also characterize the behavior of ceval on equivalent
- states (this result is a bit more complicated to write down
- because ceval is a relation).
-
-Now we need to redefine cequiv to use ¬ instead of =. It is
- not completely trivial to do this in a way that keeps the
- definition simple and symmetric, but here is one approach (thanks
- to Andrew McCreight). We first define a looser variant of ⇓
- that "folds in" the notion of equivalence.
-
-
-
-
-Reserved Notation "c1 '/' st '⇓'' st'" (atlevel 40, statlevel 39).
-
-
-Inductiveceval' : com→state→state→Prop :=
- | E_equiv : ∀cstst'st'',
- c / st⇓st'→
- st' ¬ st''→
- c / st⇓' st''
- where "c1 '/' st '⇓'' st'" := (ceval'c1stst').
-
-
-
-
-
-Now the revised definition of cequiv' looks familiar:
-
-A sanity check shows that the original notion of command
- equivalence is at least as strong as this new one. (The converse
- is not true, naturally.)
-
- Finally, here is our example once more... (You can complete the
- proof.)
-
-
-
-
-Exampleidentity_assignment' :
- cequiv'SKIP (X ::= AIdX).
-Proof.
- unfoldcequiv'. intros. split; intros.
- Case "→".
- inversionH; subst; clearH. inversionH0; subst.
- applyE_equivwith (updatest'0X (st'0X)).
- constructor. reflexivity. applystequiv_transwithst'0.
- unfoldstequiv. intros. applyupdate_same.
- reflexivity. assumption.
- Case "←".
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- On the whole, this explicit equivalence approach is considerably
- harder to work with than relying on functional
- extensionality. (Coq does have an advanced mechanism called
- "setoids" that makes working with equivalences somewhat easier, by
- allowing them to be registered with the system so that standard
- rewriting tactics work for them almost as well as for equalities.)
- But it is worth knowing about, because it applies even in
- situations where the equivalence in question is not over
- functions. For example, if we chose to represent state mappings
- as binary search trees, we would need to use an explicit
- equivalence of this kind.
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 4 stars, optional (for_while_equiv)
- This exercise extends the optional add_for_loop exercise from
- Imp.v, where you were asked to extend the language of commands
- with C-style for loops. Prove that the command:
-
-
-Theoremswap_noninterfering_assignments: ∀l1l2a1a2,
- l1 ≠ l2→
- var_not_used_in_aexpl1a2→
- var_not_used_in_aexpl2a1→
- cequiv
- (l1 ::= a1;; l2 ::= a2)
- (l2 ::= a2;; l1 ::= a1).
-Proof.
-(* Hint: You'll need functional_extensionality *)
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Equiv.v b/Equiv.v
deleted file mode 100644
index 45ba5ef..0000000
--- a/Equiv.v
+++ /dev/null
@@ -1,1773 +0,0 @@
-(** * Equiv: Program Equivalence *)
-
-
-
-Require Export Imp.
-
-(** *** Some general advice for working on exercises:
-
- - Most of the Coq proofs we ask you to do are similar to proofs
- that we've provided. Before starting to work on the homework
- problems, take the time to work through our proofs (both
- informally, on paper, and in Coq) and make sure you understand
- them in detail. This will save you a lot of time.
-
- - The Coq proofs we're doing now are sufficiently complicated that
- it is more or less impossible to complete them simply by random
- experimentation or "following your nose." You need to start
- with an idea about why the property is true and how the proof is
- going to go. The best way to do this is to write out at least a
- sketch of an informal proof on paper -- one that intuitively
- convinces you of the truth of the theorem -- before starting to
- work on the formal one. Alternately, grab a friend and try to
- convince them that the theorem is true; then try to formalize
- your explanation.
-
- - Use automation to save work! Some of the proofs in this
- chapter's exercises are pretty long if you try to write out all
- the cases explicitly. *)
-
-(* ####################################################### *)
-(** * Behavioral Equivalence *)
-
-(** In the last chapter, we investigated the correctness of a very
- simple program transformation: the [optimize_0plus] function. The
- programming language we were considering was the first version of
- the language of arithmetic expressions -- with no variables -- so
- in that setting it was very easy to define what it _means_ for a
- program transformation to be correct: it should always yield a
- program that evaluates to the same number as the original.
-
- To go further and talk about the correctness of program
- transformations in the full Imp language, we need to consider the
- role of variables and state. *)
-
-(* ####################################################### *)
-(** ** Definitions *)
-
-(** For [aexp]s and [bexp]s with variables, the definition we want is
- clear. We say
- that two [aexp]s or [bexp]s are _behaviorally equivalent_ if they
- evaluate to the same result _in every state_. *)
-
-Definition aequiv (a1 a2 : aexp) : Prop :=
- forall (st:state),
- aeval st a1 = aeval st a2.
-
-Definition bequiv (b1 b2 : bexp) : Prop :=
- forall (st:state),
- beval st b1 = beval st b2.
-
-(** For commands, the situation is a little more subtle. We can't
- simply say "two commands are behaviorally equivalent if they
- evaluate to the same ending state whenever they are started in the
- same initial state," because some commands (in some starting
- states) don't terminate in any final state at all! What we need
- instead is this: two commands are behaviorally equivalent if, for
- any given starting state, they either both diverge or both
- terminate in the same final state. A compact way to express this
- is "if the first one terminates in a particular state then so does
- the second, and vice versa." *)
-
-Definition cequiv (c1 c2 : com) : Prop :=
- forall (st st' : state),
- (c1 / st || st') <-> (c2 / st || st').
-
-
-
-(** **** Exercise: 2 stars (equiv_classes) *)
-
-(** Given the following programs, group together those that are
- equivalent in [Imp]. For example, if you think programs (a)
- through (h) are all equivalent to each other, but not to (i), your
- answer should look like this: {a,b,c,d,e,f,g,h} {i}.
-
-(a)
- WHILE X > 0 DO
- X ::= X + 1
- END
-
-(b)
- IFB X = 0 THEN
- X ::= X + 1;;
- Y ::= 1
- ELSE
- Y ::= 0
- FI;;
- X ::= X - Y;;
- Y ::= 0
-
-(c)
- SKIP
-
-(d)
- WHILE X <> 0 DO
- X ::= X * Y + 1
- END
-
-(e)
- Y ::= 0
-
-(f)
- Y ::= X + 1;;
- WHILE X <> Y DO
- Y ::= X + 1
- END
-
-(g)
- WHILE TRUE DO
- SKIP
- END
-
-(h)
- WHILE X <> X DO
- X ::= X + 1
- END
-
-(i)
- WHILE X <> Y DO
- X ::= Y + 1
- END
-
-(* FILL IN HERE *)
-[] *)
-
-
-(* ####################################################### *)
-(** ** Examples *)
-
-(** Here are some simple examples of equivalences of arithmetic
- and boolean expressions. *)
-
-Theorem aequiv_example:
- aequiv (AMinus (AId X) (AId X)) (ANum 0).
-Proof.
- intros st. simpl. omega.
-Qed.
-
-Theorem bequiv_example:
- bequiv (BEq (AMinus (AId X) (AId X)) (ANum 0)) BTrue.
-Proof.
- intros st. unfold beval.
- rewrite aequiv_example. reflexivity.
-Qed.
-
-(** For examples of command equivalence, let's start by looking at
- some trivial program transformations involving [SKIP]: *)
-
-Theorem skip_left: forall c,
- cequiv
- (SKIP;; c)
- c.
-Proof.
- (* WORKED IN CLASS *)
- intros c st st'.
- split; intros H.
- Case "->".
- inversion H. subst.
- inversion H2. subst.
- assumption.
- Case "<-".
- apply E_Seq with st.
- apply E_Skip.
- assumption.
-Qed.
-
-(** **** Exercise: 2 stars (skip_right) *)
-(** Prove that adding a SKIP after a command results in an equivalent
- program *)
-
-Theorem skip_right: forall c,
- cequiv
- (c;; SKIP)
- c.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Similarly, here is a simple transformations that simplifies [IFB]
- commands: *)
-
-Theorem IFB_true_simple: forall c1 c2,
- cequiv
- (IFB BTrue THEN c1 ELSE c2 FI)
- c1.
-Proof.
- intros c1 c2.
- split; intros H.
- Case "->".
- inversion H; subst. assumption. inversion H5.
- Case "<-".
- apply E_IfTrue. reflexivity. assumption. Qed.
-
-
-(** Of course, few programmers would be tempted to write a conditional
- whose guard is literally [BTrue]. A more interesting case is when
- the guard is _equivalent_ to true:
-
- _Theorem_: If [b] is equivalent to [BTrue], then [IFB b THEN c1
- ELSE c2 FI] is equivalent to [c1].
-*)
-(** ** *)
-(**
- _Proof_:
-
- - ([->]) We must show, for all [st] and [st'], that if [IFB b
- THEN c1 ELSE c2 FI / st || st'] then [c1 / st || st'].
-
- Proceed by cases on the rules that could possibly have been
- used to show [IFB b THEN c1 ELSE c2 FI / st || st'], namely
- [E_IfTrue] and [E_IfFalse].
-
- - Suppose the final rule rule in the derivation of [IFB b THEN
- c1 ELSE c2 FI / st || st'] was [E_IfTrue]. We then have, by
- the premises of [E_IfTrue], that [c1 / st || st']. This is
- exactly what we set out to prove.
-
- - On the other hand, suppose the final rule in the derivation
- of [IFB b THEN c1 ELSE c2 FI / st || st'] was [E_IfFalse].
- We then know that [beval st b = false] and [c2 / st || st'].
-
- Recall that [b] is equivalent to [BTrue], i.e. forall [st],
- [beval st b = beval st BTrue]. In particular, this means
- that [beval st b = true], since [beval st BTrue = true]. But
- this is a contradiction, since [E_IfFalse] requires that
- [beval st b = false]. Thus, the final rule could not have
- been [E_IfFalse].
-
- - ([<-]) We must show, for all [st] and [st'], that if [c1 / st
- || st'] then [IFB b THEN c1 ELSE c2 FI / st || st'].
-
- Since [b] is equivalent to [BTrue], we know that [beval st b] =
- [beval st BTrue] = [true]. Together with the assumption that
- [c1 / st || st'], we can apply [E_IfTrue] to derive [IFB b THEN
- c1 ELSE c2 FI / st || st']. []
-
- Here is the formal version of this proof: *)
-
-Theorem IFB_true: forall b c1 c2,
- bequiv b BTrue ->
- cequiv
- (IFB b THEN c1 ELSE c2 FI)
- c1.
-Proof.
- intros b c1 c2 Hb.
- split; intros H.
- Case "->".
- inversion H; subst.
- SCase "b evaluates to true".
- assumption.
- SCase "b evaluates to false (contradiction)".
- unfold bequiv in Hb. simpl in Hb.
- rewrite Hb in H5.
- inversion H5.
- Case "<-".
- apply E_IfTrue; try assumption.
- unfold bequiv in Hb. simpl in Hb.
- rewrite Hb. reflexivity. Qed.
-
-(** **** Exercise: 2 stars (IFB_false) *)
-Theorem IFB_false: forall b c1 c2,
- bequiv b BFalse ->
- cequiv
- (IFB b THEN c1 ELSE c2 FI)
- c2.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (swap_if_branches) *)
-(** Show that we can swap the branches of an IF by negating its
- condition *)
-
-Theorem swap_if_branches: forall b e1 e2,
- cequiv
- (IFB b THEN e1 ELSE e2 FI)
- (IFB BNot b THEN e2 ELSE e1 FI).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** ** *)
-
-(** For [WHILE] loops, we can give a similar pair of theorems. A loop
- whose guard is equivalent to [BFalse] is equivalent to [SKIP],
- while a loop whose guard is equivalent to [BTrue] is equivalent to
- [WHILE BTrue DO SKIP END] (or any other non-terminating program).
- The first of these facts is easy. *)
-
-Theorem WHILE_false : forall b c,
- bequiv b BFalse ->
- cequiv
- (WHILE b DO c END)
- SKIP.
-Proof.
- intros b c Hb. split; intros H.
- Case "->".
- inversion H; subst.
- SCase "E_WhileEnd".
- apply E_Skip.
- SCase "E_WhileLoop".
- rewrite Hb in H2. inversion H2.
- Case "<-".
- inversion H; subst.
- apply E_WhileEnd.
- rewrite Hb.
- reflexivity. Qed.
-
-(** **** Exercise: 2 stars, advanced, optional (WHILE_false_informal) *)
-(** Write an informal proof of [WHILE_false].
-
-(* FILL IN HERE *)
-[]
-*)
-
-(** ** *)
-(** To prove the second fact, we need an auxiliary lemma stating that
- [WHILE] loops whose guards are equivalent to [BTrue] never
- terminate:
-
- _Lemma_: If [b] is equivalent to [BTrue], then it cannot be the
- case that [(WHILE b DO c END) / st || st'].
-
- _Proof_: Suppose that [(WHILE b DO c END) / st || st']. We show,
- by induction on a derivation of [(WHILE b DO c END) / st || st'],
- that this assumption leads to a contradiction.
-
- - Suppose [(WHILE b DO c END) / st || st'] is proved using rule
- [E_WhileEnd]. Then by assumption [beval st b = false]. But
- this contradicts the assumption that [b] is equivalent to
- [BTrue].
-
- - Suppose [(WHILE b DO c END) / st || st'] is proved using rule
- [E_WhileLoop]. Then we are given the induction hypothesis
- that [(WHILE b DO c END) / st || st'] is contradictory, which
- is exactly what we are trying to prove!
-
- - Since these are the only rules that could have been used to
- prove [(WHILE b DO c END) / st || st'], the other cases of
- the induction are immediately contradictory. [] *)
-
-Lemma WHILE_true_nonterm : forall b c st st',
- bequiv b BTrue ->
- ~( (WHILE b DO c END) / st || st' ).
-Proof.
- (* WORKED IN CLASS *)
- intros b c st st' Hb.
- intros H.
- remember (WHILE b DO c END) as cw eqn:Heqcw.
- ceval_cases (induction H) Case;
- (* Most rules don't apply, and we can rule them out
- by inversion *)
- inversion Heqcw; subst; clear Heqcw.
- (* The two interesting cases are the ones for WHILE loops: *)
- Case "E_WhileEnd". (* contradictory -- b is always true! *)
- unfold bequiv in Hb.
- (* [rewrite] is able to instantiate the quantifier in [st] *)
- rewrite Hb in H. inversion H.
- Case "E_WhileLoop". (* immediate from the IH *)
- apply IHceval2. reflexivity. Qed.
-
-(** **** Exercise: 2 stars, optional (WHILE_true_nonterm_informal) *)
-(** Explain what the lemma [WHILE_true_nonterm] means in English.
-
-(* FILL IN HERE *)
-*)
-(** [] *)
-
-(** **** Exercise: 2 stars (WHILE_true) *)
-(** Prove the following theorem. _Hint_: You'll want to use
- [WHILE_true_nonterm] here. *)
-
-Theorem WHILE_true: forall b c,
- bequiv b BTrue ->
- cequiv
- (WHILE b DO c END)
- (WHILE BTrue DO SKIP END).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-Theorem loop_unrolling: forall b c,
- cequiv
- (WHILE b DO c END)
- (IFB b THEN (c;; WHILE b DO c END) ELSE SKIP FI).
-Proof.
- (* WORKED IN CLASS *)
- intros b c st st'.
- split; intros Hce.
- Case "->".
- inversion Hce; subst.
- SCase "loop doesn't run".
- apply E_IfFalse. assumption. apply E_Skip.
- SCase "loop runs".
- apply E_IfTrue. assumption.
- apply E_Seq with (st' := st'0). assumption. assumption.
- Case "<-".
- inversion Hce; subst.
- SCase "loop runs".
- inversion H5; subst.
- apply E_WhileLoop with (st' := st'0).
- assumption. assumption. assumption.
- SCase "loop doesn't run".
- inversion H5; subst. apply E_WhileEnd. assumption. Qed.
-
-(** **** Exercise: 2 stars, optional (seq_assoc) *)
-Theorem seq_assoc : forall c1 c2 c3,
- cequiv ((c1;;c2);;c3) (c1;;(c2;;c3)).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** ** The Functional Equivalence Axiom *)
-
-(** Finally, let's look at simple equivalences involving assignments.
- For example, we might expect to be able to show that [X ::= AId X]
- is equivalent to [SKIP]. However, when we try to show it, we get
- stuck in an interesting way. *)
-
-Theorem identity_assignment_first_try : forall (X:id),
- cequiv (X ::= AId X) SKIP.
-Proof.
- intros. split; intro H.
- Case "->".
- inversion H; subst. simpl.
- replace (update st X (st X)) with st.
- constructor.
- (* Stuck... *) Abort.
-
-(** Here we're stuck. The goal looks reasonable, but in fact it is not
- provable! If we look back at the set of lemmas we proved about
- [update] in the last chapter, we can see that lemma [update_same]
- almost does the job, but not quite: it says that the original and
- updated states agree at all values, but this is not the same thing
- as saying that they are [=] in Coq's sense! *)
-
-(** What is going on here? Recall that our states are just
- functions from identifiers to values. For Coq, functions are only
- equal when their definitions are syntactically the same, modulo
- simplification. (This is the only way we can legally apply the
- [refl_equal] constructor of the inductively defined proposition
- [eq]!) In practice, for functions built up by repeated uses of the
- [update] operation, this means that two functions can be proven
- equal only if they were constructed using the _same_ [update]
- operations, applied in the same order. In the theorem above, the
- sequence of updates on the first parameter [cequiv] is one longer
- than for the second parameter, so it is no wonder that the
- equality doesn't hold. *)
-
-(** ** *)
-(** This problem is actually quite general. If we try to prove other
- simple facts, such as
- cequiv (X ::= X + 1;;
- X ::= X + 1)
- (X ::= X + 2)
- or
- cequiv (X ::= 1;; Y ::= 2)
- (y ::= 2;; X ::= 1)
-
- we'll get stuck in the same way: we'll have two functions that
- behave the same way on all inputs, but cannot be proven to be [eq]
- to each other.
-
- The reasoning principle we would like to use in these situations
- is called _functional extensionality_:
- forall x, f x = g x
- -------------------
- f = g
- Although this principle is not derivable in Coq's built-in logic,
- it is safe to add it as an additional _axiom_. *)
-
-Axiom functional_extensionality : forall {X Y: Type} {f g : X -> Y},
- (forall (x: X), f x = g x) -> f = g.
-
-(** It can be shown that adding this axiom doesn't introduce any
- inconsistencies into Coq. (In this way, it is similar to adding
- one of the classical logic axioms, such as [excluded_middle].) *)
-
-(** With the benefit of this axiom we can prove our theorem. *)
-
-Theorem identity_assignment : forall (X:id),
- cequiv
- (X ::= AId X)
- SKIP.
-Proof.
- intros. split; intro H.
- Case "->".
- inversion H; subst. simpl.
- replace (update st X (st X)) with st.
- constructor.
- apply functional_extensionality. intro.
- rewrite update_same; reflexivity.
- Case "<-".
- inversion H; subst.
- assert (st' = (update st' X (st' X))).
- apply functional_extensionality. intro.
- rewrite update_same; reflexivity.
- rewrite H0 at 2.
- constructor. reflexivity.
-Qed.
-
-(** **** Exercise: 2 stars (assign_aequiv) *)
-Theorem assign_aequiv : forall X e,
- aequiv (AId X) e ->
- cequiv SKIP (X ::= e).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ####################################################### *)
-(** * Properties of Behavioral Equivalence *)
-
-(** We now turn to developing some of the properties of the program
- equivalences we have defined. *)
-
-(* ####################################################### *)
-(** ** Behavioral Equivalence is an Equivalence *)
-
-(** First, we verify that the equivalences on [aexps], [bexps], and
- [com]s really are _equivalences_ -- i.e., that they are reflexive,
- symmetric, and transitive. The proofs are all easy. *)
-
-Lemma refl_aequiv : forall (a : aexp), aequiv a a.
-Proof.
- intros a st. reflexivity. Qed.
-
-Lemma sym_aequiv : forall (a1 a2 : aexp),
- aequiv a1 a2 -> aequiv a2 a1.
-Proof.
- intros a1 a2 H. intros st. symmetry. apply H. Qed.
-
-Lemma trans_aequiv : forall (a1 a2 a3 : aexp),
- aequiv a1 a2 -> aequiv a2 a3 -> aequiv a1 a3.
-Proof.
- unfold aequiv. intros a1 a2 a3 H12 H23 st.
- rewrite (H12 st). rewrite (H23 st). reflexivity. Qed.
-
-Lemma refl_bequiv : forall (b : bexp), bequiv b b.
-Proof.
- unfold bequiv. intros b st. reflexivity. Qed.
-
-Lemma sym_bequiv : forall (b1 b2 : bexp),
- bequiv b1 b2 -> bequiv b2 b1.
-Proof.
- unfold bequiv. intros b1 b2 H. intros st. symmetry. apply H. Qed.
-
-Lemma trans_bequiv : forall (b1 b2 b3 : bexp),
- bequiv b1 b2 -> bequiv b2 b3 -> bequiv b1 b3.
-Proof.
- unfold bequiv. intros b1 b2 b3 H12 H23 st.
- rewrite (H12 st). rewrite (H23 st). reflexivity. Qed.
-
-Lemma refl_cequiv : forall (c : com), cequiv c c.
-Proof.
- unfold cequiv. intros c st st'. apply iff_refl. Qed.
-
-Lemma sym_cequiv : forall (c1 c2 : com),
- cequiv c1 c2 -> cequiv c2 c1.
-Proof.
- unfold cequiv. intros c1 c2 H st st'.
- assert (c1 / st || st' <-> c2 / st || st') as H'.
- SCase "Proof of assertion". apply H.
- apply iff_sym. assumption.
-Qed.
-
-Lemma iff_trans : forall (P1 P2 P3 : Prop),
- (P1 <-> P2) -> (P2 <-> P3) -> (P1 <-> P3).
-Proof.
- intros P1 P2 P3 H12 H23.
- inversion H12. inversion H23.
- split; intros A.
- apply H1. apply H. apply A.
- apply H0. apply H2. apply A. Qed.
-
-Lemma trans_cequiv : forall (c1 c2 c3 : com),
- cequiv c1 c2 -> cequiv c2 c3 -> cequiv c1 c3.
-Proof.
- unfold cequiv. intros c1 c2 c3 H12 H23 st st'.
- apply iff_trans with (c2 / st || st'). apply H12. apply H23. Qed.
-
-(* ######################################################## *)
-(** ** Behavioral Equivalence is a Congruence *)
-
-(** Less obviously, behavioral equivalence is also a _congruence_.
- That is, the equivalence of two subprograms implies the
- equivalence of the larger programs in which they are embedded:
- aequiv a1 a1'
- -----------------------------
- cequiv (i ::= a1) (i ::= a1')
-
- cequiv c1 c1'
- cequiv c2 c2'
- ------------------------
- cequiv (c1;;c2) (c1';;c2')
- ...and so on.
-
- (Note that we are using the inference rule notation here not as
- part of a definition, but simply to write down some valid
- implications in a readable format. We prove these implications
- below.) *)
-
-(** We will see a concrete example of why these congruence
- properties are important in the following section (in the proof of
- [fold_constants_com_sound]), but the main idea is that they allow
- us to replace a small part of a large program with an equivalent
- small part and know that the whole large programs are equivalent
- _without_ doing an explicit proof about the non-varying parts --
- i.e., the "proof burden" of a small change to a large program is
- proportional to the size of the change, not the program. *)
-
-Theorem CAss_congruence : forall i a1 a1',
- aequiv a1 a1' ->
- cequiv (CAss i a1) (CAss i a1').
-Proof.
- intros i a1 a2 Heqv st st'.
- split; intros Hceval.
- Case "->".
- inversion Hceval. subst. apply E_Ass.
- rewrite Heqv. reflexivity.
- Case "<-".
- inversion Hceval. subst. apply E_Ass.
- rewrite Heqv. reflexivity. Qed.
-
-(** The congruence property for loops is a little more interesting,
- since it requires induction.
-
- _Theorem_: Equivalence is a congruence for [WHILE] -- that is, if
- [b1] is equivalent to [b1'] and [c1] is equivalent to [c1'], then
- [WHILE b1 DO c1 END] is equivalent to [WHILE b1' DO c1' END].
-
- _Proof_: Suppose [b1] is equivalent to [b1'] and [c1] is
- equivalent to [c1']. We must show, for every [st] and [st'], that
- [WHILE b1 DO c1 END / st || st'] iff [WHILE b1' DO c1' END / st
- || st']. We consider the two directions separately.
-
- - ([->]) We show that [WHILE b1 DO c1 END / st || st'] implies
- [WHILE b1' DO c1' END / st || st'], by induction on a
- derivation of [WHILE b1 DO c1 END / st || st']. The only
- nontrivial cases are when the final rule in the derivation is
- [E_WhileEnd] or [E_WhileLoop].
-
- - [E_WhileEnd]: In this case, the form of the rule gives us
- [beval st b1 = false] and [st = st']. But then, since
- [b1] and [b1'] are equivalent, we have [beval st b1' =
- false], and [E-WhileEnd] applies, giving us [WHILE b1' DO
- c1' END / st || st'], as required.
-
- - [E_WhileLoop]: The form of the rule now gives us [beval st
- b1 = true], with [c1 / st || st'0] and [WHILE b1 DO c1
- END / st'0 || st'] for some state [st'0], with the
- induction hypothesis [WHILE b1' DO c1' END / st'0 ||
- st'].
-
- Since [c1] and [c1'] are equivalent, we know that [c1' /
- st || st'0]. And since [b1] and [b1'] are equivalent, we
- have [beval st b1' = true]. Now [E-WhileLoop] applies,
- giving us [WHILE b1' DO c1' END / st || st'], as
- required.
-
- - ([<-]) Similar. [] *)
-
-Theorem CWhile_congruence : forall b1 b1' c1 c1',
- bequiv b1 b1' -> cequiv c1 c1' ->
- cequiv (WHILE b1 DO c1 END) (WHILE b1' DO c1' END).
-Proof.
- (* WORKED IN CLASS *)
- unfold bequiv,cequiv.
- intros b1 b1' c1 c1' Hb1e Hc1e st st'.
- split; intros Hce.
- Case "->".
- remember (WHILE b1 DO c1 END) as cwhile eqn:Heqcwhile.
- induction Hce; inversion Heqcwhile; subst.
- SCase "E_WhileEnd".
- apply E_WhileEnd. rewrite <- Hb1e. apply H.
- SCase "E_WhileLoop".
- apply E_WhileLoop with (st' := st').
- SSCase "show loop runs". rewrite <- Hb1e. apply H.
- SSCase "body execution".
- apply (Hc1e st st'). apply Hce1.
- SSCase "subsequent loop execution".
- apply IHHce2. reflexivity.
- Case "<-".
- remember (WHILE b1' DO c1' END) as c'while eqn:Heqc'while.
- induction Hce; inversion Heqc'while; subst.
- SCase "E_WhileEnd".
- apply E_WhileEnd. rewrite -> Hb1e. apply H.
- SCase "E_WhileLoop".
- apply E_WhileLoop with (st' := st').
- SSCase "show loop runs". rewrite -> Hb1e. apply H.
- SSCase "body execution".
- apply (Hc1e st st'). apply Hce1.
- SSCase "subsequent loop execution".
- apply IHHce2. reflexivity. Qed.
-
-(** **** Exercise: 3 stars, optional (CSeq_congruence) *)
-Theorem CSeq_congruence : forall c1 c1' c2 c2',
- cequiv c1 c1' -> cequiv c2 c2' ->
- cequiv (c1;;c2) (c1';;c2').
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (CIf_congruence) *)
-Theorem CIf_congruence : forall b b' c1 c1' c2 c2',
- bequiv b b' -> cequiv c1 c1' -> cequiv c2 c2' ->
- cequiv (IFB b THEN c1 ELSE c2 FI) (IFB b' THEN c1' ELSE c2' FI).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** ** *)
-
-(** For example, here are two equivalent programs and a proof of their
- equivalence... *)
-
-Example congruence_example:
- cequiv
- (* Program 1: *)
- (X ::= ANum 0;;
- IFB (BEq (AId X) (ANum 0))
- THEN
- Y ::= ANum 0
- ELSE
- Y ::= ANum 42
- FI)
- (* Program 2: *)
- (X ::= ANum 0;;
- IFB (BEq (AId X) (ANum 0))
- THEN
- Y ::= AMinus (AId X) (AId X) (* <--- changed here *)
- ELSE
- Y ::= ANum 42
- FI).
-Proof.
- apply CSeq_congruence.
- apply refl_cequiv.
- apply CIf_congruence.
- apply refl_bequiv.
- apply CAss_congruence. unfold aequiv. simpl.
- symmetry. apply minus_diag.
- apply refl_cequiv.
-Qed.
-
-(* ####################################################### *)
-(** * Program Transformations *)
-
-(** A _program transformation_ is a function that takes a program
- as input and produces some variant of the program as its
- output. Compiler optimizations such as constant folding are
- a canonical example, but there are many others. *)
-
-(** A program transformation is _sound_ if it preserves the
- behavior of the original program.
-
- We can define a notion of soundness for translations of
- [aexp]s, [bexp]s, and [com]s. *)
-
-Definition atrans_sound (atrans : aexp -> aexp) : Prop :=
- forall (a : aexp),
- aequiv a (atrans a).
-
-Definition btrans_sound (btrans : bexp -> bexp) : Prop :=
- forall (b : bexp),
- bequiv b (btrans b).
-
-Definition ctrans_sound (ctrans : com -> com) : Prop :=
- forall (c : com),
- cequiv c (ctrans c).
-
-(* ######################################################## *)
-(** ** The Constant-Folding Transformation *)
-
-(** An expression is _constant_ when it contains no variable
- references.
-
- Constant folding is an optimization that finds constant
- expressions and replaces them by their values. *)
-
-Fixpoint fold_constants_aexp (a : aexp) : aexp :=
- match a with
- | ANum n => ANum n
- | AId i => AId i
- | APlus a1 a2 =>
- match (fold_constants_aexp a1, fold_constants_aexp a2) with
- | (ANum n1, ANum n2) => ANum (n1 + n2)
- | (a1', a2') => APlus a1' a2'
- end
- | AMinus a1 a2 =>
- match (fold_constants_aexp a1, fold_constants_aexp a2) with
- | (ANum n1, ANum n2) => ANum (n1 - n2)
- | (a1', a2') => AMinus a1' a2'
- end
- | AMult a1 a2 =>
- match (fold_constants_aexp a1, fold_constants_aexp a2) with
- | (ANum n1, ANum n2) => ANum (n1 * n2)
- | (a1', a2') => AMult a1' a2'
- end
- end.
-
-Example fold_aexp_ex1 :
- fold_constants_aexp
- (AMult (APlus (ANum 1) (ANum 2)) (AId X))
- = AMult (ANum 3) (AId X).
-Proof. reflexivity. Qed.
-
-(** Note that this version of constant folding doesn't eliminate
- trivial additions, etc. -- we are focusing attention on a single
- optimization for the sake of simplicity. It is not hard to
- incorporate other ways of simplifying expressions; the definitions
- and proofs just get longer. *)
-
-Example fold_aexp_ex2 :
- fold_constants_aexp
- (AMinus (AId X) (APlus (AMult (ANum 0) (ANum 6)) (AId Y)))
- = AMinus (AId X) (APlus (ANum 0) (AId Y)).
-Proof. reflexivity. Qed.
-
-(** ** *)
-(** Not only can we lift [fold_constants_aexp] to [bexp]s (in the
- [BEq] and [BLe] cases), we can also find constant _boolean_
- expressions and reduce them in-place. *)
-
-Fixpoint fold_constants_bexp (b : bexp) : bexp :=
- match b with
- | BTrue => BTrue
- | BFalse => BFalse
- | BEq a1 a2 =>
- match (fold_constants_aexp a1, fold_constants_aexp a2) with
- | (ANum n1, ANum n2) => if beq_nat n1 n2 then BTrue else BFalse
- | (a1', a2') => BEq a1' a2'
- end
- | BLe a1 a2 =>
- match (fold_constants_aexp a1, fold_constants_aexp a2) with
- | (ANum n1, ANum n2) => if ble_nat n1 n2 then BTrue else BFalse
- | (a1', a2') => BLe a1' a2'
- end
- | BNot b1 =>
- match (fold_constants_bexp b1) with
- | BTrue => BFalse
- | BFalse => BTrue
- | b1' => BNot b1'
- end
- | BAnd b1 b2 =>
- match (fold_constants_bexp b1, fold_constants_bexp b2) with
- | (BTrue, BTrue) => BTrue
- | (BTrue, BFalse) => BFalse
- | (BFalse, BTrue) => BFalse
- | (BFalse, BFalse) => BFalse
- | (b1', b2') => BAnd b1' b2'
- end
- end.
-
-Example fold_bexp_ex1 :
- fold_constants_bexp (BAnd BTrue (BNot (BAnd BFalse BTrue)))
- = BTrue.
-Proof. reflexivity. Qed.
-
-Example fold_bexp_ex2 :
- fold_constants_bexp
- (BAnd (BEq (AId X) (AId Y))
- (BEq (ANum 0)
- (AMinus (ANum 2) (APlus (ANum 1) (ANum 1)))))
- = BAnd (BEq (AId X) (AId Y)) BTrue.
-Proof. reflexivity. Qed.
-
-(** ** *)
-(** To fold constants in a command, we apply the appropriate folding
- functions on all embedded expressions. *)
-
-Fixpoint fold_constants_com (c : com) : com :=
- match c with
- | SKIP =>
- SKIP
- | i ::= a =>
- CAss i (fold_constants_aexp a)
- | c1 ;; c2 =>
- (fold_constants_com c1) ;; (fold_constants_com c2)
- | IFB b THEN c1 ELSE c2 FI =>
- match fold_constants_bexp b with
- | BTrue => fold_constants_com c1
- | BFalse => fold_constants_com c2
- | b' => IFB b' THEN fold_constants_com c1
- ELSE fold_constants_com c2 FI
- end
- | WHILE b DO c END =>
- match fold_constants_bexp b with
- | BTrue => WHILE BTrue DO SKIP END
- | BFalse => SKIP
- | b' => WHILE b' DO (fold_constants_com c) END
- end
- end.
-
-(** ** *)
-Example fold_com_ex1 :
- fold_constants_com
- (* Original program: *)
- (X ::= APlus (ANum 4) (ANum 5);;
- Y ::= AMinus (AId X) (ANum 3);;
- IFB BEq (AMinus (AId X) (AId Y)) (APlus (ANum 2) (ANum 4)) THEN
- SKIP
- ELSE
- Y ::= ANum 0
- FI;;
- IFB BLe (ANum 0) (AMinus (ANum 4) (APlus (ANum 2) (ANum 1))) THEN
- Y ::= ANum 0
- ELSE
- SKIP
- FI;;
- WHILE BEq (AId Y) (ANum 0) DO
- X ::= APlus (AId X) (ANum 1)
- END)
- = (* After constant folding: *)
- (X ::= ANum 9;;
- Y ::= AMinus (AId X) (ANum 3);;
- IFB BEq (AMinus (AId X) (AId Y)) (ANum 6) THEN
- SKIP
- ELSE
- (Y ::= ANum 0)
- FI;;
- Y ::= ANum 0;;
- WHILE BEq (AId Y) (ANum 0) DO
- X ::= APlus (AId X) (ANum 1)
- END).
-Proof. reflexivity. Qed.
-
-(* ################################################### *)
-(** ** Soundness of Constant Folding *)
-
-(** Now we need to show that what we've done is correct. *)
-
-(** Here's the proof for arithmetic expressions: *)
-
-Theorem fold_constants_aexp_sound :
- atrans_sound fold_constants_aexp.
-Proof.
- unfold atrans_sound. intros a. unfold aequiv. intros st.
- aexp_cases (induction a) Case; simpl;
- (* ANum and AId follow immediately *)
- try reflexivity;
- (* APlus, AMinus, and AMult follow from the IH
- and the observation that
- aeval st (APlus a1 a2)
- = ANum ((aeval st a1) + (aeval st a2))
- = aeval st (ANum ((aeval st a1) + (aeval st a2)))
- (and similarly for AMinus/minus and AMult/mult) *)
- try (destruct (fold_constants_aexp a1);
- destruct (fold_constants_aexp a2);
- rewrite IHa1; rewrite IHa2; reflexivity). Qed.
-
-(** **** Exercise: 3 stars, optional (fold_bexp_Eq_informal) *)
-(** Here is an informal proof of the [BEq] case of the soundness
- argument for boolean expression constant folding. Read it
- carefully and compare it to the formal proof that follows. Then
- fill in the [BLe] case of the formal proof (without looking at the
- [BEq] case, if possible).
-
- _Theorem_: The constant folding function for booleans,
- [fold_constants_bexp], is sound.
-
- _Proof_: We must show that [b] is equivalent to [fold_constants_bexp],
- for all boolean expressions [b]. Proceed by induction on [b]. We
- show just the case where [b] has the form [BEq a1 a2].
-
- In this case, we must show
- beval st (BEq a1 a2)
- = beval st (fold_constants_bexp (BEq a1 a2)).
- There are two cases to consider:
-
- - First, suppose [fold_constants_aexp a1 = ANum n1] and
- [fold_constants_aexp a2 = ANum n2] for some [n1] and [n2].
-
- In this case, we have
- fold_constants_bexp (BEq a1 a2)
- = if beq_nat n1 n2 then BTrue else BFalse
- and
- beval st (BEq a1 a2)
- = beq_nat (aeval st a1) (aeval st a2).
- By the soundness of constant folding for arithmetic
- expressions (Lemma [fold_constants_aexp_sound]), we know
- aeval st a1
- = aeval st (fold_constants_aexp a1)
- = aeval st (ANum n1)
- = n1
- and
- aeval st a2
- = aeval st (fold_constants_aexp a2)
- = aeval st (ANum n2)
- = n2,
- so
- beval st (BEq a1 a2)
- = beq_nat (aeval a1) (aeval a2)
- = beq_nat n1 n2.
- Also, it is easy to see (by considering the cases [n1 = n2] and
- [n1 <> n2] separately) that
- beval st (if beq_nat n1 n2 then BTrue else BFalse)
- = if beq_nat n1 n2 then beval st BTrue else beval st BFalse
- = if beq_nat n1 n2 then true else false
- = beq_nat n1 n2.
- So
- beval st (BEq a1 a2)
- = beq_nat n1 n2.
- = beval st (if beq_nat n1 n2 then BTrue else BFalse),
-]]
- as required.
-
- - Otherwise, one of [fold_constants_aexp a1] and
- [fold_constants_aexp a2] is not a constant. In this case, we
- must show
- beval st (BEq a1 a2)
- = beval st (BEq (fold_constants_aexp a1)
- (fold_constants_aexp a2)),
- which, by the definition of [beval], is the same as showing
- beq_nat (aeval st a1) (aeval st a2)
- = beq_nat (aeval st (fold_constants_aexp a1))
- (aeval st (fold_constants_aexp a2)).
- But the soundness of constant folding for arithmetic
- expressions ([fold_constants_aexp_sound]) gives us
- aeval st a1 = aeval st (fold_constants_aexp a1)
- aeval st a2 = aeval st (fold_constants_aexp a2),
- completing the case. []
-*)
-
-Theorem fold_constants_bexp_sound:
- btrans_sound fold_constants_bexp.
-Proof.
- unfold btrans_sound. intros b. unfold bequiv. intros st.
- bexp_cases (induction b) Case;
- (* BTrue and BFalse are immediate *)
- try reflexivity.
- Case "BEq".
- (* Doing induction when there are a lot of constructors makes
- specifying variable names a chore, but Coq doesn't always
- choose nice variable names. We can rename entries in the
- context with the [rename] tactic: [rename a into a1] will
- change [a] to [a1] in the current goal and context. *)
- rename a into a1. rename a0 into a2. simpl.
- remember (fold_constants_aexp a1) as a1' eqn:Heqa1'.
- remember (fold_constants_aexp a2) as a2' eqn:Heqa2'.
- replace (aeval st a1) with (aeval st a1') by
- (subst a1'; rewrite <- fold_constants_aexp_sound; reflexivity).
- replace (aeval st a2) with (aeval st a2') by
- (subst a2'; rewrite <- fold_constants_aexp_sound; reflexivity).
- destruct a1'; destruct a2'; try reflexivity.
- (* The only interesting case is when both a1 and a2
- become constants after folding *)
- simpl. destruct (beq_nat n n0); reflexivity.
- Case "BLe".
- (* FILL IN HERE *) admit.
- Case "BNot".
- simpl. remember (fold_constants_bexp b) as b' eqn:Heqb'.
- rewrite IHb.
- destruct b'; reflexivity.
- Case "BAnd".
- simpl.
- remember (fold_constants_bexp b1) as b1' eqn:Heqb1'.
- remember (fold_constants_bexp b2) as b2' eqn:Heqb2'.
- rewrite IHb1. rewrite IHb2.
- destruct b1'; destruct b2'; reflexivity. Qed.
-(** [] *)
-
-(** **** Exercise: 3 stars (fold_constants_com_sound) *)
-(** Complete the [WHILE] case of the following proof. *)
-
-Theorem fold_constants_com_sound :
- ctrans_sound fold_constants_com.
-Proof.
- unfold ctrans_sound. intros c.
- com_cases (induction c) Case; simpl.
- Case "SKIP". apply refl_cequiv.
- Case "::=". apply CAss_congruence. apply fold_constants_aexp_sound.
- Case ";;". apply CSeq_congruence; assumption.
- Case "IFB".
- assert (bequiv b (fold_constants_bexp b)).
- SCase "Pf of assertion". apply fold_constants_bexp_sound.
- destruct (fold_constants_bexp b) eqn:Heqb;
- (* If the optimization doesn't eliminate the if, then the result
- is easy to prove from the IH and fold_constants_bexp_sound *)
- try (apply CIf_congruence; assumption).
- SCase "b always true".
- apply trans_cequiv with c1; try assumption.
- apply IFB_true; assumption.
- SCase "b always false".
- apply trans_cequiv with c2; try assumption.
- apply IFB_false; assumption.
- Case "WHILE".
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ########################################################## *)
-(** *** Soundness of (0 + n) Elimination, Redux *)
-
-(** **** Exercise: 4 stars, advanced, optional (optimize_0plus) *)
-(** Recall the definition [optimize_0plus] from Imp.v:
- Fixpoint optimize_0plus (e:aexp) : aexp :=
- match e with
- | ANum n =>
- ANum n
- | APlus (ANum 0) e2 =>
- optimize_0plus e2
- | APlus e1 e2 =>
- APlus (optimize_0plus e1) (optimize_0plus e2)
- | AMinus e1 e2 =>
- AMinus (optimize_0plus e1) (optimize_0plus e2)
- | AMult e1 e2 =>
- AMult (optimize_0plus e1) (optimize_0plus e2)
- end.
- Note that this function is defined over the old [aexp]s,
- without states.
-
- Write a new version of this function that accounts for variables,
- and analogous ones for [bexp]s and commands:
- optimize_0plus_aexp
- optimize_0plus_bexp
- optimize_0plus_com
- Prove that these three functions are sound, as we did for
- [fold_constants_*]. (Make sure you use the congruence lemmas in
- the proof of [optimize_0plus_com] -- otherwise it will be _long_!)
-
- Then define an optimizer on commands that first folds
- constants (using [fold_constants_com]) and then eliminates [0 + n]
- terms (using [optimize_0plus_com]).
-
- - Give a meaningful example of this optimizer's output.
-
- - Prove that the optimizer is sound. (This part should be _very_
- easy.) *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ####################################################### *)
-(** * Proving That Programs Are _Not_ Equivalent *)
-
-(** Suppose that [c1] is a command of the form [X ::= a1;; Y ::= a2]
- and [c2] is the command [X ::= a1;; Y ::= a2'], where [a2'] is
- formed by substituting [a1] for all occurrences of [X] in [a2].
- For example, [c1] and [c2] might be:
- c1 = (X ::= 42 + 53;;
- Y ::= Y + X)
- c2 = (X ::= 42 + 53;;
- Y ::= Y + (42 + 53))
- Clearly, this _particular_ [c1] and [c2] are equivalent. Is this
- true in general? *)
-
-(** We will see in a moment that it is not, but it is worthwhile
- to pause, now, and see if you can find a counter-example on your
- own. *)
-
-(** Here, formally, is the function that substitutes an arithmetic
- expression for each occurrence of a given variable in another
- expression: *)
-
-Fixpoint subst_aexp (i : id) (u : aexp) (a : aexp) : aexp :=
- match a with
- | ANum n => ANum n
- | AId i' => if eq_id_dec i i' then u else AId i'
- | APlus a1 a2 => APlus (subst_aexp i u a1) (subst_aexp i u a2)
- | AMinus a1 a2 => AMinus (subst_aexp i u a1) (subst_aexp i u a2)
- | AMult a1 a2 => AMult (subst_aexp i u a1) (subst_aexp i u a2)
- end.
-
-Example subst_aexp_ex :
- subst_aexp X (APlus (ANum 42) (ANum 53)) (APlus (AId Y) (AId X)) =
- (APlus (AId Y) (APlus (ANum 42) (ANum 53))).
-Proof. reflexivity. Qed.
-
-(** And here is the property we are interested in, expressing the
- claim that commands [c1] and [c2] as described above are
- always equivalent. *)
-
-Definition subst_equiv_property := forall i1 i2 a1 a2,
- cequiv (i1 ::= a1;; i2 ::= a2)
- (i1 ::= a1;; i2 ::= subst_aexp i1 a1 a2).
-
-(** ** *)
-(** Sadly, the property does _not_ always hold.
-
- _Theorem_: It is not the case that, for all [i1], [i2], [a1],
- and [a2],
- cequiv (i1 ::= a1;; i2 ::= a2)
- (i1 ::= a1;; i2 ::= subst_aexp i1 a1 a2).
-]]
- _Proof_: Suppose, for a contradiction, that for all [i1], [i2],
- [a1], and [a2], we have
- cequiv (i1 ::= a1;; i2 ::= a2)
- (i1 ::= a1;; i2 ::= subst_aexp i1 a1 a2).
- Consider the following program:
- X ::= APlus (AId X) (ANum 1);; Y ::= AId X
- Note that
- (X ::= APlus (AId X) (ANum 1);; Y ::= AId X)
- / empty_state || st1,
- where [st1 = { X |-> 1, Y |-> 1 }].
-
- By our assumption, we know that
- cequiv (X ::= APlus (AId X) (ANum 1);; Y ::= AId X)
- (X ::= APlus (AId X) (ANum 1);; Y ::= APlus (AId X) (ANum 1))
- so, by the definition of [cequiv], we have
- (X ::= APlus (AId X) (ANum 1);; Y ::= APlus (AId X) (ANum 1))
- / empty_state || st1.
- But we can also derive
- (X ::= APlus (AId X) (ANum 1);; Y ::= APlus (AId X) (ANum 1))
- / empty_state || st2,
- where [st2 = { X |-> 1, Y |-> 2 }]. Note that [st1 <> st2]; this
- is a contradiction, since [ceval] is deterministic! [] *)
-
-
-Theorem subst_inequiv :
- ~ subst_equiv_property.
-Proof.
- unfold subst_equiv_property.
- intros Contra.
-
- (* Here is the counterexample: assuming that [subst_equiv_property]
- holds allows us to prove that these two programs are
- equivalent... *)
- remember (X ::= APlus (AId X) (ANum 1);;
- Y ::= AId X)
- as c1.
- remember (X ::= APlus (AId X) (ANum 1);;
- Y ::= APlus (AId X) (ANum 1))
- as c2.
- assert (cequiv c1 c2) by (subst; apply Contra).
-
- (* ... allows us to show that the command [c2] can terminate
- in two different final states:
- st1 = {X |-> 1, Y |-> 1}
- st2 = {X |-> 1, Y |-> 2}. *)
- remember (update (update empty_state X 1) Y 1) as st1.
- remember (update (update empty_state X 1) Y 2) as st2.
- assert (H1: c1 / empty_state || st1);
- assert (H2: c2 / empty_state || st2);
- try (subst;
- apply E_Seq with (st' := (update empty_state X 1));
- apply E_Ass; reflexivity).
- apply H in H1.
-
- (* Finally, we use the fact that evaluation is deterministic
- to obtain a contradiction. *)
- assert (Hcontra: st1 = st2)
- by (apply (ceval_deterministic c2 empty_state); assumption).
- assert (Hcontra': st1 Y = st2 Y)
- by (rewrite Hcontra; reflexivity).
- subst. inversion Hcontra'. Qed.
-
-(** **** Exercise: 4 stars, optional (better_subst_equiv) *)
-(** The equivalence we had in mind above was not complete nonsense --
- it was actually almost right. To make it correct, we just need to
- exclude the case where the variable [X] occurs in the
- right-hand-side of the first assignment statement. *)
-
-Inductive var_not_used_in_aexp (X:id) : aexp -> Prop :=
- | VNUNum: forall n, var_not_used_in_aexp X (ANum n)
- | VNUId: forall Y, X <> Y -> var_not_used_in_aexp X (AId Y)
- | VNUPlus: forall a1 a2,
- var_not_used_in_aexp X a1 ->
- var_not_used_in_aexp X a2 ->
- var_not_used_in_aexp X (APlus a1 a2)
- | VNUMinus: forall a1 a2,
- var_not_used_in_aexp X a1 ->
- var_not_used_in_aexp X a2 ->
- var_not_used_in_aexp X (AMinus a1 a2)
- | VNUMult: forall a1 a2,
- var_not_used_in_aexp X a1 ->
- var_not_used_in_aexp X a2 ->
- var_not_used_in_aexp X (AMult a1 a2).
-
-Lemma aeval_weakening : forall i st a ni,
- var_not_used_in_aexp i a ->
- aeval (update st i ni) a = aeval st a.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Using [var_not_used_in_aexp], formalize and prove a correct verson
- of [subst_equiv_property]. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (inequiv_exercise) *)
-(** Prove that an infinite loop is not equivalent to [SKIP] *)
-
-Theorem inequiv_exercise:
- ~ cequiv (WHILE BTrue DO SKIP END) SKIP.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** * Extended exercise: Non-deterministic Imp *)
-
-(** As we have seen (in theorem [ceval_deterministic] in the Imp
- chapter), Imp's evaluation relation is deterministic. However,
- _non_-determinism is an important part of the definition of many
- real programming languages. For example, in many imperative
- languages (such as C and its relatives), the order in which
- function arguments are evaluated is unspecified. The program
- fragment
- x = 0;;
- f(++x, x)
- might call [f] with arguments [(1, 0)] or [(1, 1)], depending how
- the compiler chooses to order things. This can be a little
- confusing for programmers, but it gives the compiler writer useful
- freedom.
-
- In this exercise, we will extend Imp with a simple
- non-deterministic command and study how this change affects
- program equivalence. The new command has the syntax [HAVOC X],
- where [X] is an identifier. The effect of executing [HAVOC X] is
- to assign an _arbitrary_ number to the variable [X],
- non-deterministically. For example, after executing the program:
- HAVOC Y;;
- Z ::= Y * 2
- the value of [Y] can be any number, while the value of [Z] is
- twice that of [Y] (so [Z] is always even). Note that we are not
- saying anything about the _probabilities_ of the outcomes -- just
- that there are (infinitely) many different outcomes that can
- possibly happen after executing this non-deterministic code.
-
- In a sense a variable on which we do [HAVOC] roughly corresponds
- to an unitialized variable in the C programming language. After
- the [HAVOC] the variable holds a fixed but arbitrary number. Most
- sources of nondeterminism in language definitions are there
- precisely because programmers don't care which choice is made (and
- so it is good to leave it open to the compiler to choose whichever
- will run faster).
-
- We call this new language _Himp_ (``Imp extended with [HAVOC]''). *)
-
-Module Himp.
-
-(** To formalize the language, we first add a clause to the definition of
- commands. *)
-
-Inductive com : Type :=
- | CSkip : com
- | CAss : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com
- | CHavoc : id -> com. (* <---- new *)
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";;"
- | Case_aux c "IFB" | Case_aux c "WHILE" | Case_aux c "HAVOC" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "X '::=' a" :=
- (CAss X a) (at level 60).
-Notation "c1 ;; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' e1 'THEN' e2 'ELSE' e3 'FI'" :=
- (CIf e1 e2 e3) (at level 80, right associativity).
-Notation "'HAVOC' l" := (CHavoc l) (at level 60).
-
-(** **** Exercise: 2 stars (himp_ceval) *)
-(** Now, we must extend the operational semantics. We have provided
- a template for the [ceval] relation below, specifying the big-step
- semantics. What rule(s) must be added to the definition of [ceval]
- to formalize the behavior of the [HAVOC] command? *)
-
-Reserved Notation "c1 '/' st '||' st'" (at level 40, st at level 39).
-
-Inductive ceval : com -> state -> state -> Prop :=
- | E_Skip : forall st : state, SKIP / st || st
- | E_Ass : forall (st : state) (a1 : aexp) (n : nat) (X : id),
- aeval st a1 = n -> (X ::= a1) / st || update st X n
- | E_Seq : forall (c1 c2 : com) (st st' st'' : state),
- c1 / st || st' -> c2 / st' || st'' -> (c1 ;; c2) / st || st''
- | E_IfTrue : forall (st st' : state) (b1 : bexp) (c1 c2 : com),
- beval st b1 = true ->
- c1 / st || st' -> (IFB b1 THEN c1 ELSE c2 FI) / st || st'
- | E_IfFalse : forall (st st' : state) (b1 : bexp) (c1 c2 : com),
- beval st b1 = false ->
- c2 / st || st' -> (IFB b1 THEN c1 ELSE c2 FI) / st || st'
- | E_WhileEnd : forall (b1 : bexp) (st : state) (c1 : com),
- beval st b1 = false -> (WHILE b1 DO c1 END) / st || st
- | E_WhileLoop : forall (st st' st'' : state) (b1 : bexp) (c1 : com),
- beval st b1 = true ->
- c1 / st || st' ->
- (WHILE b1 DO c1 END) / st' || st'' ->
- (WHILE b1 DO c1 END) / st || st''
-(* FILL IN HERE *)
-
- where "c1 '/' st '||' st'" := (ceval c1 st st').
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip" | Case_aux c "E_Ass" | Case_aux c "E_Seq"
- | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
- | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop"
-(* FILL IN HERE *)
-].
-
-(** As a sanity check, the following claims should be provable for
- your definition: *)
-
-Example havoc_example1 : (HAVOC X) / empty_state || update empty_state X 0.
-Proof.
-(* FILL IN HERE *) Admitted.
-
-Example havoc_example2 :
- (SKIP;; HAVOC Z) / empty_state || update empty_state Z 42.
-Proof.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Finally, we repeat the definition of command equivalence from above: *)
-
-Definition cequiv (c1 c2 : com) : Prop := forall st st' : state,
- c1 / st || st' <-> c2 / st || st'.
-
-(** This definition still makes perfect sense in the case of always
- terminating programs, so let's apply it to prove some
- non-deterministic programs equivalent or non-equivalent. *)
-
-(** **** Exercise: 3 stars (havoc_swap) *)
-(** Are the following two programs equivalent? *)
-
-Definition pXY :=
- HAVOC X;; HAVOC Y.
-
-Definition pYX :=
- HAVOC Y;; HAVOC X.
-
-(** If you think they are equivalent, prove it. If you think they are
- not, prove that. *)
-
-
-Theorem pXY_cequiv_pYX :
- cequiv pXY pYX \/ ~cequiv pXY pYX.
-Proof. (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 4 stars, optional (havoc_copy) *)
-(** Are the following two programs equivalent? *)
-
-Definition ptwice :=
- HAVOC X;; HAVOC Y.
-
-Definition pcopy :=
- HAVOC X;; Y ::= AId X.
-
-(** If you think they are equivalent, then prove it. If you think they
- are not, then prove that. (Hint: You may find the [assert] tactic
- useful.) *)
-
-Theorem ptwice_cequiv_pcopy :
- cequiv ptwice pcopy \/ ~cequiv ptwice pcopy.
-Proof. (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** The definition of program equivalence we are using here has some
- subtle consequences on programs that may loop forever. What
- [cequiv] says is that the set of possible _terminating_ outcomes
- of two equivalent programs is the same. However, in a language
- with non-determinism, like Himp, some programs always terminate,
- some programs always diverge, and some programs can
- non-deterministically terminate in some runs and diverge in
- others. The final part of the following exercise illustrates this
- phenomenon.
-*)
-
-(** **** Exercise: 5 stars, advanced (p1_p2_equiv) *)
-(** Prove that p1 and p2 are equivalent. In this and the following
- exercises, try to understand why the [cequiv] definition has the
- behavior it has on these examples. *)
-
-Definition p1 : com :=
- WHILE (BNot (BEq (AId X) (ANum 0))) DO
- HAVOC Y;;
- X ::= APlus (AId X) (ANum 1)
- END.
-
-Definition p2 : com :=
- WHILE (BNot (BEq (AId X) (ANum 0))) DO
- SKIP
- END.
-
-
-(** Intuitively, the programs have the same termination
- behavior: either they loop forever, or they terminate in the
- same state they started in. We can capture the termination
- behavior of p1 and p2 individually with these lemmas: *)
-
-Lemma p1_may_diverge : forall st st', st X <> 0 ->
- ~ p1 / st || st'.
-Proof. (* FILL IN HERE *) Admitted.
-
-Lemma p2_may_diverge : forall st st', st X <> 0 ->
- ~ p2 / st || st'.
-Proof.
-(* FILL IN HERE *) Admitted.
-
-(** You should use these lemmas to prove that p1 and p2 are actually
- equivalent. *)
-
-Theorem p1_p2_equiv : cequiv p1 p2.
-Proof. (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 4 stars, advanced (p3_p4_inquiv) *)
-
-(** Prove that the following programs are _not_ equivalent. *)
-
-Definition p3 : com :=
- Z ::= ANum 1;;
- WHILE (BNot (BEq (AId X) (ANum 0))) DO
- HAVOC X;;
- HAVOC Z
- END.
-
-Definition p4 : com :=
- X ::= (ANum 0);;
- Z ::= (ANum 1).
-
-
-Theorem p3_p4_inequiv : ~ cequiv p3 p4.
-Proof. (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 5 stars, advanced, optional (p5_p6_equiv) *)
-
-Definition p5 : com :=
- WHILE (BNot (BEq (AId X) (ANum 1))) DO
- HAVOC X
- END.
-
-Definition p6 : com :=
- X ::= ANum 1.
-
-
-Theorem p5_p6_equiv : cequiv p5 p6.
-Proof. (* FILL IN HERE *) Admitted.
-(** [] *)
-
-End Himp.
-
-(* ####################################################### *)
-(** * Doing Without Extensionality (Optional) *)
-
-(** Purists might object to using the [functional_extensionality]
- axiom. In general, it can be quite dangerous to add axioms,
- particularly several at once (as they may be mutually
- inconsistent). In fact, [functional_extensionality] and
- [excluded_middle] can both be assumed without any problems, but
- some Coq users prefer to avoid such "heavyweight" general
- techniques, and instead craft solutions for specific problems that
- stay within Coq's standard logic.
-
- For our particular problem here, rather than extending the
- definition of equality to do what we want on functions
- representing states, we could instead give an explicit notion of
- _equivalence_ on states. For example: *)
-
-Definition stequiv (st1 st2 : state) : Prop :=
- forall (X:id), st1 X = st2 X.
-
-Notation "st1 '~' st2" := (stequiv st1 st2) (at level 30).
-
-(** It is easy to prove that [stequiv] is an _equivalence_ (i.e., it
- is reflexive, symmetric, and transitive), so it partitions the set
- of all states into equivalence classes. *)
-
-(** **** Exercise: 1 star, optional (stequiv_refl) *)
-Lemma stequiv_refl : forall (st : state),
- st ~ st.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (stequiv_sym) *)
-Lemma stequiv_sym : forall (st1 st2 : state),
- st1 ~ st2 ->
- st2 ~ st1.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (stequiv_trans) *)
-Lemma stequiv_trans : forall (st1 st2 st3 : state),
- st1 ~ st2 ->
- st2 ~ st3 ->
- st1 ~ st3.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Another useful fact... *)
-(** **** Exercise: 1 star, optional (stequiv_update) *)
-Lemma stequiv_update : forall (st1 st2 : state),
- st1 ~ st2 ->
- forall (X:id) (n:nat),
- update st1 X n ~ update st2 X n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** It is then straightforward to show that [aeval] and [beval] behave
- uniformly on all members of an equivalence class: *)
-
-(** **** Exercise: 2 stars, optional (stequiv_aeval) *)
-Lemma stequiv_aeval : forall (st1 st2 : state),
- st1 ~ st2 ->
- forall (a:aexp), aeval st1 a = aeval st2 a.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (stequiv_beval) *)
-Lemma stequiv_beval : forall (st1 st2 : state),
- st1 ~ st2 ->
- forall (b:bexp), beval st1 b = beval st2 b.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** We can also characterize the behavior of [ceval] on equivalent
- states (this result is a bit more complicated to write down
- because [ceval] is a relation). *)
-
-Lemma stequiv_ceval: forall (st1 st2 : state),
- st1 ~ st2 ->
- forall (c: com) (st1': state),
- (c / st1 || st1') ->
- exists st2' : state,
- ((c / st2 || st2') /\ st1' ~ st2').
-Proof.
- intros st1 st2 STEQV c st1' CEV1. generalize dependent st2.
- induction CEV1; intros st2 STEQV.
- Case "SKIP".
- exists st2. split.
- constructor.
- assumption.
- Case ":=".
- exists (update st2 x n). split.
- constructor. rewrite <- H. symmetry. apply stequiv_aeval.
- assumption. apply stequiv_update. assumption.
- Case ";".
- destruct (IHCEV1_1 st2 STEQV) as [st2' [P1 EQV1]].
- destruct (IHCEV1_2 st2' EQV1) as [st2'' [P2 EQV2]].
- exists st2''. split.
- apply E_Seq with st2'; assumption.
- assumption.
- Case "IfTrue".
- destruct (IHCEV1 st2 STEQV) as [st2' [P EQV]].
- exists st2'. split.
- apply E_IfTrue. rewrite <- H. symmetry. apply stequiv_beval.
- assumption. assumption. assumption.
- Case "IfFalse".
- destruct (IHCEV1 st2 STEQV) as [st2' [P EQV]].
- exists st2'. split.
- apply E_IfFalse. rewrite <- H. symmetry. apply stequiv_beval.
- assumption. assumption. assumption.
- Case "WhileEnd".
- exists st2. split.
- apply E_WhileEnd. rewrite <- H. symmetry. apply stequiv_beval.
- assumption. assumption.
- Case "WhileLoop".
- destruct (IHCEV1_1 st2 STEQV) as [st2' [P1 EQV1]].
- destruct (IHCEV1_2 st2' EQV1) as [st2'' [P2 EQV2]].
- exists st2''. split.
- apply E_WhileLoop with st2'. rewrite <- H. symmetry.
- apply stequiv_beval. assumption. assumption. assumption.
- assumption.
-Qed.
-
-(** Now we need to redefine [cequiv] to use [~] instead of [=]. It is
- not completely trivial to do this in a way that keeps the
- definition simple and symmetric, but here is one approach (thanks
- to Andrew McCreight). We first define a looser variant of [||]
- that "folds in" the notion of equivalence. *)
-
-Reserved Notation "c1 '/' st '||'' st'" (at level 40, st at level 39).
-
-Inductive ceval' : com -> state -> state -> Prop :=
- | E_equiv : forall c st st' st'',
- c / st || st' ->
- st' ~ st'' ->
- c / st ||' st''
- where "c1 '/' st '||'' st'" := (ceval' c1 st st').
-
-(** Now the revised definition of [cequiv'] looks familiar: *)
-
-Definition cequiv' (c1 c2 : com) : Prop :=
- forall (st st' : state),
- (c1 / st ||' st') <-> (c2 / st ||' st').
-
-(** A sanity check shows that the original notion of command
- equivalence is at least as strong as this new one. (The converse
- is not true, naturally.) *)
-
-Lemma cequiv__cequiv' : forall (c1 c2: com),
- cequiv c1 c2 -> cequiv' c1 c2.
-Proof.
- unfold cequiv, cequiv'; split; intros.
- inversion H0 ; subst. apply E_equiv with st'0.
- apply (H st st'0); assumption. assumption.
- inversion H0 ; subst. apply E_equiv with st'0.
- apply (H st st'0). assumption. assumption.
-Qed.
-
-(** **** Exercise: 2 stars, optional (identity_assignment') *)
-(** Finally, here is our example once more... (You can complete the
- proof.) *)
-
-Example identity_assignment' :
- cequiv' SKIP (X ::= AId X).
-Proof.
- unfold cequiv'. intros. split; intros.
- Case "->".
- inversion H; subst; clear H. inversion H0; subst.
- apply E_equiv with (update st'0 X (st'0 X)).
- constructor. reflexivity. apply stequiv_trans with st'0.
- unfold stequiv. intros. apply update_same.
- reflexivity. assumption.
- Case "<-".
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** On the whole, this explicit equivalence approach is considerably
- harder to work with than relying on functional
- extensionality. (Coq does have an advanced mechanism called
- "setoids" that makes working with equivalences somewhat easier, by
- allowing them to be registered with the system so that standard
- rewriting tactics work for them almost as well as for equalities.)
- But it is worth knowing about, because it applies even in
- situations where the equivalence in question is _not_ over
- functions. For example, if we chose to represent state mappings
- as binary search trees, we would need to use an explicit
- equivalence of this kind. *)
-
-(* ####################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 4 stars, optional (for_while_equiv) *)
-(** This exercise extends the optional [add_for_loop] exercise from
- Imp.v, where you were asked to extend the language of commands
- with C-style [for] loops. Prove that the command:
- for (c1 ; b ; c2) {
- c3
- }
- is equivalent to:
- c1 ;
- WHILE b DO
- c3 ;
- c2
- END
-*)
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (swap_noninterfering_assignments) *)
-Theorem swap_noninterfering_assignments: forall l1 l2 a1 a2,
- l1 <> l2 ->
- var_not_used_in_aexp l1 a2 ->
- var_not_used_in_aexp l2 a1 ->
- cequiv
- (l1 ::= a1;; l2 ::= a2)
- (l2 ::= a2;; l1 ::= a1).
-Proof.
-(* Hint: You'll need [functional_extensionality] *)
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
diff --git a/Extraction.html b/Extraction.html
deleted file mode 100644
index 5012ca6..0000000
--- a/Extraction.html
+++ /dev/null
@@ -1,287 +0,0 @@
-
-
-
-
-
-Extraction: Extracting ML from Coq
-
-
-
-
-
-
-
-
- In its simplest form, program extraction from Coq is completely straightforward.
-
-
- First we say what language we want to extract into. Options are OCaml (the
- most mature), Haskell (which mostly works), and Scheme (a bit out
- of date).
-
-
-
-
-ExtractionLanguageOcaml.
-
-
-
-
-
-Now we load up the Coq environment with some definitions, either
- directly or by importing them from other modules.
-
-Finally, we tell Coq the name of a definition to extract and the
- name of a file to put the extracted code into.
-
-
-
-
-Extraction "imp1.ml" ceval_step.
-
-
-
-
-
-When Coq processes this command, it generates a file imp1.ml
- containing an extracted version of ceval_step, together with
- everything that it recursively depends on. Have a look at this
- file now.
-
-
-
-
-
-
-
-
Controlling Extraction of Specific Types
-
-
-
- We can tell Coq to extract certain Inductive definitions to
- specific OCaml types. For each one, we must say
-
-
-
-
-
how the Coq type itself should be represented in OCaml, and
-
-
-Also, for non-enumeration types (where the constructors take
- arguments), we give an OCaml expression that can be used as a
- "recursor" over elements of the type. (Think Church numerals.)
-
-
-
-
-ExtractInductivenat ⇒ "int"
- [ "0" "(fun x → x + 1)" ]
- "(fun zero succ n →
- if n=0 then zero () else succ (n-1))".
-
-
-
-
-
-We can also extract defined constants to specific OCaml terms or
- operators.
-
-Important: It is entirely your responsibility to make sure that
- the translations you're proving make sense. For example, it might
- be tempting to include this one
-
-
-
-
- ExtractConstantminus ⇒ "( - )".
-
-
-
- but doing so could lead to serious confusion! (Why?)
-
-
-
-
-
-Extraction "imp2.ml" ceval_step.
-
-
-
-
-
-Have a look at the file imp2.ml. Notice how the fundamental
- definitions have changed from imp1.ml.
-
-
-
-
-
-
-
-
A Complete Example
-
-
-
- To use our extracted evaluator to run Imp programs, all we need to
- add is a tiny driver program that calls the evaluator and somehow
- prints out the result.
-
-
-
- For simplicity, we'll print results by dumping out the first four
- memory locations in the final state.
-
-
-
- Also, to make it easier to type in examples, let's extract a
- parser from the ImpParser Coq module. To do this, we need a few
- more declarations to set up the right correspondence between Coq
- strings and lists of OCaml characters.
-
-
-
-
-RequireImportAsciiString.
-ExtractInductiveascii ⇒ char
-[
-"(× If this appears, you're using Ascii internals. Please don't *) (fun (b0,b1,b2,b3,b4,b5,b6,b7) → let f b i = if b then 1 lsl i else 0 in Char.chr (f b0 0 + f b1 1 + f b2 2 + f b3 3 + f b4 4 + f b5 5 + f b6 6 + f b7 7))"
-]
-"(× If this appears, you're using Ascii internals. Please don't *) (fun f c → let n = Char.code c in let h i = (n land (1 lsl i)) ≠ 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))".
-ExtractConstantzero ⇒ "'\000'".
-ExtractConstantone ⇒ "'\001'".
-ExtractConstantshift ⇒
- "fun b c → Char.chr (((Char.code c) lsl 1) land 255 + if b then 1 else 0)".
-ExtractInlinedConstantascii_dec ⇒ "(=)".
-
-
-
- (The -w flags to ocamlc are just there to suppress a few
- spurious warnings.)
-
-
-
-
-
-
-
-
Discussion
-
-
-
- Since we've proved that the ceval_step function behaves the same
- as the ceval relation in an appropriate sense, the extracted
- program can be viewed as a certified Imp interpreter. (Of
- course, the parser is not certified in any interesting sense,
- since we didn't prove anything about it.)
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Extraction.v b/Extraction.v
deleted file mode 100644
index f0a8679..0000000
--- a/Extraction.v
+++ /dev/null
@@ -1,125 +0,0 @@
-(** * Extraction: Extracting ML from Coq *)
-
-(* $Date: 2013-01-16 22:29:57 -0500 (Wed, 16 Jan 2013) $ *)
-
-(** * Basic Extraction *)
-
-(** In its simplest form, program extraction from Coq is completely straightforward. *)
-
-(** First we say what language we want to extract into. Options are OCaml (the
- most mature), Haskell (which mostly works), and Scheme (a bit out
- of date). *)
-
-Extraction Language Ocaml.
-
-(** Now we load up the Coq environment with some definitions, either
- directly or by importing them from other modules. *)
-
-Require Import SfLib.
-Require Import ImpCEvalFun.
-
-(** Finally, we tell Coq the name of a definition to extract and the
- name of a file to put the extracted code into. *)
-
-Extraction "imp1.ml" ceval_step.
-
-(** When Coq processes this command, it generates a file [imp1.ml]
- containing an extracted version of [ceval_step], together with
- everything that it recursively depends on. Have a look at this
- file now. *)
-
-(* ############################################################## *)
-(** * Controlling Extraction of Specific Types *)
-
-(** We can tell Coq to extract certain [Inductive] definitions to
- specific OCaml types. For each one, we must say
- - how the Coq type itself should be represented in OCaml, and
- - how each constructor should be translated. *)
-
-Extract Inductive bool => "bool" [ "true" "false" ].
-
-(** Also, for non-enumeration types (where the constructors take
- arguments), we give an OCaml expression that can be used as a
- "recursor" over elements of the type. (Think Church numerals.) *)
-
-Extract Inductive nat => "int"
- [ "0" "(fun x -> x + 1)" ]
- "(fun zero succ n ->
- if n=0 then zero () else succ (n-1))".
-
-(** We can also extract defined constants to specific OCaml terms or
- operators. *)
-
-Extract Constant plus => "( + )".
-Extract Constant mult => "( * )".
-Extract Constant beq_nat => "( = )".
-
-(** Important: It is entirely _your responsibility_ to make sure that
- the translations you're proving make sense. For example, it might
- be tempting to include this one
- Extract Constant minus => "( - )".
- but doing so could lead to serious confusion! (Why?)
-*)
-
-Extraction "imp2.ml" ceval_step.
-
-(** Have a look at the file [imp2.ml]. Notice how the fundamental
- definitions have changed from [imp1.ml]. *)
-
-(* ############################################################## *)
-(** * A Complete Example *)
-
-(** To use our extracted evaluator to run Imp programs, all we need to
- add is a tiny driver program that calls the evaluator and somehow
- prints out the result.
-
- For simplicity, we'll print results by dumping out the first four
- memory locations in the final state.
-
- Also, to make it easier to type in examples, let's extract a
- parser from the [ImpParser] Coq module. To do this, we need a few
- more declarations to set up the right correspondence between Coq
- strings and lists of OCaml characters. *)
-
-Require Import Ascii String.
-Extract Inductive ascii => char
-[
-"(* If this appears, you're using Ascii internals. Please don't *) (fun (b0,b1,b2,b3,b4,b5,b6,b7) -> let f b i = if b then 1 lsl i else 0 in Char.chr (f b0 0 + f b1 1 + f b2 2 + f b3 3 + f b4 4 + f b5 5 + f b6 6 + f b7 7))"
-]
-"(* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))".
-Extract Constant zero => "'\000'".
-Extract Constant one => "'\001'".
-Extract Constant shift =>
- "fun b c -> Char.chr (((Char.code c) lsl 1) land 255 + if b then 1 else 0)".
-Extract Inlined Constant ascii_dec => "(=)".
-
-(** We also need one more variant of booleans. *)
-
-Extract Inductive sumbool => "bool" ["true" "false"].
-
-(** The extraction is the same as always. *)
-
-Require Import Imp.
-Require Import ImpParser.
-Extraction "imp.ml" empty_state ceval_step parse.
-
-(** Now let's run our generated Imp evaluator. First, have a look at
- [impdriver.ml]. (This was written by hand, not extracted.)
-
- Next, compile the driver together with the extracted code and
- execute it, as follows.
-<<
- ocamlc -w -20 -w -26 -o impdriver imp.mli imp.ml impdriver.ml
- ./impdriver
->>
- (The [-w] flags to [ocamlc] are just there to suppress a few
- spurious warnings.) *)
-
-(* ############################################################## *)
-(** * Discussion *)
-
-(** Since we've proved that the [ceval_step] function behaves the same
- as the [ceval] relation in an appropriate sense, the extracted
- program can be viewed as a _certified_ Imp interpreter. (Of
- course, the parser is not certified in any interesting sense,
- since we didn't prove anything about it.) *)
diff --git a/Hoare.html b/Hoare.html
deleted file mode 100644
index fa1dd00..0000000
--- a/Hoare.html
+++ /dev/null
@@ -1,2846 +0,0 @@
-
-
-
-
-
-Hoare: Hoare Logic, Part I
-
-
-
-
-
-
-
-
-
-
-
-
-
-
HoareHoare Logic, Part I
-
-
-
-
-
-
-
-
-
-
-RequireExportImp.
-
-
-
-
-
-In the past couple of chapters, we've begun applying the
- mathematical tools developed in the first part of the course to
- studying the theory of a small programming language, Imp.
-
-
-
-
-
We defined a type of abstract syntax trees for Imp, together
- with an evaluation relation (a partial function on states)
- that specifies the operational semantics of programs.
-
-
-
- The language we defined, though small, captures some of the key
- features of full-blown languages like C, C++, and Java,
- including the fundamental notion of mutable state and some
- common control structures.
-
-
-
-
-
-
We proved a number of metatheoretic properties — "meta" in
- the sense that they are properties of the language as a whole,
- rather than properties of particular programs in the language.
- These included:
-
-
-
-
-
determinism of evaluation
-
-
-
-
-
-
equivalence of some different ways of writing down the
- definitions (e.g. functional and relational definitions of
- arithmetic expression evaluation)
-
-
-
-
-
-
guaranteed termination of certain classes of programs
-
-
-
-
-
-
correctness (in the sense of preserving meaning) of a number
- of useful program transformations
-
-
-
-
-
-
behavioral equivalence of programs (in the Equiv chapter).
-
-
-
-
-
-
- If we stopped here, we would already have something useful: a set
- of tools for defining and discussing programming languages and
- language features that are mathematically precise, flexible, and
- easy to work with, applied to a set of key properties. All of
- these properties are things that language designers, compiler
- writers, and users might care about knowing. Indeed, many of them
- are so fundamental to our understanding of the programming
- languages we deal with that we might not consciously recognize
- them as "theorems." But properties that seem intuitively obvious
- can sometimes be quite subtle (in some cases, even subtly wrong!).
-
-
-
- We'll return to the theme of metatheoretic properties of whole
- languages later in the course when we discuss types and type
- soundness. In this chapter, though, we'll turn to a different
- set of issues.
-
-
-
- Our goal is to see how to carry out some simple examples of
- program verification — i.e., using the precise definition of
- Imp to prove formally that particular programs satisfy particular
- specifications of their behavior. We'll develop a reasoning system
- called Floyd-Hoare Logic — often shortened to just Hoare
- Logic — in which each of the syntactic constructs of Imp is
- equipped with a single, generic "proof rule" that can be used to
- reason compositionally about the correctness of programs involving
- this construct.
-
-
-
- Hoare Logic originates in the 1960s, and it continues to be the
- subject of intensive research right up to the present day. It
- lies at the core of a multitude of tools that are being used in
- academia and industry to specify and verify real software
- systems.
-
-
-
-
-
-
-
-
-
-
Hoare Logic
-
-
-
- Hoare Logic combines two beautiful ideas: a natural way of
- writing down specifications of programs, and a compositional
- proof technique for proving that programs are correct with
- respect to such specifications — where by "compositional" we mean
- that the structure of proofs directly mirrors the structure of the
- programs that they are about.
-
-
-
-
-
-
-
-
Assertions
-
-
-
- To talk about specifications of programs, the first thing we
- need is a way of making assertions about properties that hold at
- particular points during a program's execution — i.e., claims
- about the current state of the memory when program execution
- reaches that point. Formally, an assertion is just a family of
- propositions indexed by a state.
-
-
-
-
-DefinitionAssertion := state→Prop.
-
-
-
-
-
-
Exercise: 1 star, optional (assertions)
-
-
-
-
-
-
-
-
-Paraphrase the following assertions in English.
-
-
- This way of writing assertions can be a little bit heavy,
- for two reasons: (1) every single assertion that we ever write is
- going to begin with funst⇒ ; and (2) this state st is the
- only one that we ever use to look up variables (we will never need
- to talk about two different memory states at the same time). For
- discussing examples informally, we'll adopt some simplifying
- conventions: we'll drop the initial funst⇒, and we'll write
- just X to mean stX. Thus, instead of writing
-
-
-
-
-
-
- funst ⇒ (stZ) × (stZ) ≤ m∧
- ¬ ((S (stZ)) × (S (stZ)) ≤ m)
-
-
-
- we'll write just
-
-
-
-
- Z × Z ≤ m∧ ~((SZ) × (SZ) ≤ m).
-
-
-
-
-
-
- Given two assertions P and Q, we say that PimpliesQ,
- written P⇾Q (in ASCII, P->>Q), if, whenever P
- holds in some state st, Q also holds.
-
-
- Next, we need a way of making formal claims about the
- behavior of commands.
-
-
- Since the behavior of a command is to transform one state to
- another, it is natural to express claims about commands in terms
- of assertions that are true before and after the command executes:
-
-
-
-
-
"If command c is started in a state satisfying assertion
- P, and if c eventually terminates in some final state,
- then this final state will satisfy the assertion Q."
-
-
-
-
-
-
- Such a claim is called a Hoare Triple. The property P is
- called the precondition of c, while Q is the
- postcondition. Formally:
-
-(The hoare_spec_scope annotation here tells Coq that this
- notation is not global but is intended to be used in particular
- contexts. The OpenScope tells Coq that this file is one such
- context.)
-
-
-
Exercise: 1 star, optional (triples)
- Paraphrase the following Hoare triples in English.
-
-
-
- (Note that we're using informal mathematical notations for
- expressions inside of commands, for readability, rather than their
- formal aexp and bexp encodings. We'll continue doing so
- throughout the chapter.)
-
-
- To get us warmed up for what's coming, here are two simple
- facts about Hoare triples.
-
-
- The goal of Hoare logic is to provide a compositional
- method for proving the validity of Hoare triples. That is, the
- structure of a program's correctness proof should mirror the
- structure of the program itself. To this end, in the sections
- below, we'll introduce one rule for reasoning about each of the
- different syntactic forms of commands in Imp — one for
- assignment, one for sequencing, one for conditionals, etc. — plus
- a couple of "structural" rules that are useful for gluing things
- together. We will prove programs correct using these proof rules,
- without ever unfolding the definition of hoare_triple.
-
-
-
-
-
-
-
-
Assignment
-
-
-
- The rule for assignment is the most fundamental of the Hoare logic
- proof rules. Here's how it works.
-
-
-
- Consider this (valid) Hoare triple:
-
-
-
-
- {{ Y = 1 }} X ::= Y{{ X = 1 }}
-
-
-
- In English: if we start out in a state where the value of Y
- is 1 and we assign Y to X, then we'll finish in a
- state where X is 1. That is, the property of being equal
- to 1 gets transferred from Y to X.
-
-
-
- Similarly, in
-
-
-
-
- {{ Y + Z = 1 }} X ::= Y + Z{{ X = 1 }}
-
-
-
- the same property (being equal to one) gets transferred to
- X from the expression Y+Z on the right-hand side of
- the assignment.
-
-
-
- More generally, if a is any arithmetic expression, then
-
-
-
-
- {{ a = 1 }} X ::= a{{ X = 1 }}
-
-
-
- is a valid Hoare triple.
-
-
-
- This can be made even more general. To conclude that an
- arbitrary property Q holds after X::=a, we need to assume
- that Q holds before X::=a, but with all occurrences ofX
- replaced by a in Q. This leads to the Hoare rule for
- assignment
-
-
-
-
- {{ Q [X↦a] }} X ::= a{{ Q}}
-
-
-
- where "Q[X↦a]" is pronounced "Q where a is substituted
- for X".
-
-
-
- For example, these are valid applications of the assignment
- rule:
-
-
-
- To formalize the rule, we must first formalize the idea of
- "substituting an expression for an Imp variable in an assertion."
- That is, given a proposition P, a variable X, and an
- arithmetic expression a, we want to derive another proposition
- P' that is just the same as P except that, wherever P
- mentions X, P' should instead mention a.
-
-
-
- Since P is an arbitrary Coq proposition, we can't directly
- "edit" its text. Instead, we can achieve the effect we want by
- evaluating P in an updated state:
-
-
-
-
-Definitionassn_subXaP : Assertion :=
- fun (st : state) ⇒
- P (updatestX (aevalsta)).
-
-
-Notation "P [ X |-> a ]" := (assn_subXaP) (atlevel 10).
-
-
-
-
-
-That is, P[X↦a] is an assertion P' that is just like P
- except that, wherever P looks up the variable X in the current
- state, P' instead uses the value of the expression a.
-
-
-
- To see how this works, let's calculate what happens with a couple
- of examples. First, suppose P' is (X≤5)[X↦3] — that
- is, more formally, P' is the Coq expression
-
-
- ...into formal statements and use hoare_asgn to prove them.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 2 stars (hoare_asgn_wrong)
- The assignment rule looks backward to almost everyone the first
- time they see it. If it still seems backward to you, it may help
- to think a little about alternative "forward" rules. Here is a
- seemingly natural one:
-
-
-
-
- (hoare_asgn_wrong)
-
-
-
-
-
-
{{ True }} X ::= a {{ X = a }}
-
-
-
Give a counterexample showing that this rule is incorrect
- (informally). Hint: The rule universally quantifies over the
- arithmetic expression a, and your counterexample needs to
- exhibit an a for which the rule doesn't work.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (hoare_asgn_fwd)
- However, using an auxiliary variable m to remember the original
- value of X we can define a Hoare rule for assignment that does,
- intuitively, "work forwards" rather than backwards.
-
-
-
-
- (hoare_asgn_fwd)
-
-
-
-
-
-
{{fun st ⇒ P st ∧ st X = m}}
-
-
-
-
X ::= a
-
-
-
-
{{fun st ⇒ P st' ∧ st X = aeval st' a }}
-
-
-
-
(where st' = update st X m)
-
-
-
Note that we use the original value of X to reconstruct the
- state st' before the assignment took place. Prove that this rule
- is correct (the first hypothesis is the functional extensionality
- axiom, which you will need at some point). Also note that this
- rule is more complicated than hoare_asgn.
-
-
-
-
-
-Theoremhoare_asgn_fwd :
- (∀{XY: Type} {fg : X→Y},
- (∀(x: X), fx = gx) →f = g) →
- ∀maP,
- {{funst ⇒ Pst∧stX = m}}
- X ::= a
- {{funst ⇒ P (updatestXm) ∧stX = aeval (updatestXm) a}}.
-Proof.
- introsfunctional_extensionalitymaP.
- (* FILL IN HERE *)Admitted.
-
- Another way to define a forward rule for assignment is to
- existentially quantify over the previous value of the assigned
- variable.
-
-
-
-
- (hoare_asgn_fwd_exists)
-
-
-
-
-
-
{{fun st ⇒ P st}}
-
-
-
-
X ::= a
-
-
-
-
{{fun st ⇒ ∃m, P (update st X m) ∧
-
-
-
-
st X = aeval (update st X m) a }}
-
-
-
-
-
-(* This rule was proposed by Nick Giannarakis and Zoe Paraskevopoulou. *)
-
-
-Theoremhoare_asgn_fwd_exists :
- (∀{XY: Type} {fg : X→Y},
- (∀(x: X), fx = gx) →f = g) →
- ∀aP,
- {{funst ⇒ Pst}}
- X ::= a
- {{funst ⇒ ∃m, P (updatestXm) ∧
- stX = aeval (updatestXm) a}}.
-Proof.
- introsfunctional_extensionalityaP.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Consequence
-
-
-
- Sometimes the preconditions and postconditions we get from the
- Hoare rules won't quite be the ones we want in the particular
- situation at hand — they may be logically equivalent but have a
- different syntactic form that fails to unify with the goal we are
- trying to prove, or they actually may be logically weaker (for
- preconditions) or stronger (for postconditions) than what we need.
-
-
-
- For instance, while
-
-
-
-
- {{(X = 3) [X↦ 3]}} X ::= 3 {{X = 3}},
-
-
-
- follows directly from the assignment rule,
-
-
-
-
- {{True}} X ::= 3 {{X = 3}}.
-
-
-
- does not. This triple is valid, but it is not an instance of
- hoare_asgn because True and (X=3)[X↦3] are not
- syntactically equal assertions. However, they are logically
- equivalent, so if one triple is valid, then the other must
- certainly be as well. We might capture this observation with the
- following rule:
-
-
-
{{P'}} c {{Q}}
-
-
-
-
P ⇿ P'
-
- (hoare_consequence_pre_equiv)
-
-
-
-
-
-
{{P}} c {{Q}}
-
-
-
Taking this line of thought a bit further, we can see that
- strengthening the precondition or weakening the postcondition of a
- valid triple always produces another valid triple. This
- observation is captured by two Rules of Consequence.
-
-Finally, for convenience in some proofs, we can state a "combined"
- rule of consequence that allows us to vary both the precondition
- and the postcondition.
-
-
- This is a good moment to introduce another convenient feature of
- Coq. We had to write "with(P':=...)" explicitly in the proof
- of hoare_asgn_example1 and hoare_consequence above, to make
- sure that all of the metavariables in the premises to the
- hoare_consequence_pre rule would be set to specific
- values. (Since P' doesn't appear in the conclusion of
- hoare_consequence_pre, the process of unifying the conclusion
- with the current goal doesn't constrain P' to a specific
- assertion.)
-
-
-
- This is a little annoying, both because the assertion is a bit
- long and also because for hoare_asgn_example1 the very next
- thing we are going to do — applying the hoare_asgn rule — will
- tell us exactly what it should be! We can use eapply instead of
- apply to tell Coq, essentially, "Be patient: The missing part is
- going to be filled in soon."
-
-In general, eapplyH tactic works just like applyH except
- that, instead of failing if unifying the goal with the conclusion
- of H does not determine how to instantiate all of the variables
- appearing in the premises of H, eapplyH will replace these
- variables with so-called existential variables (written ?nnn)
- as placeholders for expressions that will be determined (by
- further unification) later in the proof.
-
-
- In order for Qed to succeed, all existential variables need to
- be determined by the end of the proof. Otherwise Coq
- will (rightly) refuse to accept the proof. Remember that the Coq
- tactics build proof objects, and proof objects containing
- existential variables are not complete.
-
- Trying to finish the proof with Qed gives an error:
-
- Error: Attempt to save a proof with existential variables still
- non-instantiated
-
-
-
-
-
-
-Abort.
-
-
-
-
-
-An additional constraint is that existential variables cannot be
- instantiated with terms containing (ordinary) variables that did
- not exist at the time the existential variable was created.
-
-In the last step we did applyHP' which unifies the existential
- variable in the goal with the variable y. The assumption
- tactic doesn't work in this case, since it cannot handle
- existential variables. However, Coq also provides an eassumption
- tactic that solves the goal if one of the premises matches the
- goal up to instantiations of existential variables. We can use
- it instead of applyHP'.
-
-
- More interestingly, if the command c1 takes any state where
- P holds to a state where Q holds, and if c2 takes any
- state where Q holds to one where R holds, then doing c1
- followed by c2 will take any state where P holds to one
- where R holds:
-
-Note that, in the formal rule hoare_seq, the premises are
- given in "backwards" order (c2 before c1). This matches the
- natural flow of information in many of the situations where we'll
- use the rule: the natural way to construct a Hoare-logic proof is
- to begin at the end of the program (with the final postcondition)
- and push postconditions backwards through commands until we reach
- the beginning.
-
-
- Informally, a nice way of recording a proof using the sequencing
- rule is as a "decorated program" where the intermediate assertion
- Q is written between c1 and c2:
-
-
-
-
- {{ a = n}}
- X ::= a;;
- {{ X = n}} <---- decorationforQ
- SKIP
- {{ X = n}}
-
-Proof.
- introsan. eapplyhoare_seq.
- Case "right part of seq".
- applyhoare_skip.
- Case "left part of seq".
- eapplyhoare_consequence_pre. applyhoare_asgn.
- introsstH. subst. reflexivity. Qed.
-
-
-
-
-
-
-You will most often use hoare_seq and
- hoare_consequence_pre in conjunction with the eapply tactic,
- as done above.
-
-
-
Exercise: 2 stars (hoare_asgn_example4)
- Translate this "decorated program" into a formal proof:
-
-
-
-
- {{ True}} ⇾
- {{ 1 = 1 }}
- X ::= 1;;
- {{ X = 1 }} ⇾
- {{ X = 1 ∧ 2 = 2 }}
- Y ::= 2
- {{ X = 1 ∧Y = 2 }}
-
-
-
-
-
-
-
-
-Examplehoare_asgn_example4 :
- {{funst ⇒ True}} (X ::= (ANum 1);; Y ::= (ANum 2))
- {{funst ⇒ stX = 1 ∧stY = 2}}.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (swap_exercise)
- Write an Imp program c that swaps the values of X and Y
- and show (in Coq) that it satisfies the following
- specification:
-
-
-
-
- {{X ≤ Y}} c{{Y ≤ X}}
-
-
-
-
-
-
-
-
-Definitionswap_program : com :=
- (* FILL IN HERE *)admit.
-
-
-Theoremswap_exercise :
- {{funst ⇒ stX ≤ stY}}
- swap_program
- {{funst ⇒ stY ≤ stX}}.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (hoarestate1)
- Explain why the following proposition can't be proven:
-
-
-
- What sort of rule do we want for reasoning about conditional
- commands? Certainly, if the same assertion Q holds after
- executing either branch, then it holds after the whole
- conditional. So we might be tempted to write:
-
-
-
{{P}} c1 {{Q}}
-
-
-
-
{{P}} c2 {{Q}}
-
-
-
-
-
-
-
-
{{P}} IFB b THEN c1 ELSE c2 {{Q}}
-
-
-
However, this is rather weak. For example, using this rule,
- we cannot show that:
-
-
-
-
- {{ True}}
- IFBX == 0
- THENY ::= 2
- ELSEY ::= X + 1
- FI
- {{ X ≤ Y}}
-
-
-
- since the rule tells us nothing about the state in which the
- assignments take place in the "then" and "else" branches.
-
-
- But we can actually say something more precise. In the
- "then" branch, we know that the boolean expression b evaluates to
- true, and in the "else" branch, we know it evaluates to false.
- Making this information available in the premises of the rule gives
- us more information to work with when reasoning about the behavior
- of c1 and c2 (i.e., the reasons why they establish the
- postcondition Q).
-
-
-
-
-
{{P ∧ b}} c1 {{Q}}
-
-
-
-
{{P ∧ ~b}} c2 {{Q}}
-
- (hoare_if)
-
-
-
-
-
-
{{P}} IFB b THEN c1 ELSE c2 FI {{Q}}
-
-
-
-
-
- To interpret this rule formally, we need to do a little work.
- Strictly speaking, the assertion we've written, P∧b, is the
- conjunction of an assertion and a boolean expression — i.e., it
- doesn't typecheck. To fix this, we need a way of formally
- "lifting" any bexp b to an assertion. We'll write bassnb for
- the assertion "the boolean expression b evaluates to true (in
- the given state)."
-
-Proof.
- introsPQbc1c2HTrueHFalsestst'HEHP.
- inversionHE; subst.
- Case "b is true".
- apply (HTruestst').
- assumption.
- split. assumption.
- applybexp_eval_true. assumption.
- Case "b is false".
- apply (HFalsestst').
- assumption.
- split. assumption.
- applybexp_eval_false. assumption. Qed.
-
-
-
-
-
-
-
-
-
Hoare Logic: So Far
-
-
-
-
-
-
-Idea: create a domain specific logic for reasoning about properties of Imp programs.
-
-
-
-
-
This hides the low-level details of the semantics of the program
-
-
-
Leads to a compositional reasoning process
-
-
-
-
-
-
-The basic structure is given by Hoare triples of the form:
-
-
-
-
- {{P}} c{{Q}}
-
-
-
-
-
-
-
-
P and Q are predicates about the state of the Imp program
-
-
-
"If command c is started in a state satisfying assertion
- P, and if c eventually terminates in some final state,
- then this final state will satisfy the assertion Q."
-
-
-
-
-
-
-
Hoare Logic Rules (so far)
-
-
-
-
-
-
-
-
-
-
- (hoare_asgn)
-
-
-
-
-
-
{{Q [X ↦ a]}} X::=a {{Q}}
-
-
-
-
-
-
- (hoare_skip)
-
-
-
-
-
-
{{ P }} SKIP {{ P }}
-
-
-
-
-
{{ P }} c1 {{ Q }}
-
-
-
-
{{ Q }} c2 {{ R }}
-
- (hoare_seq)
-
-
-
-
-
-
{{ P }} c1;;c2 {{ R }}
-
-
-
-
-
{{P ∧ b}} c1 {{Q}}
-
-
-
-
{{P ∧ ~b}} c2 {{Q}}
-
- (hoare_if)
-
-
-
-
-
-
{{P}} IFB b THEN c1 ELSE c2 FI {{Q}}
-
-
-
-
-
{{P'}} c {{Q'}}
-
-
-
-
P ⇾ P'
-
-
-
-
Q' ⇾ Q
-
- (hoare_consequence)
-
-
-
-
-
-
{{P}} c {{Q}}
-
-
-
-
-
-
Example
- Here is a formal proof that the program we used to motivate the
- rule satisfies the specification we gave.
-
-Proof.
- (* WORKED IN CLASS *)
- applyhoare_if.
- Case "Then".
- eapplyhoare_consequence_pre. applyhoare_asgn.
- unfoldbassn, assn_sub, update, assert_implies.
- simpl. introsst [_H].
- applybeq_nat_trueinH.
- rewriteH. omega.
- Case "Else".
- eapplyhoare_consequence_pre. applyhoare_asgn.
- unfoldassn_sub, update, assert_implies.
- simpl; introsst_. omega.
-Qed.
-
-
-
-
-
-
-
Exercise: 2 stars (if_minus_plus)
- Prove the following hoare triple using hoare_if:
-
-
-
-
-Theoremif_minus_plus :
- {{funst ⇒ True}}
- IFB (BLe (AIdX) (AIdY))
- THEN (Z ::= AMinus (AIdY) (AIdX))
- ELSE (Y ::= APlus (AIdX) (AIdZ))
- FI
- {{funst ⇒ stY = stX + stZ}}.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-
Exercise: One-sided conditionals
-
-
-
-
Exercise: 4 stars (if1_hoare)
-
-
-
- In this exercise we consider extending Imp with "one-sided
- conditionals" of the form IF1bTHENcFI. Here b is a
- boolean expression, and c is a command. If b evaluates to
- true, then command c is evaluated. If b evaluates to
- false, then IF1bTHENcFI does nothing.
-
-
-
- We recommend that you do this exercise before the ones that
- follow, as it should help solidify your understanding of the
- material.
-
-
- The first step is to extend the syntax of commands and introduce
- the usual notations. (We've done this for you. We use a separate
- module to prevent polluting the global name space.)
-
-Next we need to extend the evaluation relation to accommodate
- IF1 branches. This is for you to do... What rule(s) need to be
- added to ceval to evaluate one-sided conditionals?
-
-Finally, we (i.e., you) need to state and prove a theorem,
- hoare_if1, that expresses an appropriate Hoare logic proof rule
- for one-sided conditionals. Try to come up with a rule that is
- both sound and as precise as possible.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-For full credit, prove formally that your rule is precise enough
- to show the following valid Hoare triple:
-
-
-
-
- {{ X + Y = Z}}
- IF1Y ≠ 0 THEN
- X ::= X + Y
- FI
- {{ X = Z}}
-
-
-
-
-
-
- Hint: Your proof of this triple may need to use the other proof
- rules also. Because we're working in a separate module, you'll
- need to copy here the rules you find necessary.
-
-
-
-
-Lemmahoare_if1_good :
- {{ funst ⇒ stX + stY = stZ}}
- IF1BNot (BEq (AIdY) (ANum 0)) THEN
- X ::= APlus (AIdX) (AIdY)
- FI
- {{ funst ⇒ stX = stZ}}.
-Proof. (* FILL IN HERE *)Admitted.
-
-
-EndIf1.
-
-
-
-☐
-
-
-
-
-
-
-
-
Loops
-
-
-
- Finally, we need a rule for reasoning about while loops.
-
-
- Suppose we have a loop
-
-
-
-
- WHILEbDOcEND
-
-
-
- and we want to find a pre-condition P and a post-condition
- Q such that
-
-
-
-
- {{P}} WHILEbDOcEND{{Q}}
-
-
-
- is a valid triple.
-
-
-
-
-
-
- First of all, let's think about the case where b is false at the
- beginning — i.e., let's assume that the loop body never executes
- at all. In this case, the loop behaves like SKIP, so we might
- be tempted to write:
-
-
-
-
-
-
-
-
-
- {{P}} WHILEbDOcEND{{P}}.
-
-
-
-
-
-
-
-
-
- But, as we remarked above for the conditional, we know a
- little more at the end — not just P, but also the fact
- that b is false in the current state. So we can enrich the
- postcondition a little:
-
-
-
-
-
-
-
- {{P}} WHILEbDOcEND{{P∧ ¬b}}
-
-
-
-
-
-
-
-
-
- What about the case where the loop body does get executed?
- In order to ensure that P holds when the loop finally
- exits, we certainly need to make sure that the command c
- guarantees that P holds whenever c is finished.
- Moreover, since P holds at the beginning of the first
- execution of c, and since each execution of c
- re-establishes P when it finishes, we can always assume
- that P holds at the beginning of c. This leads us to the
- following rule:
-
-
-
-
-
-
{{P}} c {{P}}
-
-
-
-
-
-
-
-
{{P}} WHILE b DO c END {{P ∧ ~b}}
-
-
-
-
-
- This is almost the rule we want, but again it can be improved a
- little: at the beginning of the loop body, we know not only that
- P holds, but also that the guard b is true in the current
- state. This gives us a little more information to use in
- reasoning about c (showing that it establishes the invariant by
- the time it finishes). This gives us the final version of the rule:
-
-
-
-
-
-
{{P ∧ b}} c {{P}}
-
- (hoare_while)
-
-
-
-
-
-
{{P}} WHILE b DO c END {{P ∧ ~b}}
-
-
-
The proposition P is called an invariant of the loop.
-
-
-Proof.
- introsPbcHhoarestst'HeHP.
- (* Like we've seen before, we need to reason by induction
- on He, because, in the "keep looping" case, its hypotheses
- talk about the whole loop instead of just c. *)
- remember (WHILEbDOcEND) aswcomeqn:Heqwcom.
- ceval_cases (inductionHe) Case;
- try (inversionHeqwcom); subst; clearHeqwcom.
- Case "E_WhileEnd".
- split. assumption. applybexp_eval_false. assumption.
- Case "E_WhileLoop".
- applyIHHe2. reflexivity.
- apply (Hhoarestst'). assumption.
- split. assumption. applybexp_eval_true. assumption.
-Qed.
-
-
-
-
-
-
- One subtlety in the terminology is that calling some assertion P
- a "loop invariant" doesn't just mean that it is preserved by the
- body of the loop in question (i.e., {{P}}c{{P}}, where c is
- the loop body), but rather that Ptogether with the fact that
- the loop's guard is true is a sufficient precondition for c to
- ensure P as a postcondition.
-
-
-
- This is a slightly (but significantly) weaker requirement. For
- example, if P is the assertion X=0, then Pis an
- invariant of the loop
-
-
-
-
- WHILEX = 2 DOX := 1 END
-
-
-
- although it is clearly not preserved by the body of the
- loop.
-
-
-Proof.
- (* WORKED IN CLASS *)
- introsPQ.
- applyhoare_consequence_prewith (P' := funst : state ⇒ True).
- eapplyhoare_consequence_post.
- applyhoare_while.
- Case "Loop body preserves invariant".
- applyhoare_post_true. introsst. applyI.
- Case "Loop invariant and negated guard imply postcondition".
- simpl. introsst [HinvHguard].
- applyex_falso_quodlibet. applyHguard. reflexivity.
- Case "Precondition implies invariant".
- introsstH. constructor. Qed.
-
-
-
-
-
-
-Of course, this result is not surprising if we remember that
- the definition of hoare_triple asserts that the postcondition
- must hold only when the command terminates. If the command
- doesn't terminate, we can prove anything we like about the
- post-condition.
-
-
- Hoare rules that only talk about terminating commands are
- often said to describe a logic of "partial" correctness. It is
- also possible to give Hoare rules for "total" correctness, which
- build in the fact that the commands terminate. However, in this
- course we will only talk about partial correctness.
-
-
-
-
-
-
-
-
Exercise: REPEAT
-
-
-
-
-
-ModuleRepeatExercise.
-
-
-
-
-
-
Exercise: 4 stars, advanced (hoare_repeat)
- In this exercise, we'll add a new command to our language of
- commands: REPEAT c UNTIL a END. You will write the
- evaluation rule for repeat and add a new Hoare rule to
- the language for programs involving it.
-
-REPEAT behaves like WHILE, except that the loop guard is
- checked after each execution of the body, with the loop
- repeating as long as the guard stays false. Because of this,
- the body will always execute at least once.
-
-Add new rules for REPEAT to ceval below. You can use the rules
- for WHILE as a guide, but remember that the body of a REPEAT
- should always execute at least once, and that the loop ends when
- the guard becomes true. Then update the ceval_cases tactic to
- handle these added cases.
-
-To make sure you've got the evaluation rules for REPEAT right,
- prove that ex1_repeatevaluatescorrectly.
-
-
-
-
-Definitionex1_repeat :=
- REPEAT
- X ::= ANum 1;;
- Y ::= APlus (AIdY) (ANum 1)
- UNTIL (BEq (AIdX) (ANum 1)) END.
-
-
-Theoremex1_repeat_works :
- ex1_repeat / empty_state⇓
- update (updateempty_stateX 1) Y 1.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Now state and prove a theorem, hoare_repeat, that expresses an
- appropriate proof rule for repeat commands. Use hoare_while
- as a model, and try to make your rule as precise as possible.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-For full credit, make sure (informally) that your rule can be used
- to prove the following valid Hoare triple:
-
-
-
-
- {{ X > 0 }}
- REPEAT
- Y ::= X;;
- X ::= X - 1
- UNTILX = 0 END
- {{ X = 0 ∧Y > 0 }}
-
-
-
-
-
-
-
-
-EndRepeatExercise.
-
-
-
-☐
-
-
-
-
-
-
-
-
Exercise: HAVOC
-
-
-
-
Exercise: 3 stars (himp_hoare)
-
-
-
- In this exercise, we will derive proof rules for the HAVOC command
- which we studied in the last chapter. First, we enclose this work
- in a separate module, and recall the syntax and big-step semantics
- of Himp commands.
-
-The definition of Hoare triples is exactly as before. Unlike our
- notion of program equivalence, which had subtle consequences with
- occassionally nonterminating commands (exercise havoc_diverge),
- this definition is still fully satisfactory. Convince yourself of
- this before proceeding.
-
-Complete the Hoare rule for HAVOC commands below by defining
- havoc_pre and prove that the resulting rule is correct.
-
-
-
-
-Definitionhavoc_pre (X : id) (Q : Assertion) : Assertion :=
-(* FILL IN HERE *)admit.
-
-
-Theoremhoare_havoc : ∀(Q : Assertion) (X : id),
- {{ havoc_preXQ}} HAVOCX{{ Q}}.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-EndHimp.
-
-
-
-☐
-
-
-
-
-
-
-
-
Complete List of Hoare Logic Rules
-
-
-
- Above, we've introduced Hoare Logic as a tool to reasoning
- about Imp programs. In the reminder of this chapter we will
- explore a systematic way to use Hoare Logic to prove properties
- about programs. The rules of Hoare Logic are the following:
-
-
-
-
-
-
-
-
-
- (hoare_asgn)
-
-
-
-
-
-
{{Q [X ↦ a]}} X::=a {{Q}}
-
-
-
-
-
-
- (hoare_skip)
-
-
-
-
-
-
{{ P }} SKIP {{ P }}
-
-
-
-
-
{{ P }} c1 {{ Q }}
-
-
-
-
{{ Q }} c2 {{ R }}
-
- (hoare_seq)
-
-
-
-
-
-
{{ P }} c1;;c2 {{ R }}
-
-
-
-
-
{{P ∧ b}} c1 {{Q}}
-
-
-
-
{{P ∧ ~b}} c2 {{Q}}
-
- (hoare_if)
-
-
-
-
-
-
{{P}} IFB b THEN c1 ELSE c2 FI {{Q}}
-
-
-
-
-
{{P ∧ b}} c {{P}}
-
- (hoare_while)
-
-
-
-
-
-
{{P}} WHILE b DO c END {{P ∧ ~b}}
-
-
-
-
-
{{P'}} c {{Q'}}
-
-
-
-
P ⇾ P'
-
-
-
-
Q' ⇾ Q
-
- (hoare_consequence)
-
-
-
-
-
-
{{P}} c {{Q}}
-
-
-
In the next chapter, we'll see how these rules are used to prove
- that programs satisfy specifications of their behavior.
-
-
-
-
-
\ No newline at end of file
diff --git a/Hoare.v b/Hoare.v
deleted file mode 100644
index 853511c..0000000
--- a/Hoare.v
+++ /dev/null
@@ -1,1573 +0,0 @@
-(** * Hoare: Hoare Logic, Part I *)
-
-Require Export Imp.
-
-(** In the past couple of chapters, we've begun applying the
- mathematical tools developed in the first part of the course to
- studying the theory of a small programming language, Imp.
-
- - We defined a type of _abstract syntax trees_ for Imp, together
- with an _evaluation relation_ (a partial function on states)
- that specifies the _operational semantics_ of programs.
-
- The language we defined, though small, captures some of the key
- features of full-blown languages like C, C++, and Java,
- including the fundamental notion of mutable state and some
- common control structures.
-
- - We proved a number of _metatheoretic properties_ -- "meta" in
- the sense that they are properties of the language as a whole,
- rather than properties of particular programs in the language.
- These included:
-
- - determinism of evaluation
-
- - equivalence of some different ways of writing down the
- definitions (e.g. functional and relational definitions of
- arithmetic expression evaluation)
-
- - guaranteed termination of certain classes of programs
-
- - correctness (in the sense of preserving meaning) of a number
- of useful program transformations
-
- - behavioral equivalence of programs (in the [Equiv] chapter).
-
- If we stopped here, we would already have something useful: a set
- of tools for defining and discussing programming languages and
- language features that are mathematically precise, flexible, and
- easy to work with, applied to a set of key properties. All of
- these properties are things that language designers, compiler
- writers, and users might care about knowing. Indeed, many of them
- are so fundamental to our understanding of the programming
- languages we deal with that we might not consciously recognize
- them as "theorems." But properties that seem intuitively obvious
- can sometimes be quite subtle (in some cases, even subtly wrong!).
-
- We'll return to the theme of metatheoretic properties of whole
- languages later in the course when we discuss _types_ and _type
- soundness_. In this chapter, though, we'll turn to a different
- set of issues.
-
- Our goal is to see how to carry out some simple examples of
- _program verification_ -- i.e., using the precise definition of
- Imp to prove formally that particular programs satisfy particular
- specifications of their behavior. We'll develop a reasoning system
- called _Floyd-Hoare Logic_ -- often shortened to just _Hoare
- Logic_ -- in which each of the syntactic constructs of Imp is
- equipped with a single, generic "proof rule" that can be used to
- reason compositionally about the correctness of programs involving
- this construct.
-
- Hoare Logic originates in the 1960s, and it continues to be the
- subject of intensive research right up to the present day. It
- lies at the core of a multitude of tools that are being used in
- academia and industry to specify and verify real software
- systems. *)
-
-
-
-(* ####################################################### *)
-(** * Hoare Logic *)
-
-(** Hoare Logic combines two beautiful ideas: a natural way of
- writing down _specifications_ of programs, and a _compositional
- proof technique_ for proving that programs are correct with
- respect to such specifications -- where by "compositional" we mean
- that the structure of proofs directly mirrors the structure of the
- programs that they are about. *)
-
-(* ####################################################### *)
-(** ** Assertions *)
-
-(** To talk about specifications of programs, the first thing we
- need is a way of making _assertions_ about properties that hold at
- particular points during a program's execution -- i.e., claims
- about the current state of the memory when program execution
- reaches that point. Formally, an assertion is just a family of
- propositions indexed by a [state]. *)
-
-Definition Assertion := state -> Prop.
-
-(** **** Exercise: 1 star, optional (assertions) *)
-Module ExAssertions.
-
-(** Paraphrase the following assertions in English. *)
-
-Definition as1 : Assertion := fun st => st X = 3.
-Definition as2 : Assertion := fun st => st X <= st Y.
-Definition as3 : Assertion :=
- fun st => st X = 3 \/ st X <= st Y.
-Definition as4 : Assertion :=
- fun st => st Z * st Z <= st X /\
- ~ (((S (st Z)) * (S (st Z))) <= st X).
-Definition as5 : Assertion := fun st => True.
-Definition as6 : Assertion := fun st => False.
-
-(* FILL IN HERE *)
-
-End ExAssertions.
-(** [] *)
-
-(** This way of writing assertions can be a little bit heavy,
- for two reasons: (1) every single assertion that we ever write is
- going to begin with [fun st => ]; and (2) this state [st] is the
- only one that we ever use to look up variables (we will never need
- to talk about two different memory states at the same time). For
- discussing examples informally, we'll adopt some simplifying
- conventions: we'll drop the initial [fun st =>], and we'll write
- just [X] to mean [st X]. Thus, instead of writing *)
-(**
- fun st => (st Z) * (st Z) <= m /\
- ~ ((S (st Z)) * (S (st Z)) <= m)
- we'll write just
- Z * Z <= m /\ ~((S Z) * (S Z) <= m).
-*)
-
-(** Given two assertions [P] and [Q], we say that [P] _implies_ [Q],
- written [P ->> Q] (in ASCII, [P -][>][> Q]), if, whenever [P]
- holds in some state [st], [Q] also holds. *)
-
-Definition assert_implies (P Q : Assertion) : Prop :=
- forall st, P st -> Q st.
-
-Notation "P ->> Q" :=
- (assert_implies P Q) (at level 80) : hoare_spec_scope.
-Open Scope hoare_spec_scope.
-
-(** We'll also have occasion to use the "iff" variant of implication
- between assertions: *)
-
-Notation "P <<->> Q" :=
- (P ->> Q /\ Q ->> P) (at level 80) : hoare_spec_scope.
-
-(* ####################################################### *)
-(** ** Hoare Triples *)
-
-(** Next, we need a way of making formal claims about the
- behavior of commands. *)
-
-(** Since the behavior of a command is to transform one state to
- another, it is natural to express claims about commands in terms
- of assertions that are true before and after the command executes:
-
- - "If command [c] is started in a state satisfying assertion
- [P], and if [c] eventually terminates in some final state,
- then this final state will satisfy the assertion [Q]."
-
- Such a claim is called a _Hoare Triple_. The property [P] is
- called the _precondition_ of [c], while [Q] is the
- _postcondition_. Formally: *)
-
-Definition hoare_triple
- (P:Assertion) (c:com) (Q:Assertion) : Prop :=
- forall st st',
- c / st || st' ->
- P st ->
- Q st'.
-
-(** Since we'll be working a lot with Hoare triples, it's useful to
- have a compact notation:
- {{P}} c {{Q}}.
-*)
-(** (The traditional notation is [{P} c {Q}], but single braces
- are already used for other things in Coq.) *)
-
-Notation "{{ P }} c {{ Q }}" :=
- (hoare_triple P c Q) (at level 90, c at next level)
- : hoare_spec_scope.
-
-(** (The [hoare_spec_scope] annotation here tells Coq that this
- notation is not global but is intended to be used in particular
- contexts. The [Open Scope] tells Coq that this file is one such
- context.) *)
-
-(** **** Exercise: 1 star, optional (triples) *)
-(** Paraphrase the following Hoare triples in English.
- 1) {{True}} c {{X = 5}}
-
- 2) {{X = m}} c {{X = m + 5)}}
-
- 3) {{X <= Y}} c {{Y <= X}}
-
- 4) {{True}} c {{False}}
-
- 5) {{X = m}}
- c
- {{Y = real_fact m}}.
-
- 6) {{True}}
- c
- {{(Z * Z) <= m /\ ~ (((S Z) * (S Z)) <= m)}}
-
- *)
-
-
-(** [] *)
-
-
-
-
-
-
-
-
-
-(** **** Exercise: 1 star, optional (valid_triples) *)
-(** Which of the following Hoare triples are _valid_ -- i.e., the
- claimed relation between [P], [c], and [Q] is true?
- 1) {{True}} X ::= 5 {{X = 5}}
-
- 2) {{X = 2}} X ::= X + 1 {{X = 3}}
-
- 3) {{True}} X ::= 5; Y ::= 0 {{X = 5}}
-
- 4) {{X = 2 /\ X = 3}} X ::= 5 {{X = 0}}
-
- 5) {{True}} SKIP {{False}}
-
- 6) {{False}} SKIP {{True}}
-
- 7) {{True}} WHILE True DO SKIP END {{False}}
-
- 8) {{X = 0}}
- WHILE X == 0 DO X ::= X + 1 END
- {{X = 1}}
-
- 9) {{X = 1}}
- WHILE X <> 0 DO X ::= X + 1 END
- {{X = 100}}
-
-*)
-(* FILL IN HERE *)
-(** [] *)
-
-(** (Note that we're using informal mathematical notations for
- expressions inside of commands, for readability, rather than their
- formal [aexp] and [bexp] encodings. We'll continue doing so
- throughout the chapter.) *)
-
-(** To get us warmed up for what's coming, here are two simple
- facts about Hoare triples. *)
-
-Theorem hoare_post_true : forall (P Q : Assertion) c,
- (forall st, Q st) ->
- {{P}} c {{Q}}.
-Proof.
- intros P Q c H. unfold hoare_triple.
- intros st st' Heval HP.
- apply H. Qed.
-
-Theorem hoare_pre_false : forall (P Q : Assertion) c,
- (forall st, ~(P st)) ->
- {{P}} c {{Q}}.
-Proof.
- intros P Q c H. unfold hoare_triple.
- intros st st' Heval HP.
- unfold not in H. apply H in HP.
- inversion HP. Qed.
-
-(* ####################################################### *)
-(** ** Proof Rules *)
-
-(** The goal of Hoare logic is to provide a _compositional_
- method for proving the validity of Hoare triples. That is, the
- structure of a program's correctness proof should mirror the
- structure of the program itself. To this end, in the sections
- below, we'll introduce one rule for reasoning about each of the
- different syntactic forms of commands in Imp -- one for
- assignment, one for sequencing, one for conditionals, etc. -- plus
- a couple of "structural" rules that are useful for gluing things
- together. We will prove programs correct using these proof rules,
- without ever unfolding the definition of [hoare_triple]. *)
-
-(* ####################################################### *)
-(** *** Assignment *)
-
-(** The rule for assignment is the most fundamental of the Hoare logic
- proof rules. Here's how it works.
-
- Consider this (valid) Hoare triple:
- {{ Y = 1 }} X ::= Y {{ X = 1 }}
- In English: if we start out in a state where the value of [Y]
- is [1] and we assign [Y] to [X], then we'll finish in a
- state where [X] is [1]. That is, the property of being equal
- to [1] gets transferred from [Y] to [X].
-
- Similarly, in
- {{ Y + Z = 1 }} X ::= Y + Z {{ X = 1 }}
- the same property (being equal to one) gets transferred to
- [X] from the expression [Y + Z] on the right-hand side of
- the assignment.
-
- More generally, if [a] is _any_ arithmetic expression, then
- {{ a = 1 }} X ::= a {{ X = 1 }}
- is a valid Hoare triple.
-
- This can be made even more general. To conclude that an
- _arbitrary_ property [Q] holds after [X ::= a], we need to assume
- that [Q] holds before [X ::= a], but _with all occurrences of_ [X]
- replaced by [a] in [Q]. This leads to the Hoare rule for
- assignment
- {{ Q [X |-> a] }} X ::= a {{ Q }}
- where "[Q [X |-> a]]" is pronounced "[Q] where [a] is substituted
- for [X]".
-
- For example, these are valid applications of the assignment
- rule:
- {{ (X <= 5) [X |-> X + 1]
- i.e., X + 1 <= 5 }}
- X ::= X + 1
- {{ X <= 5 }}
-
- {{ (X = 3) [X |-> 3]
- i.e., 3 = 3}}
- X ::= 3
- {{ X = 3 }}
-
- {{ (0 <= X /\ X <= 5) [X |-> 3]
- i.e., (0 <= 3 /\ 3 <= 5)}}
- X ::= 3
- {{ 0 <= X /\ X <= 5 }}
-*)
-
-(** To formalize the rule, we must first formalize the idea of
- "substituting an expression for an Imp variable in an assertion."
- That is, given a proposition [P], a variable [X], and an
- arithmetic expression [a], we want to derive another proposition
- [P'] that is just the same as [P] except that, wherever [P]
- mentions [X], [P'] should instead mention [a].
-
- Since [P] is an arbitrary Coq proposition, we can't directly
- "edit" its text. Instead, we can achieve the effect we want by
- evaluating [P] in an updated state: *)
-
-Definition assn_sub X a P : Assertion :=
- fun (st : state) =>
- P (update st X (aeval st a)).
-
-Notation "P [ X |-> a ]" := (assn_sub X a P) (at level 10).
-
-(** That is, [P [X |-> a]] is an assertion [P'] that is just like [P]
- except that, wherever [P] looks up the variable [X] in the current
- state, [P'] instead uses the value of the expression [a].
-
- To see how this works, let's calculate what happens with a couple
- of examples. First, suppose [P'] is [(X <= 5) [X |-> 3]] -- that
- is, more formally, [P'] is the Coq expression
- fun st =>
- (fun st' => st' X <= 5)
- (update st X (aeval st (ANum 3))),
- which simplifies to
- fun st =>
- (fun st' => st' X <= 5)
- (update st X 3)
- and further simplifies to
- fun st =>
- ((update st X 3) X) <= 5)
- and by further simplification to
- fun st =>
- (3 <= 5).
- That is, [P'] is the assertion that [3] is less than or equal to
- [5] (as expected).
-
- For a more interesting example, suppose [P'] is [(X <= 5) [X |->
- X+1]]. Formally, [P'] is the Coq expression
- fun st =>
- (fun st' => st' X <= 5)
- (update st X (aeval st (APlus (AId X) (ANum 1)))),
- which simplifies to
- fun st =>
- (((update st X (aeval st (APlus (AId X) (ANum 1))))) X) <= 5
- and further simplifies to
- fun st =>
- (aeval st (APlus (AId X) (ANum 1))) <= 5.
- That is, [P'] is the assertion that [X+1] is at most [5].
-
-*)
-
-(** Now we can give the precise proof rule for assignment:
- ------------------------------ (hoare_asgn)
- {{Q [X |-> a]}} X ::= a {{Q}}
-*)
-
-(** We can prove formally that this rule is indeed valid. *)
-
-Theorem hoare_asgn : forall Q X a,
- {{Q [X |-> a]}} (X ::= a) {{Q}}.
-Proof.
- unfold hoare_triple.
- intros Q X a st st' HE HQ.
- inversion HE. subst.
- unfold assn_sub in HQ. assumption. Qed.
-
-(** Here's a first formal proof using this rule. *)
-
-Example assn_sub_example :
- {{(fun st => st X = 3) [X |-> ANum 3]}}
- (X ::= (ANum 3))
- {{fun st => st X = 3}}.
-Proof.
- apply hoare_asgn. Qed.
-
-(** **** Exercise: 2 stars (hoare_asgn_examples) *)
-(** Translate these informal Hoare triples...
- 1) {{ (X <= 5) [X |-> X + 1] }}
- X ::= X + 1
- {{ X <= 5 }}
-
- 2) {{ (0 <= X /\ X <= 5) [X |-> 3] }}
- X ::= 3
- {{ 0 <= X /\ X <= 5 }}
- ...into formal statements and use [hoare_asgn] to prove them. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 2 stars (hoare_asgn_wrong) *)
-(** The assignment rule looks backward to almost everyone the first
- time they see it. If it still seems backward to you, it may help
- to think a little about alternative "forward" rules. Here is a
- seemingly natural one:
- ------------------------------ (hoare_asgn_wrong)
- {{ True }} X ::= a {{ X = a }}
- Give a counterexample showing that this rule is incorrect
- (informally). Hint: The rule universally quantifies over the
- arithmetic expression [a], and your counterexample needs to
- exhibit an [a] for which the rule doesn't work. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (hoare_asgn_fwd) *)
-(** However, using an auxiliary variable [m] to remember the original
- value of [X] we can define a Hoare rule for assignment that does,
- intuitively, "work forwards" rather than backwards.
- ------------------------------------------ (hoare_asgn_fwd)
- {{fun st => P st /\ st X = m}}
- X ::= a
- {{fun st => P st' /\ st X = aeval st' a }}
- (where st' = update st X m)
- Note that we use the original value of [X] to reconstruct the
- state [st'] before the assignment took place. Prove that this rule
- is correct (the first hypothesis is the functional extensionality
- axiom, which you will need at some point). Also note that this
- rule is more complicated than [hoare_asgn].
-*)
-
-Theorem hoare_asgn_fwd :
- (forall {X Y: Type} {f g : X -> Y},
- (forall (x: X), f x = g x) -> f = g) ->
- forall m a P,
- {{fun st => P st /\ st X = m}}
- X ::= a
- {{fun st => P (update st X m) /\ st X = aeval (update st X m) a }}.
-Proof.
- intros functional_extensionality m a P.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, advanced (hoare_asgn_fwd_exists) *)
-(** Another way to define a forward rule for assignment is to
- existentially quantify over the previous value of the assigned
- variable.
- ------------------------------------------ (hoare_asgn_fwd_exists)
- {{fun st => P st}}
- X ::= a
- {{fun st => exists m, P (update st X m) /\
- st X = aeval (update st X m) a }}
-*)
-(* This rule was proposed by Nick Giannarakis and Zoe Paraskevopoulou. *)
-
-Theorem hoare_asgn_fwd_exists :
- (forall {X Y: Type} {f g : X -> Y},
- (forall (x: X), f x = g x) -> f = g) ->
- forall a P,
- {{fun st => P st}}
- X ::= a
- {{fun st => exists m, P (update st X m) /\
- st X = aeval (update st X m) a }}.
-Proof.
- intros functional_extensionality a P.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ####################################################### *)
-(** *** Consequence *)
-
-(** Sometimes the preconditions and postconditions we get from the
- Hoare rules won't quite be the ones we want in the particular
- situation at hand -- they may be logically equivalent but have a
- different syntactic form that fails to unify with the goal we are
- trying to prove, or they actually may be logically weaker (for
- preconditions) or stronger (for postconditions) than what we need.
-
- For instance, while
- {{(X = 3) [X |-> 3]}} X ::= 3 {{X = 3}},
- follows directly from the assignment rule,
- {{True}} X ::= 3 {{X = 3}}.
- does not. This triple is valid, but it is not an instance of
- [hoare_asgn] because [True] and [(X = 3) [X |-> 3]] are not
- syntactically equal assertions. However, they are logically
- equivalent, so if one triple is valid, then the other must
- certainly be as well. We might capture this observation with the
- following rule:
- {{P'}} c {{Q}}
- P <<->> P'
- ----------------------------- (hoare_consequence_pre_equiv)
- {{P}} c {{Q}}
- Taking this line of thought a bit further, we can see that
- strengthening the precondition or weakening the postcondition of a
- valid triple always produces another valid triple. This
- observation is captured by two _Rules of Consequence_.
- {{P'}} c {{Q}}
- P ->> P'
- ----------------------------- (hoare_consequence_pre)
- {{P}} c {{Q}}
-
- {{P}} c {{Q'}}
- Q' ->> Q
- ----------------------------- (hoare_consequence_post)
- {{P}} c {{Q}}
-*)
-
-(** Here are the formal versions: *)
-
-Theorem hoare_consequence_pre : forall (P P' Q : Assertion) c,
- {{P'}} c {{Q}} ->
- P ->> P' ->
- {{P}} c {{Q}}.
-Proof.
- intros P P' Q c Hhoare Himp.
- intros st st' Hc HP. apply (Hhoare st st').
- assumption. apply Himp. assumption. Qed.
-
-Theorem hoare_consequence_post : forall (P Q Q' : Assertion) c,
- {{P}} c {{Q'}} ->
- Q' ->> Q ->
- {{P}} c {{Q}}.
-Proof.
- intros P Q Q' c Hhoare Himp.
- intros st st' Hc HP.
- apply Himp.
- apply (Hhoare st st').
- assumption. assumption. Qed.
-
-(** For example, we might use the first consequence rule like this:
- {{ True }} ->>
- {{ 1 = 1 }}
- X ::= 1
- {{ X = 1 }}
- Or, formally...
-*)
-
-Example hoare_asgn_example1 :
- {{fun st => True}} (X ::= (ANum 1)) {{fun st => st X = 1}}.
-Proof.
- apply hoare_consequence_pre
- with (P' := (fun st => st X = 1) [X |-> ANum 1]).
- apply hoare_asgn.
- intros st H. unfold assn_sub, update. simpl. reflexivity.
-Qed.
-
-(** Finally, for convenience in some proofs, we can state a "combined"
- rule of consequence that allows us to vary both the precondition
- and the postcondition.
- {{P'}} c {{Q'}}
- P ->> P'
- Q' ->> Q
- ----------------------------- (hoare_consequence)
- {{P}} c {{Q}}
-*)
-
-Theorem hoare_consequence : forall (P P' Q Q' : Assertion) c,
- {{P'}} c {{Q'}} ->
- P ->> P' ->
- Q' ->> Q ->
- {{P}} c {{Q}}.
-Proof.
- intros P P' Q Q' c Hht HPP' HQ'Q.
- apply hoare_consequence_pre with (P' := P').
- apply hoare_consequence_post with (Q' := Q').
- assumption. assumption. assumption. Qed.
-
-(* ####################################################### *)
-(** *** Digression: The [eapply] Tactic *)
-
-(** This is a good moment to introduce another convenient feature of
- Coq. We had to write "[with (P' := ...)]" explicitly in the proof
- of [hoare_asgn_example1] and [hoare_consequence] above, to make
- sure that all of the metavariables in the premises to the
- [hoare_consequence_pre] rule would be set to specific
- values. (Since [P'] doesn't appear in the conclusion of
- [hoare_consequence_pre], the process of unifying the conclusion
- with the current goal doesn't constrain [P'] to a specific
- assertion.)
-
- This is a little annoying, both because the assertion is a bit
- long and also because for [hoare_asgn_example1] the very next
- thing we are going to do -- applying the [hoare_asgn] rule -- will
- tell us exactly what it should be! We can use [eapply] instead of
- [apply] to tell Coq, essentially, "Be patient: The missing part is
- going to be filled in soon." *)
-
-Example hoare_asgn_example1' :
- {{fun st => True}}
- (X ::= (ANum 1))
- {{fun st => st X = 1}}.
-Proof.
- eapply hoare_consequence_pre.
- apply hoare_asgn.
- intros st H. reflexivity. Qed.
-
-(** In general, [eapply H] tactic works just like [apply H] except
- that, instead of failing if unifying the goal with the conclusion
- of [H] does not determine how to instantiate all of the variables
- appearing in the premises of [H], [eapply H] will replace these
- variables with so-called _existential variables_ (written [?nnn])
- as placeholders for expressions that will be determined (by
- further unification) later in the proof. *)
-
-(** In order for [Qed] to succeed, all existential variables need to
- be determined by the end of the proof. Otherwise Coq
- will (rightly) refuse to accept the proof. Remember that the Coq
- tactics build proof objects, and proof objects containing
- existential variables are not complete. *)
-
-Lemma silly1 : forall (P : nat -> nat -> Prop) (Q : nat -> Prop),
- (forall x y : nat, P x y) ->
- (forall x y : nat, P x y -> Q x) ->
- Q 42.
-Proof.
- intros P Q HP HQ. eapply HQ. apply HP.
-
-(** Coq gives a warning after [apply HP]:
- No more subgoals but non-instantiated existential variables:
- Existential 1 =
- ?171 : [P : nat -> nat -> Prop
- Q : nat -> Prop
- HP : forall x y : nat, P x y
- HQ : forall x y : nat, P x y -> Q x |- nat]
-
- (dependent evars: ?171 open,)
-
- You can use Grab Existential Variables.
- Trying to finish the proof with [Qed] gives an error:
-<<
- Error: Attempt to save a proof with existential variables still
- non-instantiated
->> *)
-
-Abort.
-
-(** An additional constraint is that existential variables cannot be
- instantiated with terms containing (ordinary) variables that did
- not exist at the time the existential variable was created. *)
-
-Lemma silly2 :
- forall (P : nat -> nat -> Prop) (Q : nat -> Prop),
- (exists y, P 42 y) ->
- (forall x y : nat, P x y -> Q x) ->
- Q 42.
-Proof.
- intros P Q HP HQ. eapply HQ. destruct HP as [y HP'].
-(** Doing [apply HP'] above fails with the following error:
- Error: Impossible to unify "?175" with "y".
- In this case there is an easy fix:
- doing [destruct HP] _before_ doing [eapply HQ].
-*)
-
-Abort.
-
-Lemma silly2_fixed :
- forall (P : nat -> nat -> Prop) (Q : nat -> Prop),
- (exists y, P 42 y) ->
- (forall x y : nat, P x y -> Q x) ->
- Q 42.
-Proof.
- intros P Q HP HQ. destruct HP as [y HP'].
- eapply HQ. apply HP'.
-Qed.
-
-(** In the last step we did [apply HP'] which unifies the existential
- variable in the goal with the variable [y]. The [assumption]
- tactic doesn't work in this case, since it cannot handle
- existential variables. However, Coq also provides an [eassumption]
- tactic that solves the goal if one of the premises matches the
- goal up to instantiations of existential variables. We can use
- it instead of [apply HP']. *)
-
-Lemma silly2_eassumption : forall (P : nat -> nat -> Prop) (Q : nat -> Prop),
- (exists y, P 42 y) ->
- (forall x y : nat, P x y -> Q x) ->
- Q 42.
-Proof.
- intros P Q HP HQ. destruct HP as [y HP']. eapply HQ. eassumption.
-Qed.
-
-
-
-(** **** Exercise: 2 stars (hoare_asgn_examples_2) *)
-(** Translate these informal Hoare triples...
- {{ X + 1 <= 5 }} X ::= X + 1 {{ X <= 5 }}
- {{ 0 <= 3 /\ 3 <= 5 }} X ::= 3 {{ 0 <= X /\ X <= 5 }}
- ...into formal statements and use [hoare_asgn] and
- [hoare_consequence_pre] to prove them. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ####################################################### *)
-(** *** Skip *)
-
-(** Since [SKIP] doesn't change the state, it preserves any
- property P:
- -------------------- (hoare_skip)
- {{ P }} SKIP {{ P }}
-*)
-
-Theorem hoare_skip : forall P,
- {{P}} SKIP {{P}}.
-Proof.
- intros P st st' H HP. inversion H. subst.
- assumption. Qed.
-
-(* ####################################################### *)
-(** *** Sequencing *)
-
-(** More interestingly, if the command [c1] takes any state where
- [P] holds to a state where [Q] holds, and if [c2] takes any
- state where [Q] holds to one where [R] holds, then doing [c1]
- followed by [c2] will take any state where [P] holds to one
- where [R] holds:
- {{ P }} c1 {{ Q }}
- {{ Q }} c2 {{ R }}
- --------------------- (hoare_seq)
- {{ P }} c1;;c2 {{ R }}
-*)
-
-Theorem hoare_seq : forall P Q R c1 c2,
- {{Q}} c2 {{R}} ->
- {{P}} c1 {{Q}} ->
- {{P}} c1;;c2 {{R}}.
-Proof.
- intros P Q R c1 c2 H1 H2 st st' H12 Pre.
- inversion H12; subst.
- apply (H1 st'0 st'); try assumption.
- apply (H2 st st'0); assumption. Qed.
-
-(** Note that, in the formal rule [hoare_seq], the premises are
- given in "backwards" order ([c2] before [c1]). This matches the
- natural flow of information in many of the situations where we'll
- use the rule: the natural way to construct a Hoare-logic proof is
- to begin at the end of the program (with the final postcondition)
- and push postconditions backwards through commands until we reach
- the beginning. *)
-
-(** Informally, a nice way of recording a proof using the sequencing
- rule is as a "decorated program" where the intermediate assertion
- [Q] is written between [c1] and [c2]:
- {{ a = n }}
- X ::= a;;
- {{ X = n }} <---- decoration for Q
- SKIP
- {{ X = n }}
-*)
-
-Example hoare_asgn_example3 : forall a n,
- {{fun st => aeval st a = n}}
- (X ::= a;; SKIP)
- {{fun st => st X = n}}.
-Proof.
- intros a n. eapply hoare_seq.
- Case "right part of seq".
- apply hoare_skip.
- Case "left part of seq".
- eapply hoare_consequence_pre. apply hoare_asgn.
- intros st H. subst. reflexivity. Qed.
-
-(** You will most often use [hoare_seq] and
- [hoare_consequence_pre] in conjunction with the [eapply] tactic,
- as done above. *)
-
-(** **** Exercise: 2 stars (hoare_asgn_example4) *)
-(** Translate this "decorated program" into a formal proof:
- {{ True }} ->>
- {{ 1 = 1 }}
- X ::= 1;;
- {{ X = 1 }} ->>
- {{ X = 1 /\ 2 = 2 }}
- Y ::= 2
- {{ X = 1 /\ Y = 2 }}
-*)
-
-Example hoare_asgn_example4 :
- {{fun st => True}} (X ::= (ANum 1);; Y ::= (ANum 2))
- {{fun st => st X = 1 /\ st Y = 2}}.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (swap_exercise) *)
-(** Write an Imp program [c] that swaps the values of [X] and [Y]
- and show (in Coq) that it satisfies the following
- specification:
- {{X <= Y}} c {{Y <= X}}
-*)
-
-Definition swap_program : com :=
- (* FILL IN HERE *) admit.
-
-Theorem swap_exercise :
- {{fun st => st X <= st Y}}
- swap_program
- {{fun st => st Y <= st X}}.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (hoarestate1) *)
-(** Explain why the following proposition can't be proven:
- forall (a : aexp) (n : nat),
- {{fun st => aeval st a = n}}
- (X ::= (ANum 3);; Y ::= a)
- {{fun st => st Y = n}}.
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ####################################################### *)
-(** *** Conditionals *)
-
-(** What sort of rule do we want for reasoning about conditional
- commands? Certainly, if the same assertion [Q] holds after
- executing either branch, then it holds after the whole
- conditional. So we might be tempted to write:
- {{P}} c1 {{Q}}
- {{P}} c2 {{Q}}
- --------------------------------
- {{P}} IFB b THEN c1 ELSE c2 {{Q}}
- However, this is rather weak. For example, using this rule,
- we cannot show that:
- {{ True }}
- IFB X == 0
- THEN Y ::= 2
- ELSE Y ::= X + 1
- FI
- {{ X <= Y }}
- since the rule tells us nothing about the state in which the
- assignments take place in the "then" and "else" branches. *)
-
-(** But we can actually say something more precise. In the
- "then" branch, we know that the boolean expression [b] evaluates to
- [true], and in the "else" branch, we know it evaluates to [false].
- Making this information available in the premises of the rule gives
- us more information to work with when reasoning about the behavior
- of [c1] and [c2] (i.e., the reasons why they establish the
- postcondition [Q]). *)
-(**
- {{P /\ b}} c1 {{Q}}
- {{P /\ ~b}} c2 {{Q}}
- ------------------------------------ (hoare_if)
- {{P}} IFB b THEN c1 ELSE c2 FI {{Q}}
-*)
-
-(** To interpret this rule formally, we need to do a little work.
- Strictly speaking, the assertion we've written, [P /\ b], is the
- conjunction of an assertion and a boolean expression -- i.e., it
- doesn't typecheck. To fix this, we need a way of formally
- "lifting" any bexp [b] to an assertion. We'll write [bassn b] for
- the assertion "the boolean expression [b] evaluates to [true] (in
- the given state)." *)
-
-Definition bassn b : Assertion :=
- fun st => (beval st b = true).
-
-(** A couple of useful facts about [bassn]: *)
-
-Lemma bexp_eval_true : forall b st,
- beval st b = true -> (bassn b) st.
-Proof.
- intros b st Hbe.
- unfold bassn. assumption. Qed.
-
-Lemma bexp_eval_false : forall b st,
- beval st b = false -> ~ ((bassn b) st).
-Proof.
- intros b st Hbe contra.
- unfold bassn in contra.
- rewrite -> contra in Hbe. inversion Hbe. Qed.
-
-(** Now we can formalize the Hoare proof rule for conditionals
- and prove it correct. *)
-
-Theorem hoare_if : forall P Q b c1 c2,
- {{fun st => P st /\ bassn b st}} c1 {{Q}} ->
- {{fun st => P st /\ ~(bassn b st)}} c2 {{Q}} ->
- {{P}} (IFB b THEN c1 ELSE c2 FI) {{Q}}.
-Proof.
- intros P Q b c1 c2 HTrue HFalse st st' HE HP.
- inversion HE; subst.
- Case "b is true".
- apply (HTrue st st').
- assumption.
- split. assumption.
- apply bexp_eval_true. assumption.
- Case "b is false".
- apply (HFalse st st').
- assumption.
- split. assumption.
- apply bexp_eval_false. assumption. Qed.
-
-
-(* ####################################################### *)
-
-(** * Hoare Logic: So Far *)
-
-(**
-Idea: create a _domain specific logic_ for reasoning about properties of Imp programs.
-
-- This hides the low-level details of the semantics of the program
-- Leads to a compositional reasoning process
-
-
-The basic structure is given by _Hoare triples_ of the form:
- {{P}} c {{Q}}
-]]
-
-- [P] and [Q] are predicates about the state of the Imp program
-- "If command [c] is started in a state satisfying assertion
- [P], and if [c] eventually terminates in some final state,
- then this final state will satisfy the assertion [Q]."
-
-*)
-
-
-(** ** Hoare Logic Rules (so far) *)
-
-(**
- ------------------------------ (hoare_asgn)
- {{Q [X |-> a]}} X::=a {{Q}}
-
- -------------------- (hoare_skip)
- {{ P }} SKIP {{ P }}
-
- {{ P }} c1 {{ Q }}
- {{ Q }} c2 {{ R }}
- --------------------- (hoare_seq)
- {{ P }} c1;;c2 {{ R }}
-
- {{P /\ b}} c1 {{Q}}
- {{P /\ ~b}} c2 {{Q}}
- ------------------------------------ (hoare_if)
- {{P}} IFB b THEN c1 ELSE c2 FI {{Q}}
-
-
- {{P'}} c {{Q'}}
- P ->> P'
- Q' ->> Q
- ----------------------------- (hoare_consequence)
- {{P}} c {{Q}}
-*)
-
-
-(** *** Example *)
-(** Here is a formal proof that the program we used to motivate the
- rule satisfies the specification we gave. *)
-
-Example if_example :
- {{fun st => True}}
- IFB (BEq (AId X) (ANum 0))
- THEN (Y ::= (ANum 2))
- ELSE (Y ::= APlus (AId X) (ANum 1))
- FI
- {{fun st => st X <= st Y}}.
-Proof.
- (* WORKED IN CLASS *)
- apply hoare_if.
- Case "Then".
- eapply hoare_consequence_pre. apply hoare_asgn.
- unfold bassn, assn_sub, update, assert_implies.
- simpl. intros st [_ H].
- apply beq_nat_true in H.
- rewrite H. omega.
- Case "Else".
- eapply hoare_consequence_pre. apply hoare_asgn.
- unfold assn_sub, update, assert_implies.
- simpl; intros st _. omega.
-Qed.
-
-(** **** Exercise: 2 stars (if_minus_plus) *)
-(** Prove the following hoare triple using [hoare_if]: *)
-
-Theorem if_minus_plus :
- {{fun st => True}}
- IFB (BLe (AId X) (AId Y))
- THEN (Z ::= AMinus (AId Y) (AId X))
- ELSE (Y ::= APlus (AId X) (AId Z))
- FI
- {{fun st => st Y = st X + st Z}}.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(* ####################################################### *)
-(** *** Exercise: One-sided conditionals *)
-
-(** **** Exercise: 4 stars (if1_hoare) *)
-
-(** In this exercise we consider extending Imp with "one-sided
- conditionals" of the form [IF1 b THEN c FI]. Here [b] is a
- boolean expression, and [c] is a command. If [b] evaluates to
- [true], then command [c] is evaluated. If [b] evaluates to
- [false], then [IF1 b THEN c FI] does nothing.
-
- We recommend that you do this exercise before the ones that
- follow, as it should help solidify your understanding of the
- material. *)
-
-
-(** The first step is to extend the syntax of commands and introduce
- the usual notations. (We've done this for you. We use a separate
- module to prevent polluting the global name space.) *)
-
-Module If1.
-
-Inductive com : Type :=
- | CSkip : com
- | CAss : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com
- | CIf1 : bexp -> com -> com.
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";"
- | Case_aux c "IFB" | Case_aux c "WHILE" | Case_aux c "CIF1" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "c1 ;; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "X '::=' a" :=
- (CAss X a) (at level 60).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' e1 'THEN' e2 'ELSE' e3 'FI'" :=
- (CIf e1 e2 e3) (at level 80, right associativity).
-Notation "'IF1' b 'THEN' c 'FI'" :=
- (CIf1 b c) (at level 80, right associativity).
-
-(** Next we need to extend the evaluation relation to accommodate
- [IF1] branches. This is for you to do... What rule(s) need to be
- added to [ceval] to evaluate one-sided conditionals? *)
-
-Reserved Notation "c1 '/' st '||' st'" (at level 40, st at level 39).
-
-Inductive ceval : com -> state -> state -> Prop :=
- | E_Skip : forall st : state, SKIP / st || st
- | E_Ass : forall (st : state) (a1 : aexp) (n : nat) (X : id),
- aeval st a1 = n -> (X ::= a1) / st || update st X n
- | E_Seq : forall (c1 c2 : com) (st st' st'' : state),
- c1 / st || st' -> c2 / st' || st'' -> (c1 ;; c2) / st || st''
- | E_IfTrue : forall (st st' : state) (b1 : bexp) (c1 c2 : com),
- beval st b1 = true ->
- c1 / st || st' -> (IFB b1 THEN c1 ELSE c2 FI) / st || st'
- | E_IfFalse : forall (st st' : state) (b1 : bexp) (c1 c2 : com),
- beval st b1 = false ->
- c2 / st || st' -> (IFB b1 THEN c1 ELSE c2 FI) / st || st'
- | E_WhileEnd : forall (b1 : bexp) (st : state) (c1 : com),
- beval st b1 = false -> (WHILE b1 DO c1 END) / st || st
- | E_WhileLoop : forall (st st' st'' : state) (b1 : bexp) (c1 : com),
- beval st b1 = true ->
- c1 / st || st' ->
- (WHILE b1 DO c1 END) / st' || st'' ->
- (WHILE b1 DO c1 END) / st || st''
-(* FILL IN HERE *)
-
- where "c1 '/' st '||' st'" := (ceval c1 st st').
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip" | Case_aux c "E_Ass" | Case_aux c "E_Seq"
- | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
- | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop"
- (* FILL IN HERE *)
- ].
-
-(** Now we repeat (verbatim) the definition and notation of Hoare triples. *)
-
-Definition hoare_triple (P:Assertion) (c:com) (Q:Assertion) : Prop :=
- forall st st',
- c / st || st' ->
- P st ->
- Q st'.
-
-Notation "{{ P }} c {{ Q }}" := (hoare_triple P c Q)
- (at level 90, c at next level)
- : hoare_spec_scope.
-
-(** Finally, we (i.e., you) need to state and prove a theorem,
- [hoare_if1], that expresses an appropriate Hoare logic proof rule
- for one-sided conditionals. Try to come up with a rule that is
- both sound and as precise as possible. *)
-
-(* FILL IN HERE *)
-
-(** For full credit, prove formally that your rule is precise enough
- to show the following valid Hoare triple:
- {{ X + Y = Z }}
- IF1 Y <> 0 THEN
- X ::= X + Y
- FI
- {{ X = Z }}
-*)
-
-(** Hint: Your proof of this triple may need to use the other proof
- rules also. Because we're working in a separate module, you'll
- need to copy here the rules you find necessary. *)
-
-
-Lemma hoare_if1_good :
- {{ fun st => st X + st Y = st Z }}
- IF1 BNot (BEq (AId Y) (ANum 0)) THEN
- X ::= APlus (AId X) (AId Y)
- FI
- {{ fun st => st X = st Z }}.
-Proof. (* FILL IN HERE *) Admitted.
-
-End If1.
-(** [] *)
-
-(* ####################################################### *)
-(** *** Loops *)
-
-(** Finally, we need a rule for reasoning about while loops. *)
-
-(** Suppose we have a loop
- WHILE b DO c END
- and we want to find a pre-condition [P] and a post-condition
- [Q] such that
- {{P}} WHILE b DO c END {{Q}}
- is a valid triple. *)
-
-(** *** *)
-
-(** First of all, let's think about the case where [b] is false at the
- beginning -- i.e., let's assume that the loop body never executes
- at all. In this case, the loop behaves like [SKIP], so we might
- be tempted to write: *)
-
-(**
- {{P}} WHILE b DO c END {{P}}.
-*)
-
-(**
- But, as we remarked above for the conditional, we know a
- little more at the end -- not just [P], but also the fact
- that [b] is false in the current state. So we can enrich the
- postcondition a little:
-*)
-(**
- {{P}} WHILE b DO c END {{P /\ ~b}}
-*)
-
-(**
- What about the case where the loop body _does_ get executed?
- In order to ensure that [P] holds when the loop finally
- exits, we certainly need to make sure that the command [c]
- guarantees that [P] holds whenever [c] is finished.
- Moreover, since [P] holds at the beginning of the first
- execution of [c], and since each execution of [c]
- re-establishes [P] when it finishes, we can always assume
- that [P] holds at the beginning of [c]. This leads us to the
- following rule:
-*)
-(**
- {{P}} c {{P}}
- -----------------------------------
- {{P}} WHILE b DO c END {{P /\ ~b}}
-*)
-(**
- This is almost the rule we want, but again it can be improved a
- little: at the beginning of the loop body, we know not only that
- [P] holds, but also that the guard [b] is true in the current
- state. This gives us a little more information to use in
- reasoning about [c] (showing that it establishes the invariant by
- the time it finishes). This gives us the final version of the rule:
-*)
-(**
- {{P /\ b}} c {{P}}
- ----------------------------------- (hoare_while)
- {{P}} WHILE b DO c END {{P /\ ~b}}
- The proposition [P] is called an _invariant_ of the loop.
-*)
-
-Lemma hoare_while : forall P b c,
- {{fun st => P st /\ bassn b st}} c {{P}} ->
- {{P}} WHILE b DO c END {{fun st => P st /\ ~ (bassn b st)}}.
-Proof.
- intros P b c Hhoare st st' He HP.
- (* Like we've seen before, we need to reason by induction
- on [He], because, in the "keep looping" case, its hypotheses
- talk about the whole loop instead of just [c]. *)
- remember (WHILE b DO c END) as wcom eqn:Heqwcom.
- ceval_cases (induction He) Case;
- try (inversion Heqwcom); subst; clear Heqwcom.
- Case "E_WhileEnd".
- split. assumption. apply bexp_eval_false. assumption.
- Case "E_WhileLoop".
- apply IHHe2. reflexivity.
- apply (Hhoare st st'). assumption.
- split. assumption. apply bexp_eval_true. assumption.
-Qed.
-
-(**
- One subtlety in the terminology is that calling some assertion [P]
- a "loop invariant" doesn't just mean that it is preserved by the
- body of the loop in question (i.e., [{{P}} c {{P}}], where [c] is
- the loop body), but rather that [P] _together with the fact that
- the loop's guard is true_ is a sufficient precondition for [c] to
- ensure [P] as a postcondition.
-
- This is a slightly (but significantly) weaker requirement. For
- example, if [P] is the assertion [X = 0], then [P] _is_ an
- invariant of the loop
- WHILE X = 2 DO X := 1 END
- although it is clearly _not_ preserved by the body of the
- loop.
-*)
-
-
-
-
-
-Example while_example :
- {{fun st => st X <= 3}}
- WHILE (BLe (AId X) (ANum 2))
- DO X ::= APlus (AId X) (ANum 1) END
- {{fun st => st X = 3}}.
-Proof.
- eapply hoare_consequence_post.
- apply hoare_while.
- eapply hoare_consequence_pre.
- apply hoare_asgn.
- unfold bassn, assn_sub, assert_implies, update. simpl.
- intros st [H1 H2]. apply ble_nat_true in H2. omega.
- unfold bassn, assert_implies. intros st [Hle Hb].
- simpl in Hb. destruct (ble_nat (st X) 2) eqn : Heqle.
- apply ex_falso_quodlibet. apply Hb; reflexivity.
- apply ble_nat_false in Heqle. omega.
-Qed.
-
-
-
-
-
-
-(** *** *)
-(** We can use the while rule to prove the following Hoare triple,
- which may seem surprising at first... *)
-
-Theorem always_loop_hoare : forall P Q,
- {{P}} WHILE BTrue DO SKIP END {{Q}}.
-Proof.
- (* WORKED IN CLASS *)
- intros P Q.
- apply hoare_consequence_pre with (P' := fun st : state => True).
- eapply hoare_consequence_post.
- apply hoare_while.
- Case "Loop body preserves invariant".
- apply hoare_post_true. intros st. apply I.
- Case "Loop invariant and negated guard imply postcondition".
- simpl. intros st [Hinv Hguard].
- apply ex_falso_quodlibet. apply Hguard. reflexivity.
- Case "Precondition implies invariant".
- intros st H. constructor. Qed.
-
-(** Of course, this result is not surprising if we remember that
- the definition of [hoare_triple] asserts that the postcondition
- must hold _only_ when the command terminates. If the command
- doesn't terminate, we can prove anything we like about the
- post-condition. *)
-
-(** Hoare rules that only talk about terminating commands are
- often said to describe a logic of "partial" correctness. It is
- also possible to give Hoare rules for "total" correctness, which
- build in the fact that the commands terminate. However, in this
- course we will only talk about partial correctness. *)
-
-(* ####################################################### *)
-(** *** Exercise: [REPEAT] *)
-
-Module RepeatExercise.
-
-(** **** Exercise: 4 stars, advanced (hoare_repeat) *)
-(** In this exercise, we'll add a new command to our language of
- commands: [REPEAT] c [UNTIL] a [END]. You will write the
- evaluation rule for [repeat] and add a new Hoare rule to
- the language for programs involving it. *)
-
-Inductive com : Type :=
- | CSkip : com
- | CAsgn : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com
- | CRepeat : com -> bexp -> com.
-
-(** [REPEAT] behaves like [WHILE], except that the loop guard is
- checked _after_ each execution of the body, with the loop
- repeating as long as the guard stays _false_. Because of this,
- the body will always execute at least once. *)
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";"
- | Case_aux c "IFB" | Case_aux c "WHILE"
- | Case_aux c "CRepeat" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "c1 ;; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "X '::=' a" :=
- (CAsgn X a) (at level 60).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' e1 'THEN' e2 'ELSE' e3 'FI'" :=
- (CIf e1 e2 e3) (at level 80, right associativity).
-Notation "'REPEAT' e1 'UNTIL' b2 'END'" :=
- (CRepeat e1 b2) (at level 80, right associativity).
-
-(** Add new rules for [REPEAT] to [ceval] below. You can use the rules
- for [WHILE] as a guide, but remember that the body of a [REPEAT]
- should always execute at least once, and that the loop ends when
- the guard becomes true. Then update the [ceval_cases] tactic to
- handle these added cases. *)
-
-Inductive ceval : state -> com -> state -> Prop :=
- | E_Skip : forall st,
- ceval st SKIP st
- | E_Ass : forall st a1 n X,
- aeval st a1 = n ->
- ceval st (X ::= a1) (update st X n)
- | E_Seq : forall c1 c2 st st' st'',
- ceval st c1 st' ->
- ceval st' c2 st'' ->
- ceval st (c1 ;; c2) st''
- | E_IfTrue : forall st st' b1 c1 c2,
- beval st b1 = true ->
- ceval st c1 st' ->
- ceval st (IFB b1 THEN c1 ELSE c2 FI) st'
- | E_IfFalse : forall st st' b1 c1 c2,
- beval st b1 = false ->
- ceval st c2 st' ->
- ceval st (IFB b1 THEN c1 ELSE c2 FI) st'
- | E_WhileEnd : forall b1 st c1,
- beval st b1 = false ->
- ceval st (WHILE b1 DO c1 END) st
- | E_WhileLoop : forall st st' st'' b1 c1,
- beval st b1 = true ->
- ceval st c1 st' ->
- ceval st' (WHILE b1 DO c1 END) st'' ->
- ceval st (WHILE b1 DO c1 END) st''
-(* FILL IN HERE *)
-.
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip" | Case_aux c "E_Ass"
- | Case_aux c "E_Seq"
- | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
- | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop"
-(* FILL IN HERE *)
-].
-
-(** A couple of definitions from above, copied here so they use the
- new [ceval]. *)
-
-Notation "c1 '/' st '||' st'" := (ceval st c1 st')
- (at level 40, st at level 39).
-
-Definition hoare_triple (P:Assertion) (c:com) (Q:Assertion)
- : Prop :=
- forall st st', (c / st || st') -> P st -> Q st'.
-
-Notation "{{ P }} c {{ Q }}" :=
- (hoare_triple P c Q) (at level 90, c at next level).
-
-(** To make sure you've got the evaluation rules for [REPEAT] right,
- prove that [ex1_repeat evaluates correctly. *)
-
-Definition ex1_repeat :=
- REPEAT
- X ::= ANum 1;;
- Y ::= APlus (AId Y) (ANum 1)
- UNTIL (BEq (AId X) (ANum 1)) END.
-
-Theorem ex1_repeat_works :
- ex1_repeat / empty_state ||
- update (update empty_state X 1) Y 1.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Now state and prove a theorem, [hoare_repeat], that expresses an
- appropriate proof rule for [repeat] commands. Use [hoare_while]
- as a model, and try to make your rule as precise as possible. *)
-
-(* FILL IN HERE *)
-
-(** For full credit, make sure (informally) that your rule can be used
- to prove the following valid Hoare triple:
- {{ X > 0 }}
- REPEAT
- Y ::= X;;
- X ::= X - 1
- UNTIL X = 0 END
- {{ X = 0 /\ Y > 0 }}
-*)
-
-
-End RepeatExercise.
-(** [] *)
-
-(* ####################################################### *)
-(** ** Exercise: [HAVOC] *)
-
-(** **** Exercise: 3 stars (himp_hoare) *)
-
-(** In this exercise, we will derive proof rules for the [HAVOC] command
- which we studied in the last chapter. First, we enclose this work
- in a separate module, and recall the syntax and big-step semantics
- of Himp commands. *)
-
-Module Himp.
-
-Inductive com : Type :=
- | CSkip : com
- | CAsgn : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com
- | CHavoc : id -> com.
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";"
- | Case_aux c "IFB" | Case_aux c "WHILE" | Case_aux c "HAVOC" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "X '::=' a" :=
- (CAsgn X a) (at level 60).
-Notation "c1 ;; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' e1 'THEN' e2 'ELSE' e3 'FI'" :=
- (CIf e1 e2 e3) (at level 80, right associativity).
-Notation "'HAVOC' X" := (CHavoc X) (at level 60).
-
-Reserved Notation "c1 '/' st '||' st'" (at level 40, st at level 39).
-
-Inductive ceval : com -> state -> state -> Prop :=
- | E_Skip : forall st : state, SKIP / st || st
- | E_Ass : forall (st : state) (a1 : aexp) (n : nat) (X : id),
- aeval st a1 = n -> (X ::= a1) / st || update st X n
- | E_Seq : forall (c1 c2 : com) (st st' st'' : state),
- c1 / st || st' -> c2 / st' || st'' -> (c1 ;; c2) / st || st''
- | E_IfTrue : forall (st st' : state) (b1 : bexp) (c1 c2 : com),
- beval st b1 = true ->
- c1 / st || st' -> (IFB b1 THEN c1 ELSE c2 FI) / st || st'
- | E_IfFalse : forall (st st' : state) (b1 : bexp) (c1 c2 : com),
- beval st b1 = false ->
- c2 / st || st' -> (IFB b1 THEN c1 ELSE c2 FI) / st || st'
- | E_WhileEnd : forall (b1 : bexp) (st : state) (c1 : com),
- beval st b1 = false -> (WHILE b1 DO c1 END) / st || st
- | E_WhileLoop : forall (st st' st'' : state) (b1 : bexp) (c1 : com),
- beval st b1 = true ->
- c1 / st || st' ->
- (WHILE b1 DO c1 END) / st' || st'' ->
- (WHILE b1 DO c1 END) / st || st''
- | E_Havoc : forall (st : state) (X : id) (n : nat),
- (HAVOC X) / st || update st X n
-
- where "c1 '/' st '||' st'" := (ceval c1 st st').
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip" | Case_aux c "E_Ass" | Case_aux c "E_Seq"
- | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
- | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop"
- | Case_aux c "E_Havoc" ].
-
-(** The definition of Hoare triples is exactly as before. Unlike our
- notion of program equivalence, which had subtle consequences with
- occassionally nonterminating commands (exercise [havoc_diverge]),
- this definition is still fully satisfactory. Convince yourself of
- this before proceeding. *)
-
-Definition hoare_triple (P:Assertion) (c:com) (Q:Assertion) : Prop :=
- forall st st', c / st || st' -> P st -> Q st'.
-
-Notation "{{ P }} c {{ Q }}" := (hoare_triple P c Q)
- (at level 90, c at next level)
- : hoare_spec_scope.
-
-(** Complete the Hoare rule for [HAVOC] commands below by defining
- [havoc_pre] and prove that the resulting rule is correct. *)
-
-Definition havoc_pre (X : id) (Q : Assertion) : Assertion :=
-(* FILL IN HERE *) admit.
-
-Theorem hoare_havoc : forall (Q : Assertion) (X : id),
- {{ havoc_pre X Q }} HAVOC X {{ Q }}.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-End Himp.
-(** [] *)
-
-
-(* ####################################################### *)
-(** ** Complete List of Hoare Logic Rules *)
-
-(** Above, we've introduced Hoare Logic as a tool to reasoning
- about Imp programs. In the reminder of this chapter we will
- explore a systematic way to use Hoare Logic to prove properties
- about programs. The rules of Hoare Logic are the following: *)
-
-(**
- ------------------------------ (hoare_asgn)
- {{Q [X |-> a]}} X::=a {{Q}}
-
- -------------------- (hoare_skip)
- {{ P }} SKIP {{ P }}
-
- {{ P }} c1 {{ Q }}
- {{ Q }} c2 {{ R }}
- --------------------- (hoare_seq)
- {{ P }} c1;;c2 {{ R }}
-
- {{P /\ b}} c1 {{Q}}
- {{P /\ ~b}} c2 {{Q}}
- ------------------------------------ (hoare_if)
- {{P}} IFB b THEN c1 ELSE c2 FI {{Q}}
-
- {{P /\ b}} c {{P}}
- ----------------------------------- (hoare_while)
- {{P}} WHILE b DO c END {{P /\ ~b}}
-
- {{P'}} c {{Q'}}
- P ->> P'
- Q' ->> Q
- ----------------------------- (hoare_consequence)
- {{P}} c {{Q}}
- In the next chapter, we'll see how these rules are used to prove
- that programs satisfy specifications of their behavior.
-*)
-
-(* $Date: 2014-02-27 16:56:35 -0500 (Thu, 27 Feb 2014) $ *)
-
diff --git a/Hoare2.html b/Hoare2.html
deleted file mode 100644
index 8d632fa..0000000
--- a/Hoare2.html
+++ /dev/null
@@ -1,2228 +0,0 @@
-
-
-
-
-
-Hoare2: Hoare Logic, Part II
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Hoare2Hoare Logic, Part II
-
-
-
-
-
-
-
-
-
-
-RequireExportHoare.
-
-
-
-
-
-
Decorated Programs
-
-
-
- The beauty of Hoare Logic is that it is compositional —
- the structure of proofs exactly follows the structure of programs.
- This suggests that we can record the essential ideas of a proof
- informally (leaving out some low-level calculational details) by
- decorating programs with appropriate assertions around each
- statement. Such a decorated program carries with it
- an (informal) proof of its own correctness.
-
-
- For example, here is a complete decorated program:
-
-
-
-
-
-
- {{ True}} ⇾
- {{ m = m}}
- X ::= m;
- {{ X = m}} ⇾
- {{ X = m∧p = p}}
- Z ::= p;
- {{ X = m∧Z = p}} ⇾
- {{ Z - X = p - m}}
- WHILEX ≠ 0 DO
- {{ Z - X = p - m∧X ≠ 0 }} ⇾
- {{ (Z - 1) - (X - 1) = p - m}}
- Z ::= Z - 1;
- {{ Z - (X - 1) = p - m}}
- X ::= X - 1
- {{ Z - X = p - m}}
- END;
- {{ Z - X = p - m∧ ¬ (X ≠ 0) }} ⇾
- {{ Z = p - m}}
-
-
-
-
-
-
- Concretely, a decorated program consists of the program text
- interleaved with assertions. To check that a decorated program
- represents a valid proof, we check that each individual command is
- locally consistent with its accompanying assertions in the
- following sense:
-
-
-
-
-
-
-
SKIP is locally consistent if its precondition and
- postcondition are the same:
-
-
-
-
- {{ P}}
- SKIP
- {{ P}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
The sequential composition of c1 and c2 is locally
- consistent (with respect to assertions P and R) if c1 is
- locally consistent (with respect to P and Q) and c2 is
- locally consistent (with respect to Q and R):
-
-
-
-
- {{ P}}
- c1;
- {{ Q}}
- c2
- {{ R}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
An assignment is locally consistent if its precondition is
- the appropriate substitution of its postcondition:
-
-
-
-
- {{ P [X↦a] }}
- X ::= a
- {{ P}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
A conditional is locally consistent (with respect to assertions
- P and Q) if the assertions at the top of its "then" and
- "else" branches are exactly P∧b and P∧¬b and if its "then"
- branch is locally consistent (with respect to P∧b and Q)
- and its "else" branch is locally consistent (with respect to
- P∧¬b and Q):
-
-
A while loop with precondition P is locally consistent if its
- postcondition is P∧¬b and if the pre- and postconditions of
- its body are exactly P∧b and P:
-
-
A pair of assertions separated by ⇾ is locally consistent if
- the first implies the second (in all states):
-
-
-
-
- {{ P}} ⇾
- {{ P'}}
-
-
-
-
-
-
- This corresponds to the application of hoare_consequence and
- is the only place in a decorated program where checking if
- decorations are correct is not fully mechanical and syntactic,
- but involves logical and/or arithmetic reasoning.
-
-
-
-
-
-
- We have seen above how verifying the correctness of a
- given proof involves checking that every single command is locally
- consistent with the accompanying assertions. If we are instead
- interested in finding a proof for a given specification we need
- to discover the right assertions. This can be done in an almost
- automatic way, with the exception of finding loop invariants,
- which is the subject of in the next section. In the reminder of
- this section we explain in detail how to construct decorations for
- several simple programs that don't involve non-trivial loop
- invariants.
-
-
-
-
-
-
-
-
Example: Swapping Using Addition and Subtraction
-
-
-
- Here is a program that swaps the values of two variables using
- addition and subtraction (instead of by assigning to a temporary
- variable).
-
-
-
-
- X ::= X + Y;
- Y ::= X - Y;
- X ::= X - Y
-
-
-
- We can prove using decorations that this program is correct —
- i.e., it always swaps the values of variables X and Y.
-
-
-
-
-
-
-
-
-
- (1) {{ X = m∧Y = n}} ⇾
- (2) {{ (X + Y) - ((X + Y) - Y) = n∧ (X + Y) - Y = m}}
- X ::= X + Y;
- (3) {{ X - (X - Y) = n∧X - Y = m}}
- Y ::= X - Y;
- (4) {{ X - Y = n∧Y = m}}
- X ::= X - Y
- (5) {{ X = n∧Y = m}}
-
-
-
- The decorations were constructed as follows:
-
-
-
-
-
We begin with the undecorated program (the unnumbered lines).
-
-
-
We then add the specification — i.e., the outer
- precondition (1) and postcondition (5). In the precondition we
- use auxiliary variables (parameters) m and n to remember
- the initial values of variables X and respectively Y, so
- that we can refer to them in the postcondition (5).
-
-
-
We work backwards mechanically starting from (5) all the way
- to (2). At each step, we obtain the precondition of the
- assignment from its postcondition by substituting the assigned
- variable with the right-hand-side of the assignment. For
- instance, we obtain (4) by substituting X with X-Y
- in (5), and (3) by substituting Y with X-Y in (4).
-
-
-
Finally, we verify that (1) logically implies (2) — i.e.,
- that the step from (1) to (2) is a valid use of the law of
- consequence. For this we substitute X by m and Y by n
- and calculate as follows:
-
-
-
-
- (m + n) - ((m + n) - n) = n∧ (m + n) - n = m
- (m + n) - m = n∧m = m
- n = n∧m = m
-
-
-
-
-
-
-
-
-
- (Note that, since we are working with natural numbers, not
- fixed-size machine integers, we don't need to worry about the
- possibility of arithmetic overflow anywhere in this argument.)
-
-
-
-
-
-
-
-
-
Example: Simple Conditionals
-
-
-
- Here is a simple decorated program using conditionals:
-
-
-
-
- (1) {{True}}
- IFBX ≤ YTHEN
- (2) {{True∧X ≤ Y}} ⇾
- (3) {{(Y - X) + X = Y∨ (Y - X) + Y = X}}
- Z ::= Y - X
- (4) {{Z + X = Y∨Z + Y = X}}
- ELSE
- (5) {{True∧ ~(X ≤ Y) }} ⇾
- (6) {{(X - Y) + X = Y∨ (X - Y) + Y = X}}
- Z ::= X - Y
- (7) {{Z + X = Y∨Z + Y = X}}
- FI
- (8) {{Z + X = Y∨Z + Y = X}}
-
-
-
-
-
-
-These decorations were constructed as follows:
-
-
-
-
-
We start with the outer precondition (1) and postcondition (8).
-
-
-
We follow the format dictated by the hoare_if rule and copy the
- postcondition (8) to (4) and (7). We conjoin the precondition (1)
- with the guard of the conditional to obtain (2). We conjoin (1)
- with the negated guard of the conditional to obtain (5).
-
-
-
In order to use the assignment rule and obtain (3), we substitute
- Z by Y-X in (4). To obtain (6) we substitute Z by X-Y
- in (7).
-
-
-
Finally, we verify that (2) implies (3) and (5) implies (6). Both
- of these implications crucially depend on the ordering of X and
- Y obtained from the guard. For instance, knowing that X≤Y
- ensures that subtracting X from Y and then adding back X
- produces Y, as required by the first disjunct of (3). Similarly,
- knowing that ~(X≤Y) ensures that subtracting Y from X and
- then adding back Y produces X, as needed by the second
- disjunct of (6). Note that n-m+m=n does not hold for
- arbitrary natural numbers n and m (for example, 3-5+5=
- 5).
-
-
-
-
-
-
Exercise: 2 stars (if_minus_plus_reloaded)
- Fill in valid decorations for the following program:
-
-
-
-
- {{ True}}
- IFBX ≤ YTHEN
- {{ }} ⇾
- {{ }}
- Z ::= Y - X
- {{ }}
- ELSE
- {{ }} ⇾
- {{ }}
- Y ::= X + Z
- {{ }}
- FI
- {{ Y = X + Z}}
-
-
-
-
-
-
-
-
-
-
-
-
Example: Reduce to Zero (Trivial Loop)
-
-
-
- Here is a WHILE loop that is so simple it needs no
- invariant (i.e., the invariant True will do the job).
-
-
-
-
- (1) {{ True}}
- WHILEX ≠ 0 DO
- (2) {{ True∧X ≠ 0 }} ⇾
- (3) {{ True}}
- X ::= X - 1
- (4) {{ True}}
- END
- (5) {{ True∧X = 0 }} ⇾
- (6) {{ X = 0 }}
-
-
-
-The decorations can be constructed as follows:
-
-
-
-
-
Start with the outer precondition (1) and postcondition (6).
-
-
-
Following the format dictated by the hoare_while rule, we copy
- (1) to (4). We conjoin (1) with the guard to obtain (2) and with
- the negation of the guard to obtain (5). Note that, because the
- outer postcondition (6) does not syntactically match (5), we need a
- trivial use of the consequence rule from (5) to (6).
-
-
-
Assertion (3) is the same as (4), because X does not appear in
- 4, so the substitution in the assignment rule is trivial.
-
-
-
Finally, the implication between (2) and (3) is also trivial.
-
-
-
-
-
-
- From this informal proof, it is easy to read off a formal proof
- using the Coq versions of the Hoare rules. Note that we do not
- unfold the definition of hoare_triple anywhere in this proof —
- the idea is to use the Hoare rules as a "self-contained" logic for
- reasoning about programs.
-
-
-
-
-Definitionreduce_to_zero' : com :=
- WHILEBNot (BEq (AIdX) (ANum 0)) DO
- X ::= AMinus (AIdX) (ANum 1)
- END.
-
-
-Theoremreduce_to_zero_correct' :
- {{funst ⇒ True}}
- reduce_to_zero'
- {{funst ⇒ stX = 0}}.
-Proof.
- unfoldreduce_to_zero'.
- (* First we need to transform the postcondition so
- that hoare_while will apply. *)
- eapplyhoare_consequence_post.
- applyhoare_while.
- Case "Loop body preserves invariant".
- (* Need to massage precondition before hoare_asgn applies *)
- eapplyhoare_consequence_pre. applyhoare_asgn.
- (* Proving trivial implication (2) ->> (3) *)
- introsst [HTHbp]. unfoldassn_sub. applyI.
- Case "Invariant and negated guard imply postcondition".
- introsst [InvGuardFalse].
- unfoldbassninGuardFalse. simplinGuardFalse.
- (* SearchAbout helps to find the right lemmas *)
- SearchAbout [nottrue].
- rewritenot_true_iff_falseinGuardFalse.
- SearchAbout [negbfalse].
- rewritenegb_false_iffinGuardFalse.
- SearchAbout [beq_nattrue].
- applybeq_nat_trueinGuardFalse.
- applyGuardFalse. Qed.
-
-
-
-
-
-
Example: Division
-
-
-
- The following Imp program calculates the integer division and
- remainder of two numbers m and n that are arbitrary constants
- in the program.
-
-
-
-
- X ::= m;
- Y ::= 0;
- WHILEn ≤ XDO
- X ::= X - n;
- Y ::= Y + 1
- END;
-
-
-
- In other words, if we replace m and n by concrete numbers and
- execute the program, it will terminate with the variable X set
- to the remainder when m is divided by n and Y set to the
- quotient.
-
-
- In order to give a specification to this program we need to
- remember that dividing m by n produces a reminder X and a
- quotient Y so that n×Y+X=m∧X<n.
-
-
-
- It turns out that we get lucky with this program and don't have to
- think very hard about the loop invariant: the invariant is the
- just first conjunct n×Y+X=m, so we use that to decorate
- the program.
-
-
-
-
-
-
-
- (1) {{ True}} ⇾
- (2) {{ n × 0 + m = m}}
- X ::= m;
- (3) {{ n × 0 + X = m}}
- Y ::= 0;
- (4) {{ n × Y + X = m}}
- WHILEn ≤ XDO
- (5) {{ n × Y + X = m∧n ≤ X}} ⇾
- (6) {{ n × (Y + 1) + (X - n) = m}}
- X ::= X - n;
- (7) {{ n × (Y + 1) + X = m}}
- Y ::= Y + 1
- (8) {{ n × Y + X = m}}
- END
- (9) {{ n × Y + X = m∧X < n}}
-
-
-
-
-
-
- Assertions (4), (5), (8), and (9) are derived mechanically from
- the invariant and the loop's guard. Assertions (8), (7), and (6)
- are derived using the assignment rule going backwards from (8) to
- (6). Assertions (4), (3), and (2) are again backwards applications
- of the assignment rule.
-
-
-
- Now that we've decorated the program it only remains to check that
- the two uses of the consequence rule are correct — i.e., that (1)
- implies (2) and that (5) implies (6). This is indeed the case, so
- we have a valid decorated program.
-
-
-
-
-
-
-
-
-
Finding Loop Invariants
-
-
-
- Once the outermost precondition and postcondition are chosen, the
- only creative part in verifying programs with Hoare Logic is
- finding the right loop invariants. The reason this is difficult
- is the same as the reason that doing inductive mathematical proofs
- requires creativity: strengthening the loop invariant (or the
- induction hypothesis) means that you have a stronger assumption to
- work with when trying to establish the postcondition of the loop
- body (complete the induction step of the proof), but it also means
- that the loop body postcondition itself is harder to prove!
-
-
-
- This section is dedicated to teaching you how to approach the
- challenge of finding loop invariants using a series of examples
- and exercises.
-
-
-
Example: Slow Subtraction
-
-
-
- The following program subtracts the value of X from the value of
- Y by repeatedly decrementing both X and Y. We want to verify its
- correctness with respect to the following specification:
-
-
-
-
- {{ X = m∧Y = n}}
- WHILEX ≠ 0 DO
- Y ::= Y - 1;
- X ::= X - 1
- END
- {{ Y = n - m}}
-
-
-
-
-
-
- To verify this program we need to find an invariant I for the
- loop. As a first step we can leave I as an unknown and build a
- skeleton for the proof by applying backward the rules for local
- consistency. This process leads to the following skeleton:
-
-
-
-
- (1) {{ X = m∧Y = n}} ⇾ (a)
- (2) {{ I}}
- WHILEX ≠ 0 DO
- (3) {{ I∧X ≠ 0 }} ⇾ (c)
- (4) {{ I[X↦X-1][Y↦Y-1] }}
- Y ::= Y - 1;
- (5) {{ I[X↦X-1] }}
- X ::= X - 1
- (6) {{ I}}
- END
- (7) {{ I∧ ~(X ≠ 0) }} ⇾ (b)
- (8) {{ Y = n - m}}
-
-
-
-
-
-
- By examining this skeleton, we can see that any valid I will
- have to respect three conditions:
-
-
-
-
-
(a) it must be weak enough to be implied by the loop's
- precondition, i.e. (1) must imply (2);
-
-
-
(b) it must be strong enough to imply the loop's postcondition,
- i.e. (7) must imply (8);
-
-
-
(c) it must be preserved by one iteration of the loop, i.e. (3)
- must imply (4).
-
-
-
-
-
- These conditions are actually independent of the particular
- program and specification we are considering. Indeed, every loop
- invariant has to satisfy them. One way to find an invariant that
- simultaneously satisfies these three conditions is by using an
- iterative process: start with a "candidate" invariant (e.g. a
- guess or a heuristic choice) and check the three conditions above;
- if any of the checks fails, try to use the information that we get
- from the failure to produce another (hopefully better) candidate
- invariant, and repeat the process.
-
-
-
- For instance, in the reduce-to-zero example above, we saw that,
- for a very simple loop, choosing True as an invariant did the
- job. So let's try it again here! I.e., let's instantiate I with
- True in the skeleton above see what we get...
-
-
-
-
- (1) {{ X = m∧Y = n}} ⇾ (a - OK)
- (2) {{ True}}
- WHILEX ≠ 0 DO
- (3) {{ True∧X ≠ 0 }} ⇾ (c - OK)
- (4) {{ True}}
- Y ::= Y - 1;
- (5) {{ True}}
- X ::= X - 1
- (6) {{ True}}
- END
- (7) {{ True∧X = 0 }} ⇾ (b - WRONG!)
- (8) {{ Y = n - m}}
-
-
-
-
-
-
- While conditions (a) and (c) are trivially satisfied,
- condition (b) is wrong, i.e. it is not the case that (7) True∧
- X=0 implies (8) Y=n-m. In fact, the two assertions are
- completely unrelated and it is easy to find a counterexample (say,
- Y=X=m=0 and n=1).
-
-
-
- If we want (b) to hold, we need to strengthen the invariant so
- that it implies the postcondition (8). One very simple way to do
- this is to let the invariant be the postcondition. So let's
- return to our skeleton, instantiate I with Y=n-m, and
- check conditions (a) to (c) again.
-
-
-
-
- (1) {{ X = m∧Y = n}} ⇾ (a - WRONG!)
- (2) {{ Y = n - m}}
- WHILEX ≠ 0 DO
- (3) {{ Y = n - m∧X ≠ 0 }} ⇾ (c - WRONG!)
- (4) {{ Y - 1 = n - m}}
- Y ::= Y - 1;
- (5) {{ Y = n - m}}
- X ::= X - 1
- (6) {{ Y = n - m}}
- END
- (7) {{ Y = n - m∧X = 0 }} ⇾ (b - OK)
- (8) {{ Y = n - m}}
-
-
-
-
-
-
- This time, condition (b) holds trivially, but (a) and (c) are
- broken. Condition (a) requires that (1) X=m∧Y=n
- implies (2) Y=n-m. If we substitute Y by n we have to
- show that n=n-m for arbitrary m and n, which does not
- hold (for instance, when m=n=1). Condition (c) requires that
- n-m-1=n-m, which fails, for instance, for n=1 and m=
- 0. So, although Y=n-m holds at the end of the loop, it does
- not hold from the start, and it doesn't hold on each iteration;
- it is not a correct invariant.
-
-
-
- This failure is not very surprising: the variable Y changes
- during the loop, while m and n are constant, so the assertion
- we chose didn't have much chance of being an invariant!
-
-
-
- To do better, we need to generalize (8) to some statement that is
- equivalent to (8) when X is 0, since this will be the case
- when the loop terminates, and that "fills the gap" in some
- appropriate way when X is nonzero. Looking at how the loop
- works, we can observe that X and Y are decremented together
- until X reaches 0. So, if X=2 and Y=5 initially,
- after one iteration of the loop we obtain X=1 and Y=4;
- after two iterations X=0 and Y=3; and then the loop stops.
- Notice that the difference between Y and X stays constant
- between iterations; initially, Y=n and X=m, so this
- difference is always n-m. So let's try instantiating I in
- the skeleton above with Y-X=n-m.
-
-
-
-
- (1) {{ X = m∧Y = n}} ⇾ (a - OK)
- (2) {{ Y - X = n - m}}
- WHILEX ≠ 0 DO
- (3) {{ Y - X = n - m∧X ≠ 0 }} ⇾ (c - OK)
- (4) {{ (Y - 1) - (X - 1) = n - m}}
- Y ::= Y - 1;
- (5) {{ Y - (X - 1) = n - m}}
- X ::= X - 1
- (6) {{ Y - X = n - m}}
- END
- (7) {{ Y - X = n - m∧X = 0 }} ⇾ (b - OK)
- (8) {{ Y = n - m}}
-
-
-
-
-
-
- Success! Conditions (a), (b) and (c) all hold now. (To
- verify (c), we need to check that, under the assumption that X≠
- 0, we have Y-X=(Y-1)-(X-1); this holds for all
- natural numbers X and Y.)
-
-
-
-
-
-
-
-
Exercise: Slow Assignment
-
-
-
-
Exercise: 2 stars (slow_assignment)
- A roundabout way of assigning a number currently stored in X to
- the variable Y is to start Y at 0, then decrement X until
- it hits 0, incrementing Y at each step. Here is a program that
- implements this idea:
-
-
-
-
- {{ X = m}}
- Y ::= 0;
- WHILEX ≠ 0 DO
- X ::= X - 1;
- Y ::= Y + 1;
- END
- {{ Y = m}}
-
-
-
- Write an informal decorated program showing that this is correct.
-
- The following program adds the variable X into the variable Z
- by repeatedly decrementing X and incrementing Z.
-
-
-
-
- WHILEX ≠ 0 DO
- Z ::= Z + 1;
- X ::= X - 1
- END
-
-
-
-
-
-
- Following the pattern of the subtract_slowly example above, pick
- a precondition and postcondition that give an appropriate
- specification of add_slowly; then (informally) decorate the
- program accordingly.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Example: Parity
-
-
-
- Here is a cute little program for computing the parity of the
- value initially stored in X (due to Daniel Cristofani).
-
-
-
-
- {{ X = m}}
- WHILE 2 ≤ XDO
- X ::= X - 2
- END
- {{ X = paritym}}
-
-
-
- The mathematical parity function used in the specification is
- defined in Coq as follows:
-
-The postcondition does not hold at the beginning of the loop,
- since m=paritym does not hold for an arbitrary m, so we
- cannot use that as an invariant. To find an invariant that works,
- let's think a bit about what this loop does. On each iteration it
- decrements X by 2, which preserves the parity of X. So the
- parity of X does not change, i.e. it is invariant. The initial
- value of X is m, so the parity of X is always equal to the
- parity of m. Using parityX=paritym as an invariant we
- obtain the following decorated program:
-
-
-
-
- {{ X = m}} ⇾ (a - OK)
- {{ parityX = paritym}}
- WHILE 2 ≤ XDO
- {{ parityX = paritym∧ 2 ≤ X}} ⇾ (c - OK)
- {{ parity (X-2) = paritym}}
- X ::= X - 2
- {{ parityX = paritym}}
- END
- {{ parityX = paritym∧X < 2 }} ⇾ (b - OK)
- {{ X = paritym}}
-
-
-
-
-
-
- With this invariant, conditions (a), (b), and (c) are all
- satisfied. For verifying (b), we observe that, when X<2, we
- have parityX=X (we can easily see this in the definition of
- parity). For verifying (c), we observe that, when 2≤X, we
- have parityX=parity(X-2).
-
-
-
Exercise: 3 stars, optional (parity_formal)
- Translate this proof to Coq. Refer to the reduce-to-zero example
- for ideas. You may find the following two lemmas useful:
-
-
- This didn't work very well: both conditions (a) and (c) failed.
- Looking at condition (c), we see that the second conjunct of (4)
- is almost the same as the first conjunct of (5), except that (4)
- mentions X while (5) mentions m. But note that X is never
- assigned in this program, so we should have X=m, but we didn't
- propagate this information from (1) into the loop invariant.
-
-
-
- Also, looking at the second conjunct of (8), it seems quite
- hopeless as an invariant — and we don't even need it, since we
- can obtain it from the negation of the guard (third conjunct
- in (7)), again under the assumption that X=m.
-
-
-
- So we now try X=m∧Z×Z≤m as the loop invariant:
-
-
-
- This works, since conditions (a), (b), and (c) are now all
- trivially satisfied.
-
-
-
- Very often, if a variable is used in a loop in a read-only
- fashion (i.e., it is referred to by the program or by the
- specification and it is not changed by the loop) it is necessary
- to add the fact that it doesn't change to the loop invariant.
-
-
-
-
-
-
-
-
Example: Squaring
-
-
-
- Here is a program that squares X by repeated addition:
-
-
-
-
-
-
-
- {{ X = m}}
- Y ::= 0;
- Z ::= 0;
- WHILEY ≠ XDO
- Z ::= Z + X;
- Y ::= Y + 1
- END
- {{ Z = m×m}}
-
-
-
-
-
-
- The first thing to note is that the loop reads X but doesn't
- change its value. As we saw in the previous example, in such cases
- it is a good idea to add X=m to the invariant. The other thing
- we often use in the invariant is the postcondition, so let's add
- that too, leading to the invariant candidate Z=m×m∧X=m.
-
-
-
-
- {{ X = m}} ⇾ (a - WRONG)
- {{ 0 = m×m∧X = m}}
- Y ::= 0;
- {{ 0 = m×m∧X = m}}
- Z ::= 0;
- {{ Z = m×m∧X = m}}
- WHILEY ≠ XDO
- {{ Z = Y×m∧X = m∧Y ≠ X}} ⇾ (c - WRONG)
- {{ Z+X = m×m∧X = m}}
- Z ::= Z + X;
- {{ Z = m×m∧X = m}}
- Y ::= Y + 1
- {{ Z = m×m∧X = m}}
- END
- {{ Z = m×m∧X = m∧Y = X}} ⇾ (b - OK)
- {{ Z = m×m}}
-
-
-
-
-
-
- Conditions (a) and (c) fail because of the Z=m×m part. While
- Z starts at 0 and works itself up to m×m, we can't expect
- Z to be m×m from the start. If we look at how Z progesses
- in the loop, after the 1st iteration Z=m, after the 2nd
- iteration Z=2×m, and at the end Z=m×m. Since the variable
- Y tracks how many times we go through the loop, we derive the
- new invariant candidate Z=Y×m∧X=m.
-
-
-
-
- {{ X = m}} ⇾ (a - OK)
- {{ 0 = 0×m∧X = m}}
- Y ::= 0;
- {{ 0 = Y×m∧X = m}}
- Z ::= 0;
- {{ Z = Y×m∧X = m}}
- WHILEY ≠ XDO
- {{ Z = Y×m∧X = m∧Y ≠ X}} ⇾ (c - OK)
- {{ Z+X = (Y+1)×m∧X = m}}
- Z ::= Z + X;
- {{ Z = (Y+1)×m∧X = m}}
- Y ::= Y + 1
- {{ Z = Y×m∧X = m}}
- END
- {{ Z = Y×m∧X = m∧Y = X}} ⇾ (b - OK)
- {{ Z = m×m}}
-
-
-
-
-
-
- This new invariant makes the proof go through: all three
- conditions are easy to check.
-
-
-
- It is worth comparing the postcondition Z=m×m and the Z=
- Y×m conjunct of the invariant. It is often the case that one has
- to replace auxiliary variabes (parameters) with variables — or
- with expressions involving both variables and parameters (like
- m-Y) — when going from postconditions to invariants.
-
-
-
-
-
-
-
-
Exercise: Factorial
-
-
-
-
Exercise: 3 stars (factorial)
- Recall that n! denotes the factorial of n (i.e. n!=
- 1×2×...×n). Here is an Imp program that calculates the factorial
- of the number initially stored in the variable X and puts it in
- the variable Y:
-
-
-
-
- {{ X = m}} ;
- Y ::= 1
- WHILEX ≠ 0
- DO
- Y ::= Y × X
- X ::= X - 1
- END
- {{ Y = m! }}
-
-
-
-
-
-
- Fill in the blanks in following decorated program:
-
-
-
-
- {{ X = m}} ⇾
- {{ }}
- Y ::= 1;
- {{ }}
- WHILEX ≠ 0
- DO{{ }} ⇾
- {{ }}
- Y ::= Y × X;
- {{ }}
- X ::= X - 1
- {{ }}
- END
- {{ }} ⇾
- {{ Y = m! }}
-
-
-
-
-
-
- ☐
-
-
-
-
-
-
-
-
Exercise: Min
-
-
-
-
Exercise: 3 stars (Min_Hoare)
- Fill in valid decorations for the following program.
- For the => steps in your annotations, you may rely (silently) on the
- following facts about min
-
-
-
- Lemma lemma1 : forall x y,
- (x=0 λ/ y=0) -> min x y = 0.
- Lemma lemma2 : forall x y,
- min (x-1) (y-1) = (min x y) - 1.
-
-
-
- plus, as usual, standard high-school algebra.
-
-
-
-
-
-
-
- {{ True}} ⇾
- {{ }}
- X ::= a;
- {{ }}
- Y ::= b;
- {{ }}
- Z ::= 0;
- {{ }}
- WHILE (X ≠ 0 ∧Y ≠ 0) DO
- {{ }} ⇾
- {{ }}
- X := X - 1;
- {{ }}
- Y := Y - 1;
- {{ }}
- Z := Z + 1;
- {{ }}
- END
- {{ }} ⇾
- {{ Z = minab}}
-
-
-
-
-
-
-
Exercise: 3 stars (two_loops)
- Here is a very inefficient way of adding 3 numbers:
-
-
-
-
- X ::= 0;
- Y ::= 0;
- Z ::= c;
- WHILEX ≠ aDO
- X ::= X + 1;
- Z ::= Z + 1
- END;
- WHILEY ≠ bDO
- Y ::= Y + 1;
- Z ::= Z + 1
- END
-
-
-
-
-
-
- Show that it does what it should by filling in the blanks in the
- following decorated program.
-
-
-
-
-
-
-
- {{ True}} ⇾
- {{ }}
- X ::= 0;
- {{ }}
- Y ::= 0;
- {{ }}
- Z ::= c;
- {{ }}
- WHILEX ≠ aDO
- {{ }} ⇾
- {{ }}
- X ::= X + 1;
- {{ }}
- Z ::= Z + 1
- {{ }}
- END;
- {{ }} ⇾
- {{ }}
- WHILEY ≠ bDO
- {{ }} ⇾
- {{ }}
- Y ::= Y + 1;
- {{ }}
- Z ::= Z + 1
- {{ }}
- END
- {{ }} ⇾
- {{ Z = a + b + c}}
-
-
-
-
-
-
-
-
-
-
-
-
Exercise: Power Series
-
-
-
-
Exercise: 4 stars, optional (dpow2_down)
- Here is a program that computes the series:
- 1+2+2^2+...+2^m=2^(m+1)-1
-
-
-
-
- X ::= 0;
- Y ::= 1;
- Z ::= 1;
- WHILEX ≠ mDO
- Z ::= 2 × Z;
- Y ::= Y + Z;
- X ::= X + 1;
- END
-
-
-
- Write a decorated program for this.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-
Weakest Preconditions (Advanced)
-
-
-
- Some Hoare triples are more interesting than others.
- For example,
-
-
-
-
- {{ False}} X ::= Y + 1 {{ X ≤ 5 }}
-
-
-
- is not very interesting: although it is perfectly valid, it
- tells us nothing useful. Since the precondition isn't satisfied
- by any state, it doesn't describe any situations where we can use
- the command X::=Y+1 to achieve the postcondition X≤5.
-
-
-
- By contrast,
-
-
-
-
- {{ Y ≤ 4 ∧Z = 0 }} X ::= Y + 1 {{ X ≤ 5 }}
-
-
-
- is useful: it tells us that, if we can somehow create a situation
- in which we know that Y≤4∧Z=0, then running this command
- will produce a state satisfying the postcondition. However, this
- triple is still not as useful as it could be, because the Z=0
- clause in the precondition actually has nothing to do with the
- postcondition X≤5. The most useful triple (for a given
- command and postcondition) is this one:
-
-
-
-
- {{ Y ≤ 4 }} X ::= Y + 1 {{ X ≤ 5 }}
-
-
-
- In other words, Y≤4 is the weakest valid precondition of
- the command X::=Y+1 for the postcondition X≤5.
-
-
- In general, we say that "P is the weakest precondition of
- command c for postcondition Q" if {{P}}c{{Q}} and if,
- whenever P' is an assertion such that {{P'}}c{{Q}}, we have
- P'st implies Pst for all states st.
-
-That is, P is the weakest precondition of c for Q
- if (a) Pis a precondition for Q and c, and (b) P is the
- weakest (easiest to satisfy) assertion that guarantees Q after
- executing c.
-
-
-
Exercise: 1 star, optional (wp)
- What are the weakest preconditions of the following commands
- for the following postconditions?
-
-
-
-
- 1) {{ ? }} SKIP{{ X = 5 }}
-
- 2) {{ ? }} X ::= Y + Z{{ X = 5 }}
-
- 3) {{ ? }} X ::= Y{{ X = Y}}
-
- 4) {{ ? }}
- IFBX == 0 THENY ::= Z + 1 ELSEY ::= W + 2 FI
- {{ Y = 5 }}
-
- 5) {{ ? }}
- X ::= 5
- {{ X = 0 }}
-
- 6) {{ ? }}
- WHILETrueDOX ::= 0 END
- {{ X = 0 }}
-
- Show that your havoc_pre rule from the himp_hoare exercise
- in the Hoare chapter returns the weakest precondition.
-
-
-
-
-Lemmahoare_havoc_weakest : ∀(PQ : Assertion) (X : id),
- {{ P}} HAVOCX{{ Q}} →
- P⇾havoc_preXQ.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Formal Decorated Programs (Advanced)
-
-
-
- The informal conventions for decorated programs amount to a way of
- displaying Hoare triples in which commands are annotated with
- enough embedded assertions that checking the validity of the
- triple is reduced to simple logical and algebraic calculations
- showing that some assertions imply others. In this section, we
- show that this informal presentation style can actually be made
- completely formal and indeed that checking the validity of
- decorated programs can mostly be automated.
-
-
-
Syntax
-
-
-
- The first thing we need to do is to formalize a variant of the
- syntax of commands with embedded assertions. We call the new
- commands decorated commands, or dcoms.
-
-To avoid clashing with the existing Notation definitions
- for ordinary commands, we introduce these notations in a special
- scope called dcom_scope, and we wrap examples with the
- declaration %dcom to signal that we want the notations to be
- interpreted in this scope.
-
-
-
- Careful readers will note that we've defined two notations for the
- DCPre constructor, one with and one without a ⇾. The
- "without" version is intended to be used to supply the initial
- precondition at the very top of the program.
-
-The choice of exactly where to put assertions in the definition of
- dcom is a bit subtle. The simplest thing to do would be to
- annotate every dcom with a precondition and postcondition. But
- this would result in very verbose programs with a lot of repeated
- annotations: for example, a program like SKIP;SKIP would have to
- be annotated as
-
-
- with pre- and post-conditions on each SKIP, plus identical pre-
- and post-conditions on the semicolon!
-
-
-
- Instead, the rule we've followed is this:
-
-
-
-
-
The post-condition expected by each dcomd is embedded in d
-
-
-
-
-
-
The pre-condition is supplied by the context.
-
-
-
-
-
- In other words, the invariant of the representation is that a
- dcomd together with a precondition P determines a Hoare
- triple {{P}}(extractd){{postd}}, where post is defined as
- follows:
-
-This function is not doing anything sophisticated like calculating
- a weakest precondition; it just recursively searches for an
- explicit annotation at the very beginning of the program,
- returning default answers for programs that lack an explicit
- precondition (like a bare assignment or SKIP).
-
-
- Using pre and post, and assuming that we adopt the convention
- of always supplying an explicit precondition annotation at the
- very beginning of our decorated programs, we can express what it
- means for a decorated program to be correct as follows:
-
-To check whether this Hoare triple is valid, we need a way to
- extract the "proof obligations" from a decorated program. These
- obligations are often called verification conditions, because
- they are the facts that must be verified to see that the
- decorations are logically consistent and thus add up to a complete
- proof of correctness.
-
-
-
Extracting Verification Conditions
-
-
-
- The function verification_conditions takes a dcomd together
- with a precondition P and returns a proposition that, if it
- can be proved, implies that the triple {{P}}(extractd){{postd}}
- is valid.
-
-
- It does this by walking over d and generating a big
- conjunction including all the "local checks" that we listed when
- we described the informal rules for decorated programs. (Strictly
- speaking, we need to massage the informal rules a little bit to
- add some uses of the rule of consequence, but the correspondence
- should be clear.)
-
-And now, the key theorem, which states that
- verification_conditions does its job correctly. Not
- surprisingly, we need to use each of the Hoare Logic rules at some
- point in the proof. We have used in variants of several tactics before to
- apply them to values in the context rather than the goal. An
- extension of this idea is the syntax tacticin×, which applies
- tactic in the goal and every hypothesis in the context. We most
- commonly use this facility in conjunction with the simpl tactic,
- as below.
-
-
- In principle, we could certainly work with them using just the
- tactics we have so far, but we can make things much smoother with
- a bit of automation. We first define a custom verify tactic
- that applies splitting repeatedly to turn all the conjunctions
- into separate subgoals and then uses omega and eauto (a handy
- general-purpose automation tactic that we'll discuss in detail
- later) to deal with as many of them as possible.
-
-What's left after verify does its thing is "just the interesting
- parts" of checking that the decorations are correct. For very
- simple examples verify immediately solves the goal (provided
- that the annotations are correct).
-
-
- In the slow_assignment exercise above, we saw a roundabout way
- of assigning a number currently stored in X to the variable Y:
- start Y at 0, then decrement X until it hits 0,
- incrementing Y at each step.
-
-
-
- Write a formal version of this decorated program and prove it
- correct.
-
-
-
-
-Exampleslow_assignment_dec (m:nat) : dcom :=
-(* FILL IN HERE *)admit.
-
-
-Theoremslow_assignment_dec_correct : ∀m,
- dec_correct (slow_assignment_decm).
-Proof. (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 4 stars, advanced (factorial_dec)
- Remember the factorial function we worked with before:
-
-
-
-
\ No newline at end of file
diff --git a/Hoare2.v b/Hoare2.v
deleted file mode 100644
index 3e6c9df..0000000
--- a/Hoare2.v
+++ /dev/null
@@ -1,1406 +0,0 @@
-(** * Hoare2: Hoare Logic, Part II *)
-
-Require Export Hoare.
-
-
-
-(* ####################################################### *)
-(** * Decorated Programs *)
-
-(** The beauty of Hoare Logic is that it is _compositional_ --
- the structure of proofs exactly follows the structure of programs.
- This suggests that we can record the essential ideas of a proof
- informally (leaving out some low-level calculational details) by
- decorating programs with appropriate assertions around each
- statement. Such a _decorated program_ carries with it
- an (informal) proof of its own correctness. *)
-
-(** For example, here is a complete decorated program: *)
-(**
- {{ True }} ->>
- {{ m = m }}
- X ::= m;
- {{ X = m }} ->>
- {{ X = m /\ p = p }}
- Z ::= p;
- {{ X = m /\ Z = p }} ->>
- {{ Z - X = p - m }}
- WHILE X <> 0 DO
- {{ Z - X = p - m /\ X <> 0 }} ->>
- {{ (Z - 1) - (X - 1) = p - m }}
- Z ::= Z - 1;
- {{ Z - (X - 1) = p - m }}
- X ::= X - 1
- {{ Z - X = p - m }}
- END;
- {{ Z - X = p - m /\ ~ (X <> 0) }} ->>
- {{ Z = p - m }}
-*)
-
-(** Concretely, a decorated program consists of the program text
- interleaved with assertions. To check that a decorated program
- represents a valid proof, we check that each individual command is
- _locally consistent_ with its accompanying assertions in the
- following sense: *)
-
-(**
- - [SKIP] is locally consistent if its precondition and
- postcondition are the same:
- {{ P }}
- SKIP
- {{ P }}
-*)
-
-(**
- - The sequential composition of [c1] and [c2] is locally
- consistent (with respect to assertions [P] and [R]) if [c1] is
- locally consistent (with respect to [P] and [Q]) and [c2] is
- locally consistent (with respect to [Q] and [R]):
- {{ P }}
- c1;
- {{ Q }}
- c2
- {{ R }}
-*)
-
-(**
-
- - An assignment is locally consistent if its precondition is
- the appropriate substitution of its postcondition:
- {{ P [X |-> a] }}
- X ::= a
- {{ P }}
-*)
-
-(**
- - A conditional is locally consistent (with respect to assertions
- [P] and [Q]) if the assertions at the top of its "then" and
- "else" branches are exactly [P /\ b] and [P /\ ~b] and if its "then"
- branch is locally consistent (with respect to [P /\ b] and [Q])
- and its "else" branch is locally consistent (with respect to
- [P /\ ~b] and [Q]):
- {{ P }}
- IFB b THEN
- {{ P /\ b }}
- c1
- {{ Q }}
- ELSE
- {{ P /\ ~b }}
- c2
- {{ Q }}
- FI
- {{ Q }}
-*)
-
-(**
-
- - A while loop with precondition [P] is locally consistent if its
- postcondition is [P /\ ~b] and if the pre- and postconditions of
- its body are exactly [P /\ b] and [P]:
- {{ P }}
- WHILE b DO
- {{ P /\ b }}
- c1
- {{ P }}
- END
- {{ P /\ ~b }}
-*)
-
-(**
-
- - A pair of assertions separated by [->>] is locally consistent if
- the first implies the second (in all states):
- {{ P }} ->>
- {{ P' }}
-
- This corresponds to the application of [hoare_consequence] and
- is the only place in a decorated program where checking if
- decorations are correct is not fully mechanical and syntactic,
- but involves logical and/or arithmetic reasoning.
-*)
-
-(** We have seen above how _verifying_ the correctness of a
- given proof involves checking that every single command is locally
- consistent with the accompanying assertions. If we are instead
- interested in _finding_ a proof for a given specification we need
- to discover the right assertions. This can be done in an almost
- automatic way, with the exception of finding loop invariants,
- which is the subject of in the next section. In the reminder of
- this section we explain in detail how to construct decorations for
- several simple programs that don't involve non-trivial loop
- invariants. *)
-
-(* ####################################################### *)
-(** ** Example: Swapping Using Addition and Subtraction *)
-
-(** Here is a program that swaps the values of two variables using
- addition and subtraction (instead of by assigning to a temporary
- variable).
- X ::= X + Y;
- Y ::= X - Y;
- X ::= X - Y
- We can prove using decorations that this program is correct --
- i.e., it always swaps the values of variables [X] and [Y]. *)
-
-(**
- (1) {{ X = m /\ Y = n }} ->>
- (2) {{ (X + Y) - ((X + Y) - Y) = n /\ (X + Y) - Y = m }}
- X ::= X + Y;
- (3) {{ X - (X - Y) = n /\ X - Y = m }}
- Y ::= X - Y;
- (4) {{ X - Y = n /\ Y = m }}
- X ::= X - Y
- (5) {{ X = n /\ Y = m }}
- The decorations were constructed as follows:
- - We begin with the undecorated program (the unnumbered lines).
- - We then add the specification -- i.e., the outer
- precondition (1) and postcondition (5). In the precondition we
- use auxiliary variables (parameters) [m] and [n] to remember
- the initial values of variables [X] and respectively [Y], so
- that we can refer to them in the postcondition (5).
- - We work backwards mechanically starting from (5) all the way
- to (2). At each step, we obtain the precondition of the
- assignment from its postcondition by substituting the assigned
- variable with the right-hand-side of the assignment. For
- instance, we obtain (4) by substituting [X] with [X - Y]
- in (5), and (3) by substituting [Y] with [X - Y] in (4).
- - Finally, we verify that (1) logically implies (2) -- i.e.,
- that the step from (1) to (2) is a valid use of the law of
- consequence. For this we substitute [X] by [m] and [Y] by [n]
- and calculate as follows:
- (m + n) - ((m + n) - n) = n /\ (m + n) - n = m
- (m + n) - m = n /\ m = m
- n = n /\ m = m
-
- (Note that, since we are working with natural numbers, not
- fixed-size machine integers, we don't need to worry about the
- possibility of arithmetic overflow anywhere in this argument.)
-*)
-
-(* ####################################################### *)
-(** ** Example: Simple Conditionals *)
-
-(** Here is a simple decorated program using conditionals:
- (1) {{True}}
- IFB X <= Y THEN
- (2) {{True /\ X <= Y}} ->>
- (3) {{(Y - X) + X = Y \/ (Y - X) + Y = X}}
- Z ::= Y - X
- (4) {{Z + X = Y \/ Z + Y = X}}
- ELSE
- (5) {{True /\ ~(X <= Y) }} ->>
- (6) {{(X - Y) + X = Y \/ (X - Y) + Y = X}}
- Z ::= X - Y
- (7) {{Z + X = Y \/ Z + Y = X}}
- FI
- (8) {{Z + X = Y \/ Z + Y = X}}
-
-These decorations were constructed as follows:
- - We start with the outer precondition (1) and postcondition (8).
- - We follow the format dictated by the [hoare_if] rule and copy the
- postcondition (8) to (4) and (7). We conjoin the precondition (1)
- with the guard of the conditional to obtain (2). We conjoin (1)
- with the negated guard of the conditional to obtain (5).
- - In order to use the assignment rule and obtain (3), we substitute
- [Z] by [Y - X] in (4). To obtain (6) we substitute [Z] by [X - Y]
- in (7).
- - Finally, we verify that (2) implies (3) and (5) implies (6). Both
- of these implications crucially depend on the ordering of [X] and
- [Y] obtained from the guard. For instance, knowing that [X <= Y]
- ensures that subtracting [X] from [Y] and then adding back [X]
- produces [Y], as required by the first disjunct of (3). Similarly,
- knowing that [~(X <= Y)] ensures that subtracting [Y] from [X] and
- then adding back [Y] produces [X], as needed by the second
- disjunct of (6). Note that [n - m + m = n] does _not_ hold for
- arbitrary natural numbers [n] and [m] (for example, [3 - 5 + 5 =
- 5]). *)
-
-(** **** Exercise: 2 stars (if_minus_plus_reloaded) *)
-(** Fill in valid decorations for the following program:
- {{ True }}
- IFB X <= Y THEN
- {{ }} ->>
- {{ }}
- Z ::= Y - X
- {{ }}
- ELSE
- {{ }} ->>
- {{ }}
- Y ::= X + Z
- {{ }}
- FI
- {{ Y = X + Z }}
-*)
-
-
-(* ####################################################### *)
-(** ** Example: Reduce to Zero (Trivial Loop) *)
-
-(** Here is a [WHILE] loop that is so simple it needs no
- invariant (i.e., the invariant [True] will do the job).
- (1) {{ True }}
- WHILE X <> 0 DO
- (2) {{ True /\ X <> 0 }} ->>
- (3) {{ True }}
- X ::= X - 1
- (4) {{ True }}
- END
- (5) {{ True /\ X = 0 }} ->>
- (6) {{ X = 0 }}
-The decorations can be constructed as follows:
- - Start with the outer precondition (1) and postcondition (6).
- - Following the format dictated by the [hoare_while] rule, we copy
- (1) to (4). We conjoin (1) with the guard to obtain (2) and with
- the negation of the guard to obtain (5). Note that, because the
- outer postcondition (6) does not syntactically match (5), we need a
- trivial use of the consequence rule from (5) to (6).
- - Assertion (3) is the same as (4), because [X] does not appear in
- [4], so the substitution in the assignment rule is trivial.
- - Finally, the implication between (2) and (3) is also trivial.
-*)
-
-(** From this informal proof, it is easy to read off a formal proof
- using the Coq versions of the Hoare rules. Note that we do _not_
- unfold the definition of [hoare_triple] anywhere in this proof --
- the idea is to use the Hoare rules as a "self-contained" logic for
- reasoning about programs. *)
-
-Definition reduce_to_zero' : com :=
- WHILE BNot (BEq (AId X) (ANum 0)) DO
- X ::= AMinus (AId X) (ANum 1)
- END.
-
-Theorem reduce_to_zero_correct' :
- {{fun st => True}}
- reduce_to_zero'
- {{fun st => st X = 0}}.
-Proof.
- unfold reduce_to_zero'.
- (* First we need to transform the postcondition so
- that hoare_while will apply. *)
- eapply hoare_consequence_post.
- apply hoare_while.
- Case "Loop body preserves invariant".
- (* Need to massage precondition before [hoare_asgn] applies *)
- eapply hoare_consequence_pre. apply hoare_asgn.
- (* Proving trivial implication (2) ->> (3) *)
- intros st [HT Hbp]. unfold assn_sub. apply I.
- Case "Invariant and negated guard imply postcondition".
- intros st [Inv GuardFalse].
- unfold bassn in GuardFalse. simpl in GuardFalse.
- (* SearchAbout helps to find the right lemmas *)
- SearchAbout [not true].
- rewrite not_true_iff_false in GuardFalse.
- SearchAbout [negb false].
- rewrite negb_false_iff in GuardFalse.
- SearchAbout [beq_nat true].
- apply beq_nat_true in GuardFalse.
- apply GuardFalse. Qed.
-
-(* ####################################################### *)
-(** ** Example: Division *)
-
-
-(** The following Imp program calculates the integer division and
- remainder of two numbers [m] and [n] that are arbitrary constants
- in the program.
- X ::= m;
- Y ::= 0;
- WHILE n <= X DO
- X ::= X - n;
- Y ::= Y + 1
- END;
- In other words, if we replace [m] and [n] by concrete numbers and
- execute the program, it will terminate with the variable [X] set
- to the remainder when [m] is divided by [n] and [Y] set to the
- quotient. *)
-
-(** In order to give a specification to this program we need to
- remember that dividing [m] by [n] produces a reminder [X] and a
- quotient [Y] so that [n * Y + X = m /\ X < n].
-
- It turns out that we get lucky with this program and don't have to
- think very hard about the loop invariant: the invariant is the
- just first conjunct [n * Y + X = m], so we use that to decorate
- the program.
-
- (1) {{ True }} ->>
- (2) {{ n * 0 + m = m }}
- X ::= m;
- (3) {{ n * 0 + X = m }}
- Y ::= 0;
- (4) {{ n * Y + X = m }}
- WHILE n <= X DO
- (5) {{ n * Y + X = m /\ n <= X }} ->>
- (6) {{ n * (Y + 1) + (X - n) = m }}
- X ::= X - n;
- (7) {{ n * (Y + 1) + X = m }}
- Y ::= Y + 1
- (8) {{ n * Y + X = m }}
- END
- (9) {{ n * Y + X = m /\ X < n }}
-
- Assertions (4), (5), (8), and (9) are derived mechanically from
- the invariant and the loop's guard. Assertions (8), (7), and (6)
- are derived using the assignment rule going backwards from (8) to
- (6). Assertions (4), (3), and (2) are again backwards applications
- of the assignment rule.
-
- Now that we've decorated the program it only remains to check that
- the two uses of the consequence rule are correct -- i.e., that (1)
- implies (2) and that (5) implies (6). This is indeed the case, so
- we have a valid decorated program.
-*)
-
-(* ####################################################### *)
-(** * Finding Loop Invariants *)
-
-(** Once the outermost precondition and postcondition are chosen, the
- only creative part in verifying programs with Hoare Logic is
- finding the right loop invariants. The reason this is difficult
- is the same as the reason that doing inductive mathematical proofs
- requires creativity: strengthening the loop invariant (or the
- induction hypothesis) means that you have a stronger assumption to
- work with when trying to establish the postcondition of the loop
- body (complete the induction step of the proof), but it also means
- that the loop body postcondition itself is harder to prove!
-
- This section is dedicated to teaching you how to approach the
- challenge of finding loop invariants using a series of examples
- and exercises. *)
-
-(** ** Example: Slow Subtraction *)
-
-(** The following program subtracts the value of [X] from the value of
- [Y] by repeatedly decrementing both [X] and [Y]. We want to verify its
- correctness with respect to the following specification:
- {{ X = m /\ Y = n }}
- WHILE X <> 0 DO
- Y ::= Y - 1;
- X ::= X - 1
- END
- {{ Y = n - m }}
-
- To verify this program we need to find an invariant [I] for the
- loop. As a first step we can leave [I] as an unknown and build a
- _skeleton_ for the proof by applying backward the rules for local
- consistency. This process leads to the following skeleton:
- (1) {{ X = m /\ Y = n }} ->> (a)
- (2) {{ I }}
- WHILE X <> 0 DO
- (3) {{ I /\ X <> 0 }} ->> (c)
- (4) {{ I[X |-> X-1][Y |-> Y-1] }}
- Y ::= Y - 1;
- (5) {{ I[X |-> X-1] }}
- X ::= X - 1
- (6) {{ I }}
- END
- (7) {{ I /\ ~(X <> 0) }} ->> (b)
- (8) {{ Y = n - m }}
-
- By examining this skeleton, we can see that any valid [I] will
- have to respect three conditions:
- - (a) it must be weak enough to be implied by the loop's
- precondition, i.e. (1) must imply (2);
- - (b) it must be strong enough to imply the loop's postcondition,
- i.e. (7) must imply (8);
- - (c) it must be preserved by one iteration of the loop, i.e. (3)
- must imply (4). *)
-
-(** These conditions are actually independent of the particular
- program and specification we are considering. Indeed, every loop
- invariant has to satisfy them. One way to find an invariant that
- simultaneously satisfies these three conditions is by using an
- iterative process: start with a "candidate" invariant (e.g. a
- guess or a heuristic choice) and check the three conditions above;
- if any of the checks fails, try to use the information that we get
- from the failure to produce another (hopefully better) candidate
- invariant, and repeat the process.
-
- For instance, in the reduce-to-zero example above, we saw that,
- for a very simple loop, choosing [True] as an invariant did the
- job. So let's try it again here! I.e., let's instantiate [I] with
- [True] in the skeleton above see what we get...
- (1) {{ X = m /\ Y = n }} ->> (a - OK)
- (2) {{ True }}
- WHILE X <> 0 DO
- (3) {{ True /\ X <> 0 }} ->> (c - OK)
- (4) {{ True }}
- Y ::= Y - 1;
- (5) {{ True }}
- X ::= X - 1
- (6) {{ True }}
- END
- (7) {{ True /\ X = 0 }} ->> (b - WRONG!)
- (8) {{ Y = n - m }}
-
- While conditions (a) and (c) are trivially satisfied,
- condition (b) is wrong, i.e. it is not the case that (7) [True /\
- X = 0] implies (8) [Y = n - m]. In fact, the two assertions are
- completely unrelated and it is easy to find a counterexample (say,
- [Y = X = m = 0] and [n = 1]).
-
- If we want (b) to hold, we need to strengthen the invariant so
- that it implies the postcondition (8). One very simple way to do
- this is to let the invariant _be_ the postcondition. So let's
- return to our skeleton, instantiate [I] with [Y = n - m], and
- check conditions (a) to (c) again.
- (1) {{ X = m /\ Y = n }} ->> (a - WRONG!)
- (2) {{ Y = n - m }}
- WHILE X <> 0 DO
- (3) {{ Y = n - m /\ X <> 0 }} ->> (c - WRONG!)
- (4) {{ Y - 1 = n - m }}
- Y ::= Y - 1;
- (5) {{ Y = n - m }}
- X ::= X - 1
- (6) {{ Y = n - m }}
- END
- (7) {{ Y = n - m /\ X = 0 }} ->> (b - OK)
- (8) {{ Y = n - m }}
-
- This time, condition (b) holds trivially, but (a) and (c) are
- broken. Condition (a) requires that (1) [X = m /\ Y = n]
- implies (2) [Y = n - m]. If we substitute [Y] by [n] we have to
- show that [n = n - m] for arbitrary [m] and [n], which does not
- hold (for instance, when [m = n = 1]). Condition (c) requires that
- [n - m - 1 = n - m], which fails, for instance, for [n = 1] and [m =
- 0]. So, although [Y = n - m] holds at the end of the loop, it does
- not hold from the start, and it doesn't hold on each iteration;
- it is not a correct invariant.
-
- This failure is not very surprising: the variable [Y] changes
- during the loop, while [m] and [n] are constant, so the assertion
- we chose didn't have much chance of being an invariant!
-
- To do better, we need to generalize (8) to some statement that is
- equivalent to (8) when [X] is [0], since this will be the case
- when the loop terminates, and that "fills the gap" in some
- appropriate way when [X] is nonzero. Looking at how the loop
- works, we can observe that [X] and [Y] are decremented together
- until [X] reaches [0]. So, if [X = 2] and [Y = 5] initially,
- after one iteration of the loop we obtain [X = 1] and [Y = 4];
- after two iterations [X = 0] and [Y = 3]; and then the loop stops.
- Notice that the difference between [Y] and [X] stays constant
- between iterations; initially, [Y = n] and [X = m], so this
- difference is always [n - m]. So let's try instantiating [I] in
- the skeleton above with [Y - X = n - m].
- (1) {{ X = m /\ Y = n }} ->> (a - OK)
- (2) {{ Y - X = n - m }}
- WHILE X <> 0 DO
- (3) {{ Y - X = n - m /\ X <> 0 }} ->> (c - OK)
- (4) {{ (Y - 1) - (X - 1) = n - m }}
- Y ::= Y - 1;
- (5) {{ Y - (X - 1) = n - m }}
- X ::= X - 1
- (6) {{ Y - X = n - m }}
- END
- (7) {{ Y - X = n - m /\ X = 0 }} ->> (b - OK)
- (8) {{ Y = n - m }}
-
- Success! Conditions (a), (b) and (c) all hold now. (To
- verify (c), we need to check that, under the assumption that [X <>
- 0], we have [Y - X = (Y - 1) - (X - 1)]; this holds for all
- natural numbers [X] and [Y].) *)
-
-(* ####################################################### *)
-(** ** Exercise: Slow Assignment *)
-
-
-(** **** Exercise: 2 stars (slow_assignment) *)
-(** A roundabout way of assigning a number currently stored in [X] to
- the variable [Y] is to start [Y] at [0], then decrement [X] until
- it hits [0], incrementing [Y] at each step. Here is a program that
- implements this idea:
- {{ X = m }}
- Y ::= 0;
- WHILE X <> 0 DO
- X ::= X - 1;
- Y ::= Y + 1;
- END
- {{ Y = m }}
- Write an informal decorated program showing that this is correct. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ####################################################### *)
-(** ** Exercise: Slow Addition *)
-
-
-(** **** Exercise: 3 stars, optional (add_slowly_decoration) *)
-(** The following program adds the variable X into the variable Z
- by repeatedly decrementing X and incrementing Z.
- WHILE X <> 0 DO
- Z ::= Z + 1;
- X ::= X - 1
- END
-
- Following the pattern of the [subtract_slowly] example above, pick
- a precondition and postcondition that give an appropriate
- specification of [add_slowly]; then (informally) decorate the
- program accordingly. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ####################################################### *)
-(** ** Example: Parity *)
-
-
-(** Here is a cute little program for computing the parity of the
- value initially stored in [X] (due to Daniel Cristofani).
- {{ X = m }}
- WHILE 2 <= X DO
- X ::= X - 2
- END
- {{ X = parity m }}
- The mathematical [parity] function used in the specification is
- defined in Coq as follows: *)
-
-Fixpoint parity x :=
- match x with
- | 0 => 0
- | 1 => 1
- | S (S x') => parity x'
- end.
-
-(** The postcondition does not hold at the beginning of the loop,
- since [m = parity m] does not hold for an arbitrary [m], so we
- cannot use that as an invariant. To find an invariant that works,
- let's think a bit about what this loop does. On each iteration it
- decrements [X] by [2], which preserves the parity of [X]. So the
- parity of [X] does not change, i.e. it is invariant. The initial
- value of [X] is [m], so the parity of [X] is always equal to the
- parity of [m]. Using [parity X = parity m] as an invariant we
- obtain the following decorated program:
- {{ X = m }} ->> (a - OK)
- {{ parity X = parity m }}
- WHILE 2 <= X DO
- {{ parity X = parity m /\ 2 <= X }} ->> (c - OK)
- {{ parity (X-2) = parity m }}
- X ::= X - 2
- {{ parity X = parity m }}
- END
- {{ parity X = parity m /\ X < 2 }} ->> (b - OK)
- {{ X = parity m }}
-
- With this invariant, conditions (a), (b), and (c) are all
- satisfied. For verifying (b), we observe that, when [X < 2], we
- have [parity X = X] (we can easily see this in the definition of
- [parity]). For verifying (c), we observe that, when [2 <= X], we
- have [parity X = parity (X-2)]. *)
-
-
-(** **** Exercise: 3 stars, optional (parity_formal) *)
-(** Translate this proof to Coq. Refer to the reduce-to-zero example
- for ideas. You may find the following two lemmas useful: *)
-
-Lemma parity_ge_2 : forall x,
- 2 <= x ->
- parity (x - 2) = parity x.
-Proof.
- induction x; intro. reflexivity.
- destruct x. inversion H. inversion H1.
- simpl. rewrite <- minus_n_O. reflexivity.
-Qed.
-
-Lemma parity_lt_2 : forall x,
- ~ 2 <= x ->
- parity (x) = x.
-Proof.
- intros. induction x. reflexivity. destruct x. reflexivity.
- apply ex_falso_quodlibet. apply H. omega.
-Qed.
-
-Theorem parity_correct : forall m,
- {{ fun st => st X = m }}
- WHILE BLe (ANum 2) (AId X) DO
- X ::= AMinus (AId X) (ANum 2)
- END
- {{ fun st => st X = parity m }}.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ####################################################### *)
-(** ** Example: Finding Square Roots *)
-
-
-(** The following program computes the square root of [X]
- by naive iteration:
- {{ X=m }}
- Z ::= 0;
- WHILE (Z+1)*(Z+1) <= X DO
- Z ::= Z+1
- END
- {{ Z*Z<=m /\ m<(Z+1)*(Z+1) }}
-*)
-
-(** As above, we can try to use the postcondition as a candidate
- invariant, obtaining the following decorated program:
- (1) {{ X=m }} ->> (a - second conjunct of (2) WRONG!)
- (2) {{ 0*0 <= m /\ m<1*1 }}
- Z ::= 0;
- (3) {{ Z*Z <= m /\ m<(Z+1)*(Z+1) }}
- WHILE (Z+1)*(Z+1) <= X DO
- (4) {{ Z*Z<=m /\ (Z+1)*(Z+1)<=X }} ->> (c - WRONG!)
- (5) {{ (Z+1)*(Z+1)<=m /\ m<(Z+2)*(Z+2) }}
- Z ::= Z+1
- (6) {{ Z*Z<=m /\ m<(Z+1)*(Z+1) }}
- END
- (7) {{ Z*Z<=m /\ m<(Z+1)*(Z+1) /\ X<(Z+1)*(Z+1) }} ->> (b - OK)
- (8) {{ Z*Z<=m /\ m<(Z+1)*(Z+1) }}
-
- This didn't work very well: both conditions (a) and (c) failed.
- Looking at condition (c), we see that the second conjunct of (4)
- is almost the same as the first conjunct of (5), except that (4)
- mentions [X] while (5) mentions [m]. But note that [X] is never
- assigned in this program, so we should have [X=m], but we didn't
- propagate this information from (1) into the loop invariant.
-
- Also, looking at the second conjunct of (8), it seems quite
- hopeless as an invariant -- and we don't even need it, since we
- can obtain it from the negation of the guard (third conjunct
- in (7)), again under the assumption that [X=m].
-
- So we now try [X=m /\ Z*Z <= m] as the loop invariant:
- {{ X=m }} ->> (a - OK)
- {{ X=m /\ 0*0 <= m }}
- Z ::= 0;
- {{ X=m /\ Z*Z <= m }}
- WHILE (Z+1)*(Z+1) <= X DO
- {{ X=m /\ Z*Z<=m /\ (Z+1)*(Z+1)<=X }} ->> (c - OK)
- {{ X=m /\ (Z+1)*(Z+1)<=m }}
- Z ::= Z+1
- {{ X=m /\ Z*Z<=m }}
- END
- {{ X=m /\ Z*Z<=m /\ X<(Z+1)*(Z+1) }} ->> (b - OK)
- {{ Z*Z<=m /\ m<(Z+1)*(Z+1) }}
-
- This works, since conditions (a), (b), and (c) are now all
- trivially satisfied.
-
- Very often, if a variable is used in a loop in a read-only
- fashion (i.e., it is referred to by the program or by the
- specification and it is not changed by the loop) it is necessary
- to add the fact that it doesn't change to the loop invariant. *)
-
-(* ####################################################### *)
-(** ** Example: Squaring *)
-
-
-(** Here is a program that squares [X] by repeated addition:
-
- {{ X = m }}
- Y ::= 0;
- Z ::= 0;
- WHILE Y <> X DO
- Z ::= Z + X;
- Y ::= Y + 1
- END
- {{ Z = m*m }}
-*)
-
-(** The first thing to note is that the loop reads [X] but doesn't
- change its value. As we saw in the previous example, in such cases
- it is a good idea to add [X = m] to the invariant. The other thing
- we often use in the invariant is the postcondition, so let's add
- that too, leading to the invariant candidate [Z = m * m /\ X = m].
- {{ X = m }} ->> (a - WRONG)
- {{ 0 = m*m /\ X = m }}
- Y ::= 0;
- {{ 0 = m*m /\ X = m }}
- Z ::= 0;
- {{ Z = m*m /\ X = m }}
- WHILE Y <> X DO
- {{ Z = Y*m /\ X = m /\ Y <> X }} ->> (c - WRONG)
- {{ Z+X = m*m /\ X = m }}
- Z ::= Z + X;
- {{ Z = m*m /\ X = m }}
- Y ::= Y + 1
- {{ Z = m*m /\ X = m }}
- END
- {{ Z = m*m /\ X = m /\ Y = X }} ->> (b - OK)
- {{ Z = m*m }}
-
- Conditions (a) and (c) fail because of the [Z = m*m] part. While
- [Z] starts at [0] and works itself up to [m*m], we can't expect
- [Z] to be [m*m] from the start. If we look at how [Z] progesses
- in the loop, after the 1st iteration [Z = m], after the 2nd
- iteration [Z = 2*m], and at the end [Z = m*m]. Since the variable
- [Y] tracks how many times we go through the loop, we derive the
- new invariant candidate [Z = Y*m /\ X = m].
- {{ X = m }} ->> (a - OK)
- {{ 0 = 0*m /\ X = m }}
- Y ::= 0;
- {{ 0 = Y*m /\ X = m }}
- Z ::= 0;
- {{ Z = Y*m /\ X = m }}
- WHILE Y <> X DO
- {{ Z = Y*m /\ X = m /\ Y <> X }} ->> (c - OK)
- {{ Z+X = (Y+1)*m /\ X = m }}
- Z ::= Z + X;
- {{ Z = (Y+1)*m /\ X = m }}
- Y ::= Y + 1
- {{ Z = Y*m /\ X = m }}
- END
- {{ Z = Y*m /\ X = m /\ Y = X }} ->> (b - OK)
- {{ Z = m*m }}
-
- This new invariant makes the proof go through: all three
- conditions are easy to check.
-
- It is worth comparing the postcondition [Z = m*m] and the [Z =
- Y*m] conjunct of the invariant. It is often the case that one has
- to replace auxiliary variabes (parameters) with variables -- or
- with expressions involving both variables and parameters (like
- [m - Y]) -- when going from postconditions to invariants. *)
-
-(* ####################################################### *)
-(** ** Exercise: Factorial *)
-
-(** **** Exercise: 3 stars (factorial) *)
-(** Recall that [n!] denotes the factorial of [n] (i.e. [n! =
- 1*2*...*n]). Here is an Imp program that calculates the factorial
- of the number initially stored in the variable [X] and puts it in
- the variable [Y]:
- {{ X = m }} ;
- Y ::= 1
- WHILE X <> 0
- DO
- Y ::= Y * X
- X ::= X - 1
- END
- {{ Y = m! }}
-
- Fill in the blanks in following decorated program:
- {{ X = m }} ->>
- {{ }}
- Y ::= 1;
- {{ }}
- WHILE X <> 0
- DO {{ }} ->>
- {{ }}
- Y ::= Y * X;
- {{ }}
- X ::= X - 1
- {{ }}
- END
- {{ }} ->>
- {{ Y = m! }}
-*)
-
-
-(** [] *)
-
-
-(* ####################################################### *)
-(** ** Exercise: Min *)
-
-(** **** Exercise: 3 stars (Min_Hoare) *)
-(** Fill in valid decorations for the following program.
- For the => steps in your annotations, you may rely (silently) on the
- following facts about min
-
- Lemma lemma1 : forall x y,
- (x=0 \/ y=0) -> min x y = 0.
- Lemma lemma2 : forall x y,
- min (x-1) (y-1) = (min x y) - 1.
-
- plus, as usual, standard high-school algebra.
-
- {{ True }} ->>
- {{ }}
- X ::= a;
- {{ }}
- Y ::= b;
- {{ }}
- Z ::= 0;
- {{ }}
- WHILE (X <> 0 /\ Y <> 0) DO
- {{ }} ->>
- {{ }}
- X := X - 1;
- {{ }}
- Y := Y - 1;
- {{ }}
- Z := Z + 1;
- {{ }}
- END
- {{ }} ->>
- {{ Z = min a b }}
-*)
-
-
-
-
-(** **** Exercise: 3 stars (two_loops) *)
-(** Here is a very inefficient way of adding 3 numbers:
- X ::= 0;
- Y ::= 0;
- Z ::= c;
- WHILE X <> a DO
- X ::= X + 1;
- Z ::= Z + 1
- END;
- WHILE Y <> b DO
- Y ::= Y + 1;
- Z ::= Z + 1
- END
-
- Show that it does what it should by filling in the blanks in the
- following decorated program.
-
- {{ True }} ->>
- {{ }}
- X ::= 0;
- {{ }}
- Y ::= 0;
- {{ }}
- Z ::= c;
- {{ }}
- WHILE X <> a DO
- {{ }} ->>
- {{ }}
- X ::= X + 1;
- {{ }}
- Z ::= Z + 1
- {{ }}
- END;
- {{ }} ->>
- {{ }}
- WHILE Y <> b DO
- {{ }} ->>
- {{ }}
- Y ::= Y + 1;
- {{ }}
- Z ::= Z + 1
- {{ }}
- END
- {{ }} ->>
- {{ Z = a + b + c }}
-*)
-
-
-(* ####################################################### *)
-(** ** Exercise: Power Series *)
-
-
-(** **** Exercise: 4 stars, optional (dpow2_down) *)
-(** Here is a program that computes the series:
- [1 + 2 + 2^2 + ... + 2^m = 2^(m+1) - 1]
- X ::= 0;
- Y ::= 1;
- Z ::= 1;
- WHILE X <> m DO
- Z ::= 2 * Z;
- Y ::= Y + Z;
- X ::= X + 1;
- END
- Write a decorated program for this. *)
-
-(* FILL IN HERE *)
-
-(* ####################################################### *)
-(** * Weakest Preconditions (Advanced) *)
-
-(** Some Hoare triples are more interesting than others.
- For example,
- {{ False }} X ::= Y + 1 {{ X <= 5 }}
- is _not_ very interesting: although it is perfectly valid, it
- tells us nothing useful. Since the precondition isn't satisfied
- by any state, it doesn't describe any situations where we can use
- the command [X ::= Y + 1] to achieve the postcondition [X <= 5].
-
- By contrast,
- {{ Y <= 4 /\ Z = 0 }} X ::= Y + 1 {{ X <= 5 }}
- is useful: it tells us that, if we can somehow create a situation
- in which we know that [Y <= 4 /\ Z = 0], then running this command
- will produce a state satisfying the postcondition. However, this
- triple is still not as useful as it could be, because the [Z = 0]
- clause in the precondition actually has nothing to do with the
- postcondition [X <= 5]. The _most_ useful triple (for a given
- command and postcondition) is this one:
- {{ Y <= 4 }} X ::= Y + 1 {{ X <= 5 }}
- In other words, [Y <= 4] is the _weakest_ valid precondition of
- the command [X ::= Y + 1] for the postcondition [X <= 5]. *)
-
-(** In general, we say that "[P] is the weakest precondition of
- command [c] for postcondition [Q]" if [{{P}} c {{Q}}] and if,
- whenever [P'] is an assertion such that [{{P'}} c {{Q}}], we have
- [P' st] implies [P st] for all states [st]. *)
-
-Definition is_wp P c Q :=
- {{P}} c {{Q}} /\
- forall P', {{P'}} c {{Q}} -> (P' ->> P).
-
-(** That is, [P] is the weakest precondition of [c] for [Q]
- if (a) [P] _is_ a precondition for [Q] and [c], and (b) [P] is the
- _weakest_ (easiest to satisfy) assertion that guarantees [Q] after
- executing [c]. *)
-
-(** **** Exercise: 1 star, optional (wp) *)
-(** What are the weakest preconditions of the following commands
- for the following postconditions?
- 1) {{ ? }} SKIP {{ X = 5 }}
-
- 2) {{ ? }} X ::= Y + Z {{ X = 5 }}
-
- 3) {{ ? }} X ::= Y {{ X = Y }}
-
- 4) {{ ? }}
- IFB X == 0 THEN Y ::= Z + 1 ELSE Y ::= W + 2 FI
- {{ Y = 5 }}
-
- 5) {{ ? }}
- X ::= 5
- {{ X = 0 }}
-
- 6) {{ ? }}
- WHILE True DO X ::= 0 END
- {{ X = 0 }}
-*)
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced, optional (is_wp_formal) *)
-(** Prove formally using the definition of [hoare_triple] that [Y <= 4]
- is indeed the weakest precondition of [X ::= Y + 1] with respect to
- postcondition [X <= 5]. *)
-
-Theorem is_wp_example :
- is_wp (fun st => st Y <= 4)
- (X ::= APlus (AId Y) (ANum 1)) (fun st => st X <= 5).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, advanced (hoare_asgn_weakest) *)
-(** Show that the precondition in the rule [hoare_asgn] is in fact the
- weakest precondition. *)
-
-Theorem hoare_asgn_weakest : forall Q X a,
- is_wp (Q [X |-> a]) (X ::= a) Q.
-Proof.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, advanced, optional (hoare_havoc_weakest) *)
-(** Show that your [havoc_pre] rule from the [himp_hoare] exercise
- in the [Hoare] chapter returns the weakest precondition. *)
-Module Himp2.
-Import Himp.
-
-Lemma hoare_havoc_weakest : forall (P Q : Assertion) (X : id),
- {{ P }} HAVOC X {{ Q }} ->
- P ->> havoc_pre X Q.
-Proof.
-(* FILL IN HERE *) Admitted.
-End Himp2.
-(** [] *)
-
-(* ####################################################### *)
-(** * Formal Decorated Programs (Advanced) *)
-
-(** The informal conventions for decorated programs amount to a way of
- displaying Hoare triples in which commands are annotated with
- enough embedded assertions that checking the validity of the
- triple is reduced to simple logical and algebraic calculations
- showing that some assertions imply others. In this section, we
- show that this informal presentation style can actually be made
- completely formal and indeed that checking the validity of
- decorated programs can mostly be automated. *)
-
-(** ** Syntax *)
-
-(** The first thing we need to do is to formalize a variant of the
- syntax of commands with embedded assertions. We call the new
- commands _decorated commands_, or [dcom]s. *)
-
-Inductive dcom : Type :=
- | DCSkip : Assertion -> dcom
- | DCSeq : dcom -> dcom -> dcom
- | DCAsgn : id -> aexp -> Assertion -> dcom
- | DCIf : bexp -> Assertion -> dcom -> Assertion -> dcom
- -> Assertion-> dcom
- | DCWhile : bexp -> Assertion -> dcom -> Assertion -> dcom
- | DCPre : Assertion -> dcom -> dcom
- | DCPost : dcom -> Assertion -> dcom.
-
-Tactic Notation "dcom_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "Skip" | Case_aux c "Seq" | Case_aux c "Asgn"
- | Case_aux c "If" | Case_aux c "While"
- | Case_aux c "Pre" | Case_aux c "Post" ].
-
-Notation "'SKIP' {{ P }}"
- := (DCSkip P)
- (at level 10) : dcom_scope.
-Notation "l '::=' a {{ P }}"
- := (DCAsgn l a P)
- (at level 60, a at next level) : dcom_scope.
-Notation "'WHILE' b 'DO' {{ Pbody }} d 'END' {{ Ppost }}"
- := (DCWhile b Pbody d Ppost)
- (at level 80, right associativity) : dcom_scope.
-Notation "'IFB' b 'THEN' {{ P }} d 'ELSE' {{ P' }} d' 'FI' {{ Q }}"
- := (DCIf b P d P' d' Q)
- (at level 80, right associativity) : dcom_scope.
-Notation "'->>' {{ P }} d"
- := (DCPre P d)
- (at level 90, right associativity) : dcom_scope.
-Notation "{{ P }} d"
- := (DCPre P d)
- (at level 90) : dcom_scope.
-Notation "d '->>' {{ P }}"
- := (DCPost d P)
- (at level 80, right associativity) : dcom_scope.
-Notation " d ;; d' "
- := (DCSeq d d')
- (at level 80, right associativity) : dcom_scope.
-
-Delimit Scope dcom_scope with dcom.
-
-(** To avoid clashing with the existing [Notation] definitions
- for ordinary [com]mands, we introduce these notations in a special
- scope called [dcom_scope], and we wrap examples with the
- declaration [% dcom] to signal that we want the notations to be
- interpreted in this scope.
-
- Careful readers will note that we've defined two notations for the
- [DCPre] constructor, one with and one without a [->>]. The
- "without" version is intended to be used to supply the initial
- precondition at the very top of the program. *)
-
-Example dec_while : dcom := (
- {{ fun st => True }}
- WHILE (BNot (BEq (AId X) (ANum 0)))
- DO
- {{ fun st => True /\ st X <> 0}}
- X ::= (AMinus (AId X) (ANum 1))
- {{ fun _ => True }}
- END
- {{ fun st => True /\ st X = 0}} ->>
- {{ fun st => st X = 0 }}
-) % dcom.
-
-(** It is easy to go from a [dcom] to a [com] by erasing all
- annotations. *)
-
-Fixpoint extract (d:dcom) : com :=
- match d with
- | DCSkip _ => SKIP
- | DCSeq d1 d2 => (extract d1 ;; extract d2)
- | DCAsgn X a _ => X ::= a
- | DCIf b _ d1 _ d2 _ => IFB b THEN extract d1 ELSE extract d2 FI
- | DCWhile b _ d _ => WHILE b DO extract d END
- | DCPre _ d => extract d
- | DCPost d _ => extract d
- end.
-
-(** The choice of exactly where to put assertions in the definition of
- [dcom] is a bit subtle. The simplest thing to do would be to
- annotate every [dcom] with a precondition and postcondition. But
- this would result in very verbose programs with a lot of repeated
- annotations: for example, a program like [SKIP;SKIP] would have to
- be annotated as
- {{P}} ({{P}} SKIP {{P}}) ;; ({{P}} SKIP {{P}}) {{P}},
- with pre- and post-conditions on each [SKIP], plus identical pre-
- and post-conditions on the semicolon!
-
- Instead, the rule we've followed is this:
-
- - The _post_-condition expected by each [dcom] [d] is embedded in [d]
-
- - The _pre_-condition is supplied by the context. *)
-
-(** In other words, the invariant of the representation is that a
- [dcom] [d] together with a precondition [P] determines a Hoare
- triple [{{P}} (extract d) {{post d}}], where [post] is defined as
- follows: *)
-
-Fixpoint post (d:dcom) : Assertion :=
- match d with
- | DCSkip P => P
- | DCSeq d1 d2 => post d2
- | DCAsgn X a Q => Q
- | DCIf _ _ d1 _ d2 Q => Q
- | DCWhile b Pbody c Ppost => Ppost
- | DCPre _ d => post d
- | DCPost c Q => Q
- end.
-
-(** Similarly, we can extract the "initial precondition" from a
- decorated program. *)
-
-Fixpoint pre (d:dcom) : Assertion :=
- match d with
- | DCSkip P => fun st => True
- | DCSeq c1 c2 => pre c1
- | DCAsgn X a Q => fun st => True
- | DCIf _ _ t _ e _ => fun st => True
- | DCWhile b Pbody c Ppost => fun st => True
- | DCPre P c => P
- | DCPost c Q => pre c
- end.
-
-(** This function is not doing anything sophisticated like calculating
- a weakest precondition; it just recursively searches for an
- explicit annotation at the very beginning of the program,
- returning default answers for programs that lack an explicit
- precondition (like a bare assignment or [SKIP]). *)
-
-(** Using [pre] and [post], and assuming that we adopt the convention
- of always supplying an explicit precondition annotation at the
- very beginning of our decorated programs, we can express what it
- means for a decorated program to be correct as follows: *)
-
-Definition dec_correct (d:dcom) :=
- {{pre d}} (extract d) {{post d}}.
-
-(** To check whether this Hoare triple is _valid_, we need a way to
- extract the "proof obligations" from a decorated program. These
- obligations are often called _verification conditions_, because
- they are the facts that must be verified to see that the
- decorations are logically consistent and thus add up to a complete
- proof of correctness. *)
-
-(** ** Extracting Verification Conditions *)
-
-(** The function [verification_conditions] takes a [dcom] [d] together
- with a precondition [P] and returns a _proposition_ that, if it
- can be proved, implies that the triple [{{P}} (extract d) {{post d}}]
- is valid. *)
-
-(** It does this by walking over [d] and generating a big
- conjunction including all the "local checks" that we listed when
- we described the informal rules for decorated programs. (Strictly
- speaking, we need to massage the informal rules a little bit to
- add some uses of the rule of consequence, but the correspondence
- should be clear.) *)
-
-Fixpoint verification_conditions (P : Assertion) (d:dcom) : Prop :=
- match d with
- | DCSkip Q =>
- (P ->> Q)
- | DCSeq d1 d2 =>
- verification_conditions P d1
- /\ verification_conditions (post d1) d2
- | DCAsgn X a Q =>
- (P ->> Q [X |-> a])
- | DCIf b P1 d1 P2 d2 Q =>
- ((fun st => P st /\ bassn b st) ->> P1)
- /\ ((fun st => P st /\ ~ (bassn b st)) ->> P2)
- /\ (Q <<->> post d1) /\ (Q <<->> post d2)
- /\ verification_conditions P1 d1
- /\ verification_conditions P2 d2
- | DCWhile b Pbody d Ppost =>
- (* post d is the loop invariant and the initial precondition *)
- (P ->> post d)
- /\ (Pbody <<->> (fun st => post d st /\ bassn b st))
- /\ (Ppost <<->> (fun st => post d st /\ ~(bassn b st)))
- /\ verification_conditions Pbody d
- | DCPre P' d =>
- (P ->> P') /\ verification_conditions P' d
- | DCPost d Q =>
- verification_conditions P d /\ (post d ->> Q)
- end.
-
-(** And now, the key theorem, which states that
- [verification_conditions] does its job correctly. Not
- surprisingly, we need to use each of the Hoare Logic rules at some
- point in the proof. *)
-(** We have used _in_ variants of several tactics before to
- apply them to values in the context rather than the goal. An
- extension of this idea is the syntax [tactic in *], which applies
- [tactic] in the goal and every hypothesis in the context. We most
- commonly use this facility in conjunction with the [simpl] tactic,
- as below. *)
-
-Theorem verification_correct : forall d P,
- verification_conditions P d -> {{P}} (extract d) {{post d}}.
-Proof.
- dcom_cases (induction d) Case; intros P H; simpl in *.
- Case "Skip".
- eapply hoare_consequence_pre.
- apply hoare_skip.
- assumption.
- Case "Seq".
- inversion H as [H1 H2]. clear H.
- eapply hoare_seq.
- apply IHd2. apply H2.
- apply IHd1. apply H1.
- Case "Asgn".
- eapply hoare_consequence_pre.
- apply hoare_asgn.
- assumption.
- Case "If".
- inversion H as [HPre1 [HPre2 [[Hd11 Hd12]
- [[Hd21 Hd22] [HThen HElse]]]]].
- clear H.
- apply IHd1 in HThen. clear IHd1.
- apply IHd2 in HElse. clear IHd2.
- apply hoare_if.
- eapply hoare_consequence_pre; eauto.
- eapply hoare_consequence_post; eauto.
- eapply hoare_consequence_pre; eauto.
- eapply hoare_consequence_post; eauto.
- Case "While".
- inversion H as [Hpre [[Hbody1 Hbody2] [[Hpost1 Hpost2] Hd]]];
- subst; clear H.
- eapply hoare_consequence_pre; eauto.
- eapply hoare_consequence_post; eauto.
- apply hoare_while.
- eapply hoare_consequence_pre; eauto.
- Case "Pre".
- inversion H as [HP Hd]; clear H.
- eapply hoare_consequence_pre. apply IHd. apply Hd. assumption.
- Case "Post".
- inversion H as [Hd HQ]; clear H.
- eapply hoare_consequence_post. apply IHd. apply Hd. assumption.
-Qed.
-
-(** ** Examples *)
-
-(** The propositions generated by [verification_conditions] are fairly
- big, and they contain many conjuncts that are essentially trivial. *)
-
-Eval simpl in (verification_conditions (fun st => True) dec_while).
-(**
-==>
-(((fun _ : state => True) ->> (fun _ : state => True)) /\
- ((fun _ : state => True) ->> (fun _ : state => True)) /\
- (fun st : state => True /\ bassn (BNot (BEq (AId X) (ANum 0))) st) =
- (fun st : state => True /\ bassn (BNot (BEq (AId X) (ANum 0))) st) /\
- (fun st : state => True /\ ~ bassn (BNot (BEq (AId X) (ANum 0))) st) =
- (fun st : state => True /\ ~ bassn (BNot (BEq (AId X) (ANum 0))) st) /\
- (fun st : state => True /\ bassn (BNot (BEq (AId X) (ANum 0))) st) ->>
- (fun _ : state => True) [X |-> AMinus (AId X) (ANum 1)]) /\
-(fun st : state => True /\ ~ bassn (BNot (BEq (AId X) (ANum 0))) st) ->>
-(fun st : state => st X = 0)
-*)
-
-(** In principle, we could certainly work with them using just the
- tactics we have so far, but we can make things much smoother with
- a bit of automation. We first define a custom [verify] tactic
- that applies splitting repeatedly to turn all the conjunctions
- into separate subgoals and then uses [omega] and [eauto] (a handy
- general-purpose automation tactic that we'll discuss in detail
- later) to deal with as many of them as possible. *)
-
-Lemma ble_nat_true_iff : forall n m : nat,
- ble_nat n m = true <-> n <= m.
-Proof.
- intros n m. split. apply ble_nat_true.
- generalize dependent m. induction n; intros m H. reflexivity.
- simpl. destruct m. inversion H.
- apply le_S_n in H. apply IHn. assumption.
-Qed.
-
-Lemma ble_nat_false_iff : forall n m : nat,
- ble_nat n m = false <-> ~(n <= m).
-Proof.
- intros n m. split. apply ble_nat_false.
- generalize dependent m. induction n; intros m H.
- apply ex_falso_quodlibet. apply H. apply le_0_n.
- simpl. destruct m. reflexivity.
- apply IHn. intro Hc. apply H. apply le_n_S. assumption.
-Qed.
-
-Tactic Notation "verify" :=
- apply verification_correct;
- repeat split;
- simpl; unfold assert_implies;
- unfold bassn in *; unfold beval in *; unfold aeval in *;
- unfold assn_sub; intros;
- repeat rewrite update_eq;
- repeat (rewrite update_neq; [| (intro X; inversion X)]);
- simpl in *;
- repeat match goal with [H : _ /\ _ |- _] => destruct H end;
- repeat rewrite not_true_iff_false in *;
- repeat rewrite not_false_iff_true in *;
- repeat rewrite negb_true_iff in *;
- repeat rewrite negb_false_iff in *;
- repeat rewrite beq_nat_true_iff in *;
- repeat rewrite beq_nat_false_iff in *;
- repeat rewrite ble_nat_true_iff in *;
- repeat rewrite ble_nat_false_iff in *;
- try subst;
- repeat
- match goal with
- [st : state |- _] =>
- match goal with
- [H : st _ = _ |- _] => rewrite -> H in *; clear H
- | [H : _ = st _ |- _] => rewrite <- H in *; clear H
- end
- end;
- try eauto; try omega.
-
-(** What's left after [verify] does its thing is "just the interesting
- parts" of checking that the decorations are correct. For very
- simple examples [verify] immediately solves the goal (provided
- that the annotations are correct). *)
-
-Theorem dec_while_correct :
- dec_correct dec_while.
-Proof. verify. Qed.
-
-(** Another example (formalizing a decorated program we've seen
- before): *)
-
-Example subtract_slowly_dec (m:nat) (p:nat) : dcom := (
- {{ fun st => st X = m /\ st Z = p }} ->>
- {{ fun st => st Z - st X = p - m }}
- WHILE BNot (BEq (AId X) (ANum 0))
- DO {{ fun st => st Z - st X = p - m /\ st X <> 0 }} ->>
- {{ fun st => (st Z - 1) - (st X - 1) = p - m }}
- Z ::= AMinus (AId Z) (ANum 1)
- {{ fun st => st Z - (st X - 1) = p - m }} ;;
- X ::= AMinus (AId X) (ANum 1)
- {{ fun st => st Z - st X = p - m }}
- END
- {{ fun st => st Z - st X = p - m /\ st X = 0 }} ->>
- {{ fun st => st Z = p - m }}
-) % dcom.
-
-Theorem subtract_slowly_dec_correct : forall m p,
- dec_correct (subtract_slowly_dec m p).
-Proof. intros m p. verify. (* this grinds for a bit! *) Qed.
-
-(** **** Exercise: 3 stars, advanced (slow_assignment_dec) *)
-
-(** In the [slow_assignment] exercise above, we saw a roundabout way
- of assigning a number currently stored in [X] to the variable [Y]:
- start [Y] at [0], then decrement [X] until it hits [0],
- incrementing [Y] at each step.
-
- Write a _formal_ version of this decorated program and prove it
- correct. *)
-
-Example slow_assignment_dec (m:nat) : dcom :=
-(* FILL IN HERE *) admit.
-
-Theorem slow_assignment_dec_correct : forall m,
- dec_correct (slow_assignment_dec m).
-Proof. (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (factorial_dec) *)
-(** Remember the factorial function we worked with before: *)
-
-Fixpoint real_fact (n:nat) : nat :=
- match n with
- | O => 1
- | S n' => n * (real_fact n')
- end.
-
-(** Following the pattern of [subtract_slowly_dec], write a decorated
- program that implements the factorial function and prove it
- correct. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-
-(* $Date: 2014-04-03 23:55:40 -0400 (Thu, 03 Apr 2014) $ *)
-
diff --git a/Imp.html b/Imp.html
deleted file mode 100644
index 3219f9a..0000000
--- a/Imp.html
+++ /dev/null
@@ -1,3091 +0,0 @@
-
-
-
-
-
-Imp: Simple Imperative Programs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ImpSimple Imperative Programs
-
-
-
-
-
-
-
-
- In this chapter, we begin a new direction that will continue for
- the rest of the course. Up to now most of our attention has been
- focused on various aspects of Coq itself, while from now on we'll
- mostly be using Coq to formalize other things. (We'll continue to
- pause from time to time to introduce a few additional aspects of
- Coq.)
-
-
-
- Our first case study is a simple imperative programming language
- called Imp, embodying a tiny core fragment of conventional
- mainstream languages such as C and Java. Here is a familiar
- mathematical function written in Imp.
-
-
-
-
- Z ::= X;;
- Y ::= 1;;
- WHILEnot (Z = 0) DO
- Y ::= Y × Z;;
- Z ::= Z - 1
- END
-
-
-
-
-
-
- This chapter looks at how to define the syntax and semantics
- of Imp; the chapters that follow develop a theory of program
- equivalence and introduce Hoare Logic, a widely used logic for
- reasoning about imperative programs.
-
-
-
-
-
-
-
-
Sflib
-
-
-
- A minor technical point: Instead of asking Coq to import our
- earlier definitions from chapter Logic, we import a small library
- called Sflib.v, containing just a few definitions and theorems
- from earlier chapters that we'll actually use in the rest of the
- course. This change should be nearly invisible, since most of what's
- missing from Sflib has identical definitions in the Coq standard
- library. The main reason for doing it is to tidy the global Coq
- environment so that, for example, it is easier to search for
- relevant theorems.
-
-
-
-
-RequireExportSfLib.
-
-
-
-
-
-
Arithmetic and Boolean Expressions
-
-
-
- We'll present Imp in three parts: first a core language of
- arithmetic and boolean expressions, then an extension of these
- expressions with variables, and finally a language of commands
- including assignment, conditions, sequencing, and loops.
-
-
-
-
-
-
-
-
Syntax
-
-
-
-
-
-ModuleAExp.
-
-
-
-
-
-These two definitions specify the abstract syntax of
- arithmetic and boolean expressions.
-
-In this chapter, we'll elide the translation from the
- concrete syntax that a programmer would actually write to these
- abstract syntax trees — the process that, for example, would
- translate the string "1+2×3" to the AST APlus(ANum
- 1)(AMult(ANum2)(ANum3)). The optional chapter ImpParser
- develops a simple implementation of a lexical analyzer and parser
- that can perform this translation. You do not need to
- understand that file to understand this one, but if you haven't
- taken a course where these techniques are covered (e.g., a
- compilers course) you may want to skim it.
-
-
-
- For comparison, here's a conventional BNF (Backus-Naur Form)
- grammar defining the same abstract syntax:
-
-
-
-
- a ::= nat
- | a + a
- | a - a
- | a × a
-
- b ::= true
- | false
- | a = a
- | a ≤ a
- | notb
- | bandb
-
-
-
-
-
-
- Compared to the Coq version above...
-
-
-
-
-
The BNF is more informal — for example, it gives some
- suggestions about the surface syntax of expressions (like the
- fact that the addition operation is written + and is an
- infix symbol) while leaving other aspects of lexical analysis
- and parsing (like the relative precedence of +, -, and
- ×) unspecified. Some additional information — and human
- intelligence — would be required to turn this description
- into a formal definition (when implementing a compiler, for
- example).
-
-
-
- The Coq version consistently omits all this information and
- concentrates on the abstract syntax only.
-
-
-
-
-
-
On the other hand, the BNF version is lighter and
- easier to read. Its informality makes it flexible, which is
- a huge advantage in situations like discussions at the
- blackboard, where conveying general ideas is more important
- than getting every detail nailed down precisely.
-
-
-
- Indeed, there are dozens of BNF-like notations and people
- switch freely among them, usually without bothering to say which
- form of BNF they're using because there is no need to: a
- rough-and-ready informal understanding is all that's
- needed.
-
-
-
-
-
- It's good to be comfortable with both sorts of notations:
- informal ones for communicating between humans and formal ones for
- carrying out implementations and proofs.
-
-
-
-
-
-
-
-
Evaluation
-
-
-
- Evaluating an arithmetic expression produces a number.
-
-
- We haven't defined very much yet, but we can already get
- some mileage out of the definitions. Suppose we define a function
- that takes an arithmetic expression and slightly simplifies it,
- changing every occurrence of 0+e (i.e., (APlus(ANum0)e)
- into just e.
-
-But if we want to be sure the optimization is correct —
- i.e., that evaluating an optimized expression gives the same
- result as the original — we should prove it.
-
-
- The repetition in this last proof is starting to be a little
- annoying. If either the language of arithmetic expressions or the
- optimization being proved sound were significantly more complex,
- it would begin to be a real problem.
-
-
-
- So far, we've been doing all our proofs using just a small handful
- of Coq's tactics and completely ignoring its powerful facilities
- for constructing parts of proofs automatically. This section
- introduces some of these facilities, and we will see more over the
- next several chapters. Getting used to them will take some
- energy — Coq's automation is a power tool — but it will allow us
- to scale up our efforts to more complex definitions and more
- interesting properties without becoming overwhelmed by boring,
- repetitive, low-level details.
-
-
-
-
-
-
-
-
Tacticals
-
-
-
- Tacticals is Coq's term for tactics that take other tactics as
- arguments — "higher-order tactics," if you will.
-
-
-
-
-
-
-
-
The repeat Tactical
-
-
-
- The repeat tactical takes another tactic and keeps applying
- this tactic until the tactic fails. Here is an example showing
- that 100 is even using repeat.
-
-The repeatT tactic never fails; if the tactic T doesn't apply
- to the original goal, then repeat still succeeds without changing
- the original goal (it repeats zero times).
-
-
-
-
-Theoremev100' : ev 100.
-Proof.
- repeat (applyev_0). (* doesn't fail, applies ev_0 zero times *)
- repeat (applyev_SS). applyev_0. (* we can continue the proof *)
-Qed.
-
-
-
-
-
-The repeatT tactic does not have any bound on the number of
- times it applies T. If T is a tactic that always succeeds then
- repeat T will loop forever (e.g. repeatsimpl loops forever
- since simpl always succeeds). While Coq's term language is
- guaranteed to terminate, Coq's tactic language is not!
-
-
-
-
-
-
-
-
The try Tactical
-
-
-
- If T is a tactic, then tryT is a tactic that is just like T
- except that, if T fails, tryTsuccessfully does nothing at
- all (instead of failing).
-
-
-
-
-Theoremsilly1 : ∀ae, aevalae = aevalae.
-Proof. tryreflexivity. (* this just does reflexivity *)Qed.
-
-
-Theoremsilly2 : ∀(P : Prop), P→P.
-Proof.
- introsPHP.
- tryreflexivity. (* just reflexivity would have failed *)
- applyHP. (* we can still finish the proof in some other way *)
-Qed.
-
-
-
-
-
-Using try in a completely manual proof is a bit silly, but
- we'll see below that try is very useful for doing automated
- proofs in conjunction with the ; tactical.
-
-
-
-
-
-
-
-
The ; Tactical (Simple Form)
-
-
-
- In its most commonly used form, the ; tactical takes two tactics
- as argument: T;T' first performs the tactic T and then
- performs the tactic T' on each subgoal generated by T.
-
-
- For example, consider the following trivial lemma:
-
-
-
-
-Lemmafoo : ∀n, ble_nat 0 n = true.
-Proof.
- intros.
- destructn.
- (* Leaves two subgoals, which are discharged identically... *)
- Case "n=0". simpl. reflexivity.
- Case "n=Sn'". simpl. reflexivity.
-Qed.
-
-
-
-
-
-We can simplify this proof using the ; tactical:
-
-
-
-
-Lemmafoo' : ∀n, ble_nat 0 n = true.
-Proof.
- intros.
- destructn; (* destruct the current goal *)
- simpl; (* then simpl each resulting subgoal *)
- reflexivity. (* and do reflexivity on each resulting subgoal *)
-Qed.
-
-
-
-
-
-Using try and ; together, we can get rid of the repetition in
- the proof that was bothering us a little while ago.
-
-
-
-
-Theoremoptimize_0plus_sound': ∀a,
- aeval (optimize_0plusa) = aevala.
-Proof.
- introsa.
- inductiona;
- (* Most cases follow directly by the IH *)
- try (simpl; rewriteIHa1; rewriteIHa2; reflexivity).
- (* The remaining cases -- ANum and APlus -- are different *)
- Case "ANum". reflexivity.
- Case "APlus".
- destructa1;
- (* Again, most cases follow directly by the IH *)
- try (simpl; simplinIHa1; rewriteIHa1;
- rewriteIHa2; reflexivity).
- (* The interesting case, on which the try... does nothing,
- is when e1=ANumn. In this case, we have to destruct
- n (to see whether the optimization applies) and rewrite
- with the induction hypothesis. *)
- SCase "a1 = ANum n". destructn;
- simpl; rewriteIHa2; reflexivity. Qed.
-
-
-
-
-
-Coq experts often use this "...;try... " idiom after a tactic
- like induction to take care of many similar cases all at once.
- Naturally, this practice has an analog in informal proofs.
-
-
-
- Here is an informal proof of this theorem that matches the
- structure of the formal one:
-
-
-
- Theorem: For all arithmetic expressions a,
-
-
-
-
- aeval (optimize_0plusa) = aevala.
-
-
-
- Proof: By induction on a. The AMinus and AMult cases
- follow directly from the IH. The remaining cases are as follows:
-
-
- Consider the possible forms of a1. For most of them,
- optimize_0plus simply calls itself recursively for the
- subexpressions and rebuilds a new expression of the same form
- as a1; in these cases, the result follows directly from the
- IH.
-
-
-
- The interesting case is when a1=ANumn for some n.
- If n=ANum0, then
-
-
-
-
- optimize_0plus (APlusa1a2) = optimize_0plusa2
-
-
-
- and the IH for a2 is exactly what we need. On the other
- hand, if n=Sn' for some n', then again optimize_0plus
- simply calls itself recursively, and the result follows from
- the IH. ☐
-
-
-
-
-
- This proof can still be improved: the first case (for a=ANum
- n) is very trivial — even more trivial than the cases that we
- said simply followed from the IH — yet we have chosen to write it
- out in full. It would be better and clearer to drop it and just
- say, at the top, "Most cases are either immediate or direct from
- the IH. The only interesting case is the one for APlus..." We
- can make the same improvement in our formal proof too. Here's how
- it looks:
-
-
-
-
-Theoremoptimize_0plus_sound'': ∀a,
- aeval (optimize_0plusa) = aevala.
-Proof.
- introsa.
- inductiona;
- (* Most cases follow directly by the IH *)
- try (simpl; rewriteIHa1; rewriteIHa2; reflexivity);
- (* ... or are immediate by definition *)
- tryreflexivity.
- (* The interesting case is when a = APlus a1 a2. *)
- Case "APlus".
- destructa1; try (simpl; simplinIHa1; rewriteIHa1;
- rewriteIHa2; reflexivity).
- SCase "a1 = ANum n". destructn;
- simpl; rewriteIHa2; reflexivity. Qed.
-
-
-
-
-
-
The ; Tactical (General Form)
-
-
-
- The ; tactical has a more general than the simple T;T' we've
- seen above, which is sometimes also useful. If T, T1, ...,
- Tn are tactics, then
-
-
-
-
- T; [T1 | T2 | ... | Tn]
-
-
-
- is a tactic that first performs T and then performs T1 on the
- first subgoal generated by T, performs T2 on the second
- subgoal, etc.
-
-
-
- So T;T' is just special notation for the case when all of the
- Ti's are the same tactic; i.e. T;T' is just a shorthand for:
-
-
-
-
- T; [T' | T' | ... | T']
-
-
-
-
-
-
-
-
-
-
-
-
Defining New Tactic Notations
-
-
-
- Coq also provides several ways of "programming" tactic scripts.
-
-
-
-
-
The TacticNotation idiom illustrated below gives a handy
- way to define "shorthand tactics" that bundle several tactics
- into a single command.
-
-
-
-
-
-
For more sophisticated programming, Coq offers a small
- built-in programming language called Ltac with primitives
- that can examine and modify the proof state. The details are
- a bit too complicated to get into here (and it is generally
- agreed that Ltac is not the most beautiful part of Coq's
- design!), but they can be found in the reference manual, and
- there are many examples of Ltac definitions in the Coq
- standard library that you can use as examples.
-
-
-
-
-
-
There is also an OCaml API, which can be used to build tactics
- that access Coq's internal structures at a lower level, but
- this is seldom worth the trouble for ordinary Coq users.
-
-
-
-
-
-
-The TacticNotation mechanism is the easiest to come to grips with,
-and it offers plenty of power for many purposes. Here's an example.
-
-
-This defines a new tactical called simpl_and_try which
- takes one tactic c as an argument, and is defined to be
- equivalent to the tactic simpl;tryc. For example, writing
- "simpl_and_tryreflexivity." in a proof would be the same as
- writing "simpl;tryreflexivity."
-
-
- The next subsection gives a more sophisticated use of this
- feature...
-
-
-
-
-
-
-
-
Bulletproofing Case Analyses
-
-
-
- Being able to deal with most of the cases of an induction
- or destruct all at the same time is very convenient, but it can
- also be a little confusing. One problem that often comes up is
- that maintaining proofs written in this style can be difficult.
- For example, suppose that, later, we extended the definition of
- aexp with another constructor that also required a special
- argument. The above proof might break because Coq generated the
- subgoals for this constructor before the one for APlus, so that,
- at the point when we start working on the APlus case, Coq is
- actually expecting the argument for a completely different
- constructor. What we'd like is to get a sensible error message
- saying "I was expecting the AFoo case at this point, but the
- proof script is talking about APlus." Here's a nice trick (due
- to Aaron Bohannon) that smoothly achieves this.
-
-(Case_aux implements the common functionality of Case,
- SCase, SSCase, etc. For example, Case"foo" is defined as
- Case_auxCase"foo".)
-
-
- For example, if a is a variable of type aexp, then doing
-
-
-
-
- aexp_cases (inductiona) Case
-
-
-
- will perform an induction on a (the same as if we had just typed
- inductiona) and also add a Case tag to each subgoal
- generated by the induction, labeling which constructor it comes
- from. For example, here is yet another proof of
- optimize_0plus_sound, using aexp_cases:
-
-
-
-
-Theoremoptimize_0plus_sound''': ∀a,
- aeval (optimize_0plusa) = aevala.
-Proof.
- introsa.
- aexp_cases (inductiona) Case;
- try (simpl; rewriteIHa1; rewriteIHa2; reflexivity);
- tryreflexivity.
- (* At this point, there is already an "APlus" case name
- in the context. The Case"APlus" here in the proof
- text has the effect of a sanity check: if the "Case"
- string in the context is anything _other_ than "APlus"
- (for example, because we added a clause to the definition
- of aexp and forgot to change the proof) we'll get a
- helpful error at this point telling us that this is now
- the wrong case. *)
- Case "APlus".
- aexp_cases (destructa1) SCase;
- try (simpl; simplinIHa1;
- rewriteIHa1; rewriteIHa2; reflexivity).
- SCase "ANum". destructn;
- simpl; rewriteIHa2; reflexivity. Qed.
-
-
-
-
-
-
Exercise: 3 stars (optimize_0plus_b)
- Since the optimize_0plus tranformation doesn't change the value
- of aexps, we should be able to apply it to all the aexps that
- appear in a bexp without changing the bexp's value. Write a
- function which performs that transformation on bexps, and prove
- it is sound. Use the tacticals we've just seen to make the proof
- as elegant as possible.
-
-
-
-
-Fixpointoptimize_0plus_b (b : bexp) : bexp :=
- (* FILL IN HERE *)admit.
-
-
-Theoremoptimize_0plus_b_sound : ∀b,
- beval (optimize_0plus_bb) = bevalb.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 4 stars, optional (optimizer)
- Design exercise: The optimization implemented by our
- optimize_0plus function is only one of many imaginable
- optimizations on arithmetic and boolean expressions. Write a more
- sophisticated optimizer and prove it correct.
-
-
-
-(* FILL IN HERE *)
- ☐
-
-
-
-
-
-
-
-
The omega Tactic
-
-
-
- The omega tactic implements a decision procedure for a subset of
- first-order logic called Presburger arithmetic. It is based on
- the Omega algorithm invented in 1992 by William Pugh.
-
-
-
- If the goal is a universally quantified formula made out of
-
-
-
-
-
numeric constants, addition (+ and S), subtraction (-
- and pred), and multiplication by constants (this is what
- makes it Presburger arithmetic),
-
-
-
-
-
-
equality (= and ≠) and inequality (≤), and
-
-
-
-
-
-
the logical connectives ∧, ∨, ¬, and →,
-
-
-
-
-
-
- then invoking omega will either solve the goal or tell you that
- it is actually false.
-
-
-
-
-Examplesilly_presburger_example : ∀mnop,
- m + n ≤ n + o∧o + 3 = p + 3 →
- m ≤ p.
-Proof.
- intros. omega.
-Qed.
-
-
-
-
-
-Liebniz wrote, "It is unworthy of excellent men to lose
- hours like slaves in the labor of calculation which could be
- relegated to anyone else if machines were used." We recommend
- using the omega tactic whenever possible.
-
-
-
-
-
-
-
-
A Few More Handy Tactics
-
-
-
- Finally, here are some miscellaneous tactics that you may find
- convenient.
-
-
-
-
-
clearH: Delete hypothesis H from the context.
-
-
-
-
-
-
substx: Find an assumption x=e or e=x in the
- context, replace x with e throughout the context and
- current goal, and clear the assumption.
-
-
-
-
-
-
subst: Substitute away all assumptions of the form x=e
- or e=x.
-
-
-
-
-
-
rename...into...: Change the name of a hypothesis in the
- proof context. For example, if the context includes a variable
- named x, then renamexintoy will change all occurrences
- of x to y.
-
-
-
-
-
-
assumption: Try to find a hypothesis H in the context that
- exactly matches the goal; if one is found, behave just like
- applyH.
-
-
-
-
-
-
contradiction: Try to find a hypothesis H in the current
- context that is logically equivalent to False. If one is
- found, solve the goal.
-
-
-
-
-
-
constructor: Try to find a constructor c (from some
- Inductive definition in the current environment) that can be
- applied to solve the current goal. If one is found, behave
- like applyc.
-
-
-
-
-
- We'll see many examples of these in the proofs below.
-
-
-
-
-
-
-
-
Evaluation as a Relation
-
-
-
- We have presented aeval and beval as functions defined by
- Fixpoints. Another way to think about evaluation — one that we
- will see is often more flexible — is as a relation between
- expressions and their values. This leads naturally to Inductive
- definitions like the following one for arithmetic
- expressions...
-
-As is often the case with relations, we'll find it
- convenient to define infix notation for aevalR. We'll write e
- ⇓n to mean that arithmetic expression e evaluates to value
- n. (This notation is one place where the limitation to ASCII
- symbols becomes a little bothersome. The standard notation for
- the evaluation relation is a double down-arrow. We'll typeset it
- like this in the HTML version of the notes and use a double
- vertical bar as the closest approximation in .v files.)
-
-In fact, Coq provides a way to use this notation in the definition
- of aevalR itself. This avoids situations where we're working on
- a proof involving statements in the form e⇓n but we have to
- refer back to a definition written using the form aevalRen.
-
-
-
- We do this by first "reserving" the notation, then giving the
- definition together with a declaration of what the notation
- means.
-
-
- In informal discussions, it is convenient write the rules for
- aevalR and similar relations in the more readable graphical form
- of inference rules, where the premises above the line justify
- the conclusion below the line (we have already seen them in the
- Prop chapter).
-
- ...would be written like this as an inference rule:
-
-
-
e1 ⇓ n1
-
-
-
-
e2 ⇓ n2
-
- (E_APlus)
-
-
-
-
-
-
APlus e1 e2 ⇓ n1+n2
-
-
-
-
-
- Formally, there is nothing very deep about inference rules:
- they are just implications. You can read the rule name on the
- right as the name of the constructor and read each of the
- linebreaks between the premises above the line and the line itself
- as →. All the variables mentioned in the rule (e1, n1,
- etc.) are implicitly bound by universal quantifiers at the
- beginning. (Such variables are often called metavariables to
- distinguish them from the variables of the language we are
- defining. At the moment, our arithmetic expressions don't include
- variables, but we'll soon be adding them.) The whole collection
- of rules is understood as being wrapped in an Inductive
- declaration (informally, this is either elided or else indicated
- by saying something like "Let aevalR be the smallest relation
- closed under the following rules...").
-
-
- For example, ⇓ is the smallest relation closed under these
- rules:
-
-
-
-
- (E_ANum)
-
-
-
-
-
-
ANum n ⇓ n
-
-
-
-
-
e1 ⇓ n1
-
-
-
-
e2 ⇓ n2
-
- (E_APlus)
-
-
-
-
-
-
APlus e1 e2 ⇓ n1+n2
-
-
-
-
-
e1 ⇓ n1
-
-
-
-
e2 ⇓ n2
-
- (E_AMinus)
-
-
-
-
-
-
AMinus e1 e2 ⇓ n1-n2
-
-
-
-
-
e1 ⇓ n1
-
-
-
-
e2 ⇓ n2
-
- (E_AMult)
-
-
-
-
-
-
AMult e1 e2 ⇓ n1*n2
-
-
-
-
-
-
-
-
-
-
-
Equivalence of the Definitions
-
-
-
- It is straightforward to prove that the relational and functional
- definitions of evaluation agree on all possible arithmetic
- expressions...
-
-Note: if you're reading the HTML file, you'll see an empty square box instead
-of a proof for this theorem.
-You can click on this box to "unfold" the text to see the proof.
-Click on the unfolded to text to "fold" it back up to a box. We'll be using
-this style frequently from now on to help keep the HTML easier to read.
-The full proofs always appear in the .v files.
-
-
- We can make the proof quite a bit shorter by making more
- use of tacticals...
-
-
-
-
-Theoremaeval_iff_aevalR' : ∀an,
- (a⇓n) ↔aevala = n.
-Proof.
- (* WORKED IN CLASS *)
- split.
- Case "→".
- introsH; inductionH; subst; reflexivity.
- Case "←".
- generalizedependentn.
- inductiona; simpl; intros; subst; constructor;
- tryapplyIHa1; tryapplyIHa2; reflexivity.
-Qed.
-
-
-
-
-
-
Exercise: 3 stars (bevalR)
- Write a relation bevalR in the same style as
- aevalR, and prove that it is equivalent to beval.
-
-
-
-
-(*
-Inductive bevalR:
-(* FILL IN HERE *)
-*)
-
-
-
-☐
-
-
-
-
-
-
-
-
Computational vs. Relational Definitions
-
-
-
- For the definitions of evaluation for arithmetic and boolean
- expressions, the choice of whether to use functional or relational
- definitions is mainly a matter of taste. In general, Coq has
- somewhat better support for working with relations. On the other
- hand, in some sense function definitions carry more information,
- because functions are necessarily deterministic and defined on all
- arguments; for a relation we have to show these properties
- explicitly if we need them. Functions also take advantage of Coq's
- computations mechanism.
-
-
-
- However, there are circumstances where relational definitions of
- evaluation are preferable to functional ones.
-
-
-
-
-ModuleaevalR_division.
-
-
-
-
-
-For example, suppose that we wanted to extend the arithmetic
- operations by considering also a division operation:
-
-Extending the definition of aeval to handle this new operation
- would not be straightforward (what should we return as the result
- of ADiv(ANum5)(ANum0)?). But extending aevalR is
- straightforward.
-
-Again, extending aeval would be tricky (because evaluation is
- not a deterministic function from expressions to numbers), but
- extending aevalR is no problem:
-
-
- Let's turn our attention back to defining Imp. The next thing we
- need to do is to enrich our arithmetic and boolean expressions
- with variables. To keep things simple, we'll assume that all
- variables are global and that they only hold numbers.
-
-
-
-
-
-
-
-
Identifiers
-
-
-
- To begin, we'll need to formalize identifiers such as program
- variables. We could use strings for this — or, in a real
- compiler, fancier structures like pointers into a symbol table.
- But for simplicity let's just use natural numbers as identifiers.
-
-
- (We hide this section in a module because these definitions are
- actually in SfLib, but we want to repeat them here so that we
- can explain them.)
-
-
-
-
-ModuleId.
-
-
-
-
-
-We define a new inductive datatype Id so that we won't confuse
- identifiers and numbers. We use sumbool to define a computable
- equality operator on Id.
-
-The following lemmas will be useful for rewriting terms involving eq_id_dec.
-
-
-
-
-Lemmaeq_id : ∀(T:Type) x (pq:T),
- (ifeq_id_decxxthenpelseq) = p.
-Proof.
- intros.
- destruct (eq_id_decxx).
- Case "x = x".
- reflexivity.
- Case "x ≠ x (impossible)".
- applyex_falso_quodlibet; applyn; reflexivity. Qed.
-
-
-
-
-
-
Exercise: 1 star, optional (neq_id)
-
-
-
-Lemmaneq_id : ∀(T:Type) xy (pq:T), x ≠ y→
- (ifeq_id_decxythenpelseq) = q.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-EndId.
-
-
-
-
-
-
States
-
-
-
- A state represents the current values of all the variables at
- some point in the execution of a program. For simplicity (to avoid dealing with partial functions), we
- let the state be defined for all variables, even though any
- given program is only going to mention a finite number of them.
- The state captures all of the information stored in memory. For Imp
- programs, because each variable stores only a natural number, we
- can represent the state as a mapping from identifiers to nat.
- For more complex programming languages, the state might have more
- structure.
-
-
-Defining a few variable names as notational shorthands will make
- examples easier to read:
-
-
-
-
-DefinitionX : id := Id 0.
-DefinitionY : id := Id 1.
-DefinitionZ : id := Id 2.
-
-
-
-
-
-(This convention for naming program variables (X, Y,
- Z) clashes a bit with our earlier use of uppercase letters for
- types. Since we're not using polymorphism heavily in this part of
- the course, this overloading should not cause confusion.)
-
-
- The definition of bexps is the same as before (using the new
- aexps):
-
-As usual, we can use a few Notation declarations to make things
- more readable. We need to be a bit careful to avoid conflicts
- with Coq's built-in notations, so we'll keep this light — in
- particular, we won't introduce any notations for aexps and
- bexps to avoid confusion with the numerical and boolean
- operators we've already defined. We use the keyword IFB for
- conditionals instead of IF, for similar reasons.
-
-
- Next we need to define what it means to evaluate an Imp command.
- The fact that WHILE loops don't necessarily terminate makes defining
- an evaluation function tricky...
-
-
-
-
-
-
-
-
Evaluation as a Function (Failed Attempt)
-
-
-
- Here's an attempt at defining an evaluation function for commands,
- omitting the WHILE case.
-
-In a traditional functional programming language like ML or
- Haskell we could write the WHILE case as follows:
-
- Fixpoint ceval_fun (st : state) (c : com) : state :=
- match c with
- ...
- | WHILE b DO c END =>
- if (beval st b1)
- then ceval_fun st (c1; WHILE b DO c END)
- else st
- end.
-
- Coq doesn't accept such a definition ("Error: Cannot guess
- decreasing argument of fix") because the function we want to
- define is not guaranteed to terminate. Indeed, it doesn't always
- terminate: for example, the full version of the ceval_fun
- function applied to the loop program above would never
- terminate. Since Coq is not just a functional programming
- language, but also a consistent logic, any potentially
- non-terminating function needs to be rejected. Here is
- an (invalid!) Coq program showing what would go wrong if Coq
- allowed non-terminating recursive functions:
-
- That is, propositions like False would become provable
- (e.g. loop_false0 would be a proof of False), which
- would be a disaster for Coq's logical consistency.
-
-
-
- Thus, because it doesn't terminate on all inputs, the full version
- of ceval_fun cannot be written in Coq — at least not without
- additional tricks (see chapter ImpCEvalFun if curious).
-
-
-
-
-
-
-
-
Evaluation as a Relation
-
-
-
- Here's a better way: we define ceval as a relation rather than
- a function — i.e., we define it in Prop instead of Type, as
- we did for aevalR above.
-
-
- This is an important change. Besides freeing us from the awkward
- workarounds that would be needed to define evaluation as a
- function, it gives us a lot more flexibility in the definition.
- For example, if we added concurrency features to the language,
- we'd want the definition of evaluation to be non-deterministic —
- i.e., not only would it not be total, it would not even be a
- partial function! We'll use the notation c/st⇓st' for our ceval relation:
- c/st⇓st' means that executing program c in a starting
- state st results in an ending state st'. This can be
- pronounced "c takes state st to st'".
-
-
-
-
Operational Semantics
-
-
-
-
-
- (E_Skip)
-
-
-
-
-
-
SKIP / st ⇓ st
-
-
-
-
-
aeval st a1 = n
-
- (E_Ass)
-
-
-
-
-
-
x := a1 / st ⇓ (update st x n)
-
-
-
-
-
c1 / st ⇓ st'
-
-
-
-
c2 / st' ⇓ st''
-
- (E_Seq)
-
-
-
-
-
-
c1;;c2 / st ⇓ st''
-
-
-
-
-
beval st b1 = true
-
-
-
-
c1 / st ⇓ st'
-
- (E_IfTrue)
-
-
-
-
-
-
IF b1 THEN c1 ELSE c2 FI / st ⇓ st'
-
-
-
-
-
beval st b1 = false
-
-
-
-
c2 / st ⇓ st'
-
- (E_IfFalse)
-
-
-
-
-
-
IF b1 THEN c1 ELSE c2 FI / st ⇓ st'
-
-
-
-
-
beval st b1 = false
-
- (E_WhileEnd)
-
-
-
-
-
-
WHILE b DO c END / st ⇓ st
-
-
-
-
-
beval st b1 = true
-
-
-
-
c / st ⇓ st'
-
-
-
-
WHILE b DO c END / st' ⇓ st''
-
- (E_WhileLoop)
-
-
-
-
-
-
WHILE b DO c END / st ⇓ st''
-
-
-
-
-
- Here is the formal definition. (Make sure you understand
- how it corresponds to the inference rules.)
-
- The cost of defining evaluation as a relation instead of a
- function is that we now need to construct proofs that some
- program evaluates to some result state, rather than just letting
- Coq's computation mechanism do it for us.
-
-Exampleceval_example2:
- (X ::= ANum 0;; Y ::= ANum 1;; Z ::= ANum 2) / empty_state⇓
- (update (update (updateempty_stateX 0) Y 1) Z 2).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (pup_to_n)
- Write an Imp program that sums the numbers from 1 to
- X (inclusive: 1+2+...+X) in the variable Y.
- Prove that this program executes as intended for X = 2
- (this latter part is trickier than you might expect).
-
-
-
-
-Definitionpup_to_n : com :=
- (* FILL IN HERE *)admit.
-
-
-Theorempup_to_2_ceval :
- pup_to_n / (updateempty_stateX 2) ⇓
- update (update (update (update (update (updateempty_state
- X 2) Y 0) Y 2) X 1) Y 3) X 0.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Determinism of Evaluation
-
-
-
- Changing from a computational to a relational definition of
- evaluation is a good move because it allows us to escape from the
- artificial requirement (imposed by Coq's restrictions on
- Fixpoint definitions) that evaluation should be a total
- function. But it also raises a question: Is the second definition
- of evaluation actually a partial function? That is, is it
- possible that, beginning from the same state st, we could
- evaluate some command c in different ways to reach two different
- output states st' and st''?
-
-
-
- In fact, this cannot happen: ceval is a partial function.
- Here's the proof:
-
-
-
-
-Theoremceval_deterministic: ∀cstst1st2,
- c / st⇓st1→
- c / st⇓st2→
- st1 = st2.
-
-
-Proof.
- introscstst1st2E1E2.
- generalizedependentst2.
- ceval_cases (inductionE1) Case;
- introsst2E2; inversionE2; subst.
- Case "E_Skip". reflexivity.
- Case "E_Ass". reflexivity.
- Case "E_Seq".
- assert (st' = st'0) asEQ1.
- SCase "Proof of assertion". applyIHE1_1; assumption.
- substst'0.
- applyIHE1_2. assumption.
- Case "E_IfTrue".
- SCase "b1 evaluates to true".
- applyIHE1. assumption.
- SCase "b1 evaluates to false (contradiction)".
- rewriteHinH5. inversionH5.
- Case "E_IfFalse".
- SCase "b1 evaluates to true (contradiction)".
- rewriteHinH5. inversionH5.
- SCase "b1 evaluates to false".
- applyIHE1. assumption.
- Case "E_WhileEnd".
- SCase "b1 evaluates to false".
- reflexivity.
- SCase "b1 evaluates to true (contradiction)".
- rewriteHinH2. inversionH2.
- Case "E_WhileLoop".
- SCase "b1 evaluates to false (contradiction)".
- rewriteHinH4. inversionH4.
- SCase "b1 evaluates to true".
- assert (st' = st'0) asEQ1.
- SSCase "Proof of assertion". applyIHE1_1; assumption.
- substst'0.
- applyIHE1_2. assumption. Qed.
-
-
-
-
-
-
-
Reasoning About Imp Programs
-
-
-
- We'll get much deeper into systematic techniques for reasoning
- about Imp programs in the following chapters, but we can do quite
- a bit just working with the bare definitions.
-
-
-
-
-(* This section explores some examples. *)
-
-
-Theoremplus2_spec : ∀stnst',
- stX = n→
- plus2 / st⇓st'→
- st'X = n + 2.
-Proof.
- introsstnst'HXHeval.
- (* Inverting Heval essentially forces Coq to expand one
- step of the ceval computation - in this case revealing
- that st' must be st extended with the new value of X,
- since plus2 is an assignment *)
- inversionHeval. subst. clearHeval. simpl.
- applyupdate_eq. Qed.
-
-
-
-
-
-
Exercise: 3 stars (XtimesYinZ_spec)
- State and prove a specification of XtimesYinZ.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 3 stars (loop_never_stops)
-
-
-
-Theoremloop_never_stops : ∀stst',
- ~(loop / st⇓st').
-Proof.
- introsstst'contra. unfoldloopincontra.
- remember (WHILEBTrueDOSKIPEND) asloopdefeqn:Heqloopdef.
- (* Proceed by induction on the assumed derivation showing that
- loopdef terminates. Most of the cases are immediately
- contradictory (and so can be solved in one step with
- inversion). *)
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (no_whilesR)
- Consider the definition of the no_whiles property below:
-
-This property yields true just on programs that
- have no while loops. Using Inductive, write a property
- no_whilesR such that no_whilesRc is provable exactly when c
- is a program with no while loops. Then prove its equivalence
- with no_whiles.
-
-
-
-
-Inductiveno_whilesR: com→Prop :=
- (* FILL IN HERE *)
- .
-
-
-Theoremno_whiles_eqv:
- ∀c, no_whilesc = true↔no_whilesRc.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 4 stars (no_whiles_terminating)
- Imp programs that don't involve while loops always terminate.
- State and prove a theorem that says this. (Use either no_whiles or no_whilesR, as you prefer.)
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 3 stars (stack_compiler)
- HP Calculators, programming languages like Forth and Postscript,
- and abstract machines like the Java Virtual Machine all evaluate
- arithmetic expressions using a stack. For instance, the expression
-
-Write a function to evaluate programs in the stack language. It
- takes as input a state, a stack represented as a list of
- numbers (top stack item is the head of the list), and a program
- represented as a list of instructions, and returns the stack after
- executing the program. Test your function on the examples below.
-
-
-
- Note that the specification leaves unspecified what to do when
- encountering an SPlus, SMinus, or SMult instruction if the
- stack contains less than two elements. In a sense, it is
- immaterial what we do, since our compiler will never emit such a
- malformed program.
-
-
-
-
-Fixpoints_execute (st : state) (stack : listnat)
- (prog : listsinstr)
- : listnat :=
-(* FILL IN HERE *)admit.
-
-
-Examples_execute1 :
- s_executeempty_state []
- [SPush 5; SPush 3; SPush 1; SMinus]
- = [2; 5].
-(* FILL IN HERE *)Admitted.
-
-
-Examples_execute2 :
- s_execute (updateempty_stateX 3) [3;4]
- [SPush 4; SLoadX; SMult; SPlus]
- = [15; 4].
-(* FILL IN HERE *)Admitted.
-
-
-
-
-
-Next, write a function which compiles an aexp into a stack
- machine program. The effect of running the program should be the
- same as pushing the value of the expression on the stack.
-
-
-
-
-Fixpoints_compile (e : aexp) : listsinstr :=
-(* FILL IN HERE *)admit.
-
-
-
-
-
-After you've defined s_compile, uncomment the following to test
- that it works.
-
- The task of this exercise is to prove the correctness of the
- calculator implemented in the previous exercise. Remember that
- the specification left unspecified what to do when encountering an
- SPlus, SMinus, or SMult instruction if the stack contains
- less than two elements. (In order to make your correctness proof
- easier you may find it useful to go back and change your
- implementation!)
-
-
-
- Prove the following theorem, stating that the compile function
- behaves correctly. You will need to start by stating a more
- general lemma to get a usable induction hypothesis; the main
- theorem will then be a simple corollary of this lemma.
-
-
-
-
-Theorems_compile_correct : ∀(st : state) (e : aexp),
- s_executest [] (s_compilee) = [ aevalste ].
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 5 stars, advanced (break_imp)
-
-
-
-ModuleBreakImp.
-
-
-
-
-
-Imperative languages such as C or Java often have a break or
- similar statement for interrupting the execution of loops. In this
- exercise we will consider how to add break to Imp.
-
-
-
- First, we need to enrich the language of commands with an
- additional case.
-
-Next, we need to define the behavior of BREAK. Informally,
- whenever BREAK is executed in a sequence of commands, it stops
- the execution of that sequence and signals that the innermost
- enclosing loop (if any) should terminate. If there aren't any
- enclosing loops, then the whole program simply terminates. The
- final state should be the same as the one in which the BREAK
- statement was executed.
-
-
-
- One important point is what to do when there are multiple loops
- enclosing a given BREAK. In those cases, BREAK should only
- terminate the innermost loop where it occurs. Thus, after
- executing the following piece of code...
-
-
-
-
- X ::= 0;
- Y ::= 1;
- WHILE 0 ≠ YDO
- WHILETRUEDO
- BREAK
- END;
- X ::= 1;
- Y ::= Y - 1
- END
-
-
-
- ... the value of X should be 1, and not 0.
-
-
-
- One way of expressing this behavior is to add another parameter to
- the evaluation relation that specifies whether evaluation of a
- command executes a BREAK statement:
-
-
-
-
-Inductivestatus : Type :=
- | SContinue : status
- | SBreak : status.
-
-
-Reserved Notation "c1 '/' st '⇓' s '/' st'"
- (atlevel 40, st, satlevel 39).
-
-
-
-
-
-Intuitively, c/st⇓s/st' means that, if c is started in
- state st, then it terminates in state st' and either signals
- that any surrounding loop (or the whole program) should exit
- immediately (s=SBreak) or that execution should continue
- normally (s=SContinue).
-
-
-
- The definition of the "c/st⇓s/st'" relation is very
- similar to the one we gave above for the regular evaluation
- relation (c/st⇓s/st') — we just need to handle the
- termination signals appropriately:
-
-
-
-
-
If the command is SKIP, then the state doesn't change, and
- execution of any enclosing loop can continue normally.
-
-
-
-
-
-
If the command is BREAK, the state stays unchanged, but we
- signal a SBreak.
-
-
-
-
-
-
If the command is an assignment, then we update the binding for
- that variable in the state accordingly and signal that execution
- can continue normally.
-
-
-
-
-
-
If the command is of the form IFbTHENc1ELSEc2FI, then
- the state is updated as in the original semantics of Imp, except
- that we also propagate the signal from the execution of
- whichever branch was taken.
-
-
-
-
-
-
If the command is a sequence c1;c2, we first execute
- c1. If this yields a SBreak, we skip the execution of c2
- and propagate the SBreak signal to the surrounding context;
- the resulting state should be the same as the one obtained by
- executing c1 alone. Otherwise, we execute c2 on the state
- obtained after executing c1, and propagate the signal that was
- generated there.
-
-
-
-
-
-
Finally, for a loop of the form WHILEbDOcEND, the
- semantics is almost the same as before. The only difference is
- that, when b evaluates to true, we execute c and check the
- signal that it raises. If that signal is SContinue, then the
- execution proceeds as in the original semantics. Otherwise, we
- stop the execution of the loop, and the resulting state is the
- same as the one resulting from the execution of the current
- iteration. In either case, since BREAK only terminates the
- innermost loop, WHILE signals SContinue.
-
-
-
-
-
- Based on the above description, complete the definition of the
- ceval relation.
-
-
-
-
-Inductiveceval : com→state→status→state→Prop :=
- | E_Skip : ∀st,
- CSkip / st⇓SContinue / st
- (* FILL IN HERE *)
-
- where "c1 '/' st '⇓' s '/' st'" := (cevalc1stsst').
-
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_auxc "E_Skip"
- (* FILL IN HERE *)
- ].
-
-
-
-
-
-Now the following properties of your definition of ceval:
-
-
-
-
-Theorembreak_ignore : ∀cstst's,
- (BREAK; c) / st⇓s / st'→
- st = st'.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremwhile_continue : ∀bcstst's,
- (WHILEbDOcEND) / st⇓s / st'→
- s = SContinue.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremwhile_stops_on_break : ∀bcstst',
- bevalstb = true→
- c / st⇓SBreak / st'→
- (WHILEbDOcEND) / st⇓SContinue / st'.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-Theoremceval_deterministic: ∀(c:com) stst1st2s1s2,
- c / st⇓s1 / st1→
- c / st⇓s2 / st2→
- st1 = st2∧s1 = s2.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-EndBreakImp.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (short_circuit)
- Most modern programming languages use a "short-circuit" evaluation
- rule for boolean and: to evaluate BAndb1b2, first evaluate
- b1. If it evaluates to false, then the entire BAnd
- expression evaluates to false immediately, without evaluating
- b2. Otherwise, b2 is evaluated to determine the result of the
- BAnd expression.
-
-
-
- Write an alternate version of beval that performs short-circuit
- evaluation of BAnd in this manner, and prove that it is
- equivalent to beval.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 4 stars, optional (add_for_loop)
- Add C-style for loops to the language of commands, update the
- ceval definition to define the semantics of for loops, and add
- cases for for loops as needed so that all the proofs in this file
- are accepted by Coq.
-
-
-
- A for loop should be parameterized by (a) a statement executed
- initially, (b) a test that is run on each iteration of the loop to
- determine whether the loop should continue, (c) a statement
- executed at the end of each loop iteration, and (d) a statement
- that makes up the body of the loop. (You don't need to worry
- about making up a concrete Notation for for loops, but feel free
- to play with this too if you like.)
-
-
-
-
\ No newline at end of file
diff --git a/Imp.v b/Imp.v
deleted file mode 100644
index 43fff38..0000000
--- a/Imp.v
+++ /dev/null
@@ -1,1890 +0,0 @@
-(** * Imp: Simple Imperative Programs *)
-
-(** In this chapter, we begin a new direction that will continue for
- the rest of the course. Up to now most of our attention has been
- focused on various aspects of Coq itself, while from now on we'll
- mostly be using Coq to formalize other things. (We'll continue to
- pause from time to time to introduce a few additional aspects of
- Coq.)
-
- Our first case study is a _simple imperative programming language_
- called Imp, embodying a tiny core fragment of conventional
- mainstream languages such as C and Java. Here is a familiar
- mathematical function written in Imp.
- Z ::= X;;
- Y ::= 1;;
- WHILE not (Z = 0) DO
- Y ::= Y * Z;;
- Z ::= Z - 1
- END
-*)
-
-(** This chapter looks at how to define the _syntax_ and _semantics_
- of Imp; the chapters that follow develop a theory of _program
- equivalence_ and introduce _Hoare Logic_, a widely used logic for
- reasoning about imperative programs. *)
-
-(* ####################################################### *)
-(** *** Sflib *)
-
-(** A minor technical point: Instead of asking Coq to import our
- earlier definitions from chapter [Logic], we import a small library
- called [Sflib.v], containing just a few definitions and theorems
- from earlier chapters that we'll actually use in the rest of the
- course. This change should be nearly invisible, since most of what's
- missing from Sflib has identical definitions in the Coq standard
- library. The main reason for doing it is to tidy the global Coq
- environment so that, for example, it is easier to search for
- relevant theorems. *)
-
-Require Export SfLib.
-
-(* ####################################################### *)
-(** * Arithmetic and Boolean Expressions *)
-
-(** We'll present Imp in three parts: first a core language of
- _arithmetic and boolean expressions_, then an extension of these
- expressions with _variables_, and finally a language of _commands_
- including assignment, conditions, sequencing, and loops. *)
-
-(* ####################################################### *)
-(** ** Syntax *)
-
-Module AExp.
-
-(** These two definitions specify the _abstract syntax_ of
- arithmetic and boolean expressions. *)
-
-Inductive aexp : Type :=
- | ANum : nat -> aexp
- | APlus : aexp -> aexp -> aexp
- | AMinus : aexp -> aexp -> aexp
- | AMult : aexp -> aexp -> aexp.
-
-Inductive bexp : Type :=
- | BTrue : bexp
- | BFalse : bexp
- | BEq : aexp -> aexp -> bexp
- | BLe : aexp -> aexp -> bexp
- | BNot : bexp -> bexp
- | BAnd : bexp -> bexp -> bexp.
-
-(** In this chapter, we'll elide the translation from the
- concrete syntax that a programmer would actually write to these
- abstract syntax trees -- the process that, for example, would
- translate the string ["1+2*3"] to the AST [APlus (ANum
- 1) (AMult (ANum 2) (ANum 3))]. The optional chapter [ImpParser]
- develops a simple implementation of a lexical analyzer and parser
- that can perform this translation. You do _not_ need to
- understand that file to understand this one, but if you haven't
- taken a course where these techniques are covered (e.g., a
- compilers course) you may want to skim it. *)
-
-(** *** *)
-(** For comparison, here's a conventional BNF (Backus-Naur Form)
- grammar defining the same abstract syntax:
- a ::= nat
- | a + a
- | a - a
- | a * a
-
- b ::= true
- | false
- | a = a
- | a <= a
- | not b
- | b and b
-*)
-
-(** Compared to the Coq version above...
-
- - The BNF is more informal -- for example, it gives some
- suggestions about the surface syntax of expressions (like the
- fact that the addition operation is written [+] and is an
- infix symbol) while leaving other aspects of lexical analysis
- and parsing (like the relative precedence of [+], [-], and
- [*]) unspecified. Some additional information -- and human
- intelligence -- would be required to turn this description
- into a formal definition (when implementing a compiler, for
- example).
-
- The Coq version consistently omits all this information and
- concentrates on the abstract syntax only.
-
- - On the other hand, the BNF version is lighter and
- easier to read. Its informality makes it flexible, which is
- a huge advantage in situations like discussions at the
- blackboard, where conveying general ideas is more important
- than getting every detail nailed down precisely.
-
- Indeed, there are dozens of BNF-like notations and people
- switch freely among them, usually without bothering to say which
- form of BNF they're using because there is no need to: a
- rough-and-ready informal understanding is all that's
- needed. *)
-
-(** It's good to be comfortable with both sorts of notations:
- informal ones for communicating between humans and formal ones for
- carrying out implementations and proofs. *)
-
-(* ####################################################### *)
-(** ** Evaluation *)
-
-(** _Evaluating_ an arithmetic expression produces a number. *)
-
-Fixpoint aeval (a : aexp) : nat :=
- match a with
- | ANum n => n
- | APlus a1 a2 => (aeval a1) + (aeval a2)
- | AMinus a1 a2 => (aeval a1) - (aeval a2)
- | AMult a1 a2 => (aeval a1) * (aeval a2)
- end.
-
-Example test_aeval1:
- aeval (APlus (ANum 2) (ANum 2)) = 4.
-Proof. reflexivity. Qed.
-
-(** *** *)
-(** Similarly, evaluating a boolean expression yields a boolean. *)
-
-Fixpoint beval (b : bexp) : bool :=
- match b with
- | BTrue => true
- | BFalse => false
- | BEq a1 a2 => beq_nat (aeval a1) (aeval a2)
- | BLe a1 a2 => ble_nat (aeval a1) (aeval a2)
- | BNot b1 => negb (beval b1)
- | BAnd b1 b2 => andb (beval b1) (beval b2)
- end.
-
-(* ####################################################### *)
-(** ** Optimization *)
-
-(** We haven't defined very much yet, but we can already get
- some mileage out of the definitions. Suppose we define a function
- that takes an arithmetic expression and slightly simplifies it,
- changing every occurrence of [0+e] (i.e., [(APlus (ANum 0) e])
- into just [e]. *)
-
-Fixpoint optimize_0plus (a:aexp) : aexp :=
- match a with
- | ANum n =>
- ANum n
- | APlus (ANum 0) e2 =>
- optimize_0plus e2
- | APlus e1 e2 =>
- APlus (optimize_0plus e1) (optimize_0plus e2)
- | AMinus e1 e2 =>
- AMinus (optimize_0plus e1) (optimize_0plus e2)
- | AMult e1 e2 =>
- AMult (optimize_0plus e1) (optimize_0plus e2)
- end.
-
-(** To make sure our optimization is doing the right thing we
- can test it on some examples and see if the output looks OK. *)
-
-Example test_optimize_0plus:
- optimize_0plus (APlus (ANum 2)
- (APlus (ANum 0)
- (APlus (ANum 0) (ANum 1))))
- = APlus (ANum 2) (ANum 1).
-Proof. reflexivity. Qed.
-
-(** But if we want to be sure the optimization is correct --
- i.e., that evaluating an optimized expression gives the same
- result as the original -- we should prove it. *)
-
-Theorem optimize_0plus_sound: forall a,
- aeval (optimize_0plus a) = aeval a.
-Proof.
- intros a. induction a.
- Case "ANum". reflexivity.
- Case "APlus". destruct a1.
- SCase "a1 = ANum n". destruct n.
- SSCase "n = 0". simpl. apply IHa2.
- SSCase "n <> 0". simpl. rewrite IHa2. reflexivity.
- SCase "a1 = APlus a1_1 a1_2".
- simpl. simpl in IHa1. rewrite IHa1.
- rewrite IHa2. reflexivity.
- SCase "a1 = AMinus a1_1 a1_2".
- simpl. simpl in IHa1. rewrite IHa1.
- rewrite IHa2. reflexivity.
- SCase "a1 = AMult a1_1 a1_2".
- simpl. simpl in IHa1. rewrite IHa1.
- rewrite IHa2. reflexivity.
- Case "AMinus".
- simpl. rewrite IHa1. rewrite IHa2. reflexivity.
- Case "AMult".
- simpl. rewrite IHa1. rewrite IHa2. reflexivity. Qed.
-
-(* ####################################################### *)
-(** * Coq Automation *)
-
-(** The repetition in this last proof is starting to be a little
- annoying. If either the language of arithmetic expressions or the
- optimization being proved sound were significantly more complex,
- it would begin to be a real problem.
-
- So far, we've been doing all our proofs using just a small handful
- of Coq's tactics and completely ignoring its powerful facilities
- for constructing parts of proofs automatically. This section
- introduces some of these facilities, and we will see more over the
- next several chapters. Getting used to them will take some
- energy -- Coq's automation is a power tool -- but it will allow us
- to scale up our efforts to more complex definitions and more
- interesting properties without becoming overwhelmed by boring,
- repetitive, low-level details. *)
-
-(* ####################################################### *)
-(** ** Tacticals *)
-
-(** _Tacticals_ is Coq's term for tactics that take other tactics as
- arguments -- "higher-order tactics," if you will. *)
-
-(* ####################################################### *)
-(** *** The [repeat] Tactical *)
-
-(** The [repeat] tactical takes another tactic and keeps applying
- this tactic until the tactic fails. Here is an example showing
- that [100] is even using repeat. *)
-
-Theorem ev100 : ev 100.
-Proof.
- repeat (apply ev_SS). (* applies ev_SS 50 times,
- until [apply ev_SS] fails *)
- apply ev_0.
-Qed.
-(* Print ev100. *)
-
-(** The [repeat T] tactic never fails; if the tactic [T] doesn't apply
- to the original goal, then repeat still succeeds without changing
- the original goal (it repeats zero times). *)
-
-Theorem ev100' : ev 100.
-Proof.
- repeat (apply ev_0). (* doesn't fail, applies ev_0 zero times *)
- repeat (apply ev_SS). apply ev_0. (* we can continue the proof *)
-Qed.
-
-(** The [repeat T] tactic does not have any bound on the number of
- times it applies [T]. If [T] is a tactic that always succeeds then
- repeat [T] will loop forever (e.g. [repeat simpl] loops forever
- since [simpl] always succeeds). While Coq's term language is
- guaranteed to terminate, Coq's tactic language is not! *)
-
-(* ####################################################### *)
-(** *** The [try] Tactical *)
-
-(** If [T] is a tactic, then [try T] is a tactic that is just like [T]
- except that, if [T] fails, [try T] _successfully_ does nothing at
- all (instead of failing). *)
-
-Theorem silly1 : forall ae, aeval ae = aeval ae.
-Proof. try reflexivity. (* this just does [reflexivity] *) Qed.
-
-Theorem silly2 : forall (P : Prop), P -> P.
-Proof.
- intros P HP.
- try reflexivity. (* just [reflexivity] would have failed *)
- apply HP. (* we can still finish the proof in some other way *)
-Qed.
-
-(** Using [try] in a completely manual proof is a bit silly, but
- we'll see below that [try] is very useful for doing automated
- proofs in conjunction with the [;] tactical. *)
-
-(* ####################################################### *)
-(** *** The [;] Tactical (Simple Form) *)
-
-(** In its most commonly used form, the [;] tactical takes two tactics
- as argument: [T;T'] first performs the tactic [T] and then
- performs the tactic [T'] on _each subgoal_ generated by [T]. *)
-
-(** For example, consider the following trivial lemma: *)
-
-Lemma foo : forall n, ble_nat 0 n = true.
-Proof.
- intros.
- destruct n.
- (* Leaves two subgoals, which are discharged identically... *)
- Case "n=0". simpl. reflexivity.
- Case "n=Sn'". simpl. reflexivity.
-Qed.
-
-(** We can simplify this proof using the [;] tactical: *)
-
-Lemma foo' : forall n, ble_nat 0 n = true.
-Proof.
- intros.
- destruct n; (* [destruct] the current goal *)
- simpl; (* then [simpl] each resulting subgoal *)
- reflexivity. (* and do [reflexivity] on each resulting subgoal *)
-Qed.
-
-(** Using [try] and [;] together, we can get rid of the repetition in
- the proof that was bothering us a little while ago. *)
-
-Theorem optimize_0plus_sound': forall a,
- aeval (optimize_0plus a) = aeval a.
-Proof.
- intros a.
- induction a;
- (* Most cases follow directly by the IH *)
- try (simpl; rewrite IHa1; rewrite IHa2; reflexivity).
- (* The remaining cases -- ANum and APlus -- are different *)
- Case "ANum". reflexivity.
- Case "APlus".
- destruct a1;
- (* Again, most cases follow directly by the IH *)
- try (simpl; simpl in IHa1; rewrite IHa1;
- rewrite IHa2; reflexivity).
- (* The interesting case, on which the [try...] does nothing,
- is when [e1 = ANum n]. In this case, we have to destruct
- [n] (to see whether the optimization applies) and rewrite
- with the induction hypothesis. *)
- SCase "a1 = ANum n". destruct n;
- simpl; rewrite IHa2; reflexivity. Qed.
-
-(** Coq experts often use this "[...; try... ]" idiom after a tactic
- like [induction] to take care of many similar cases all at once.
- Naturally, this practice has an analog in informal proofs.
-
- Here is an informal proof of this theorem that matches the
- structure of the formal one:
-
- _Theorem_: For all arithmetic expressions [a],
- aeval (optimize_0plus a) = aeval a.
- _Proof_: By induction on [a]. The [AMinus] and [AMult] cases
- follow directly from the IH. The remaining cases are as follows:
-
- - Suppose [a = ANum n] for some [n]. We must show
- aeval (optimize_0plus (ANum n)) = aeval (ANum n).
- This is immediate from the definition of [optimize_0plus].
-
- - Suppose [a = APlus a1 a2] for some [a1] and [a2]. We
- must show
- aeval (optimize_0plus (APlus a1 a2))
- = aeval (APlus a1 a2).
- Consider the possible forms of [a1]. For most of them,
- [optimize_0plus] simply calls itself recursively for the
- subexpressions and rebuilds a new expression of the same form
- as [a1]; in these cases, the result follows directly from the
- IH.
-
- The interesting case is when [a1 = ANum n] for some [n].
- If [n = ANum 0], then
- optimize_0plus (APlus a1 a2) = optimize_0plus a2
- and the IH for [a2] is exactly what we need. On the other
- hand, if [n = S n'] for some [n'], then again [optimize_0plus]
- simply calls itself recursively, and the result follows from
- the IH. [] *)
-
-(** This proof can still be improved: the first case (for [a = ANum
- n]) is very trivial -- even more trivial than the cases that we
- said simply followed from the IH -- yet we have chosen to write it
- out in full. It would be better and clearer to drop it and just
- say, at the top, "Most cases are either immediate or direct from
- the IH. The only interesting case is the one for [APlus]..." We
- can make the same improvement in our formal proof too. Here's how
- it looks: *)
-
-Theorem optimize_0plus_sound'': forall a,
- aeval (optimize_0plus a) = aeval a.
-Proof.
- intros a.
- induction a;
- (* Most cases follow directly by the IH *)
- try (simpl; rewrite IHa1; rewrite IHa2; reflexivity);
- (* ... or are immediate by definition *)
- try reflexivity.
- (* The interesting case is when a = APlus a1 a2. *)
- Case "APlus".
- destruct a1; try (simpl; simpl in IHa1; rewrite IHa1;
- rewrite IHa2; reflexivity).
- SCase "a1 = ANum n". destruct n;
- simpl; rewrite IHa2; reflexivity. Qed.
-
-(* ####################################################### *)
-(** *** The [;] Tactical (General Form) *)
-
-(** The [;] tactical has a more general than the simple [T;T'] we've
- seen above, which is sometimes also useful. If [T], [T1], ...,
- [Tn] are tactics, then
- T; [T1 | T2 | ... | Tn]
- is a tactic that first performs [T] and then performs [T1] on the
- first subgoal generated by [T], performs [T2] on the second
- subgoal, etc.
-
- So [T;T'] is just special notation for the case when all of the
- [Ti]'s are the same tactic; i.e. [T;T'] is just a shorthand for:
- T; [T' | T' | ... | T']
-*)
-
-(* ####################################################### *)
-(** ** Defining New Tactic Notations *)
-
-(** Coq also provides several ways of "programming" tactic scripts.
-
- - The [Tactic Notation] idiom illustrated below gives a handy
- way to define "shorthand tactics" that bundle several tactics
- into a single command.
-
- - For more sophisticated programming, Coq offers a small
- built-in programming language called [Ltac] with primitives
- that can examine and modify the proof state. The details are
- a bit too complicated to get into here (and it is generally
- agreed that [Ltac] is not the most beautiful part of Coq's
- design!), but they can be found in the reference manual, and
- there are many examples of [Ltac] definitions in the Coq
- standard library that you can use as examples.
-
- - There is also an OCaml API, which can be used to build tactics
- that access Coq's internal structures at a lower level, but
- this is seldom worth the trouble for ordinary Coq users.
-
-The [Tactic Notation] mechanism is the easiest to come to grips with,
-and it offers plenty of power for many purposes. Here's an example.
-*)
-
-Tactic Notation "simpl_and_try" tactic(c) :=
- simpl;
- try c.
-
-(** This defines a new tactical called [simpl_and_try] which
- takes one tactic [c] as an argument, and is defined to be
- equivalent to the tactic [simpl; try c]. For example, writing
- "[simpl_and_try reflexivity.]" in a proof would be the same as
- writing "[simpl; try reflexivity.]" *)
-
-(** The next subsection gives a more sophisticated use of this
- feature... *)
-
-(* ####################################################### *)
-(** *** Bulletproofing Case Analyses *)
-
-(** Being able to deal with most of the cases of an [induction]
- or [destruct] all at the same time is very convenient, but it can
- also be a little confusing. One problem that often comes up is
- that _maintaining_ proofs written in this style can be difficult.
- For example, suppose that, later, we extended the definition of
- [aexp] with another constructor that also required a special
- argument. The above proof might break because Coq generated the
- subgoals for this constructor before the one for [APlus], so that,
- at the point when we start working on the [APlus] case, Coq is
- actually expecting the argument for a completely different
- constructor. What we'd like is to get a sensible error message
- saying "I was expecting the [AFoo] case at this point, but the
- proof script is talking about [APlus]." Here's a nice trick (due
- to Aaron Bohannon) that smoothly achieves this. *)
-
-Tactic Notation "aexp_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ANum" | Case_aux c "APlus"
- | Case_aux c "AMinus" | Case_aux c "AMult" ].
-
-(** ([Case_aux] implements the common functionality of [Case],
- [SCase], [SSCase], etc. For example, [Case "foo"] is defined as
- [Case_aux Case "foo".) *)
-
-(** For example, if [a] is a variable of type [aexp], then doing
- aexp_cases (induction a) Case
- will perform an induction on [a] (the same as if we had just typed
- [induction a]) and _also_ add a [Case] tag to each subgoal
- generated by the [induction], labeling which constructor it comes
- from. For example, here is yet another proof of
- [optimize_0plus_sound], using [aexp_cases]: *)
-
-Theorem optimize_0plus_sound''': forall a,
- aeval (optimize_0plus a) = aeval a.
-Proof.
- intros a.
- aexp_cases (induction a) Case;
- try (simpl; rewrite IHa1; rewrite IHa2; reflexivity);
- try reflexivity.
- (* At this point, there is already an ["APlus"] case name
- in the context. The [Case "APlus"] here in the proof
- text has the effect of a sanity check: if the "Case"
- string in the context is anything _other_ than ["APlus"]
- (for example, because we added a clause to the definition
- of [aexp] and forgot to change the proof) we'll get a
- helpful error at this point telling us that this is now
- the wrong case. *)
- Case "APlus".
- aexp_cases (destruct a1) SCase;
- try (simpl; simpl in IHa1;
- rewrite IHa1; rewrite IHa2; reflexivity).
- SCase "ANum". destruct n;
- simpl; rewrite IHa2; reflexivity. Qed.
-
-(** **** Exercise: 3 stars (optimize_0plus_b) *)
-(** Since the [optimize_0plus] tranformation doesn't change the value
- of [aexp]s, we should be able to apply it to all the [aexp]s that
- appear in a [bexp] without changing the [bexp]'s value. Write a
- function which performs that transformation on [bexp]s, and prove
- it is sound. Use the tacticals we've just seen to make the proof
- as elegant as possible. *)
-
-Fixpoint optimize_0plus_b (b : bexp) : bexp :=
- (* FILL IN HERE *) admit.
-
-
-Theorem optimize_0plus_b_sound : forall b,
- beval (optimize_0plus_b b) = beval b.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 4 stars, optional (optimizer) *)
-(** _Design exercise_: The optimization implemented by our
- [optimize_0plus] function is only one of many imaginable
- optimizations on arithmetic and boolean expressions. Write a more
- sophisticated optimizer and prove it correct.
-
-(* FILL IN HERE *)
-*)
-(** [] *)
-
-(* ####################################################### *)
-(** ** The [omega] Tactic *)
-
-(** The [omega] tactic implements a decision procedure for a subset of
- first-order logic called _Presburger arithmetic_. It is based on
- the Omega algorithm invented in 1992 by William Pugh.
-
- If the goal is a universally quantified formula made out of
-
- - numeric constants, addition ([+] and [S]), subtraction ([-]
- and [pred]), and multiplication by constants (this is what
- makes it Presburger arithmetic),
-
- - equality ([=] and [<>]) and inequality ([<=]), and
-
- - the logical connectives [/\], [\/], [~], and [->],
-
- then invoking [omega] will either solve the goal or tell you that
- it is actually false. *)
-
-Example silly_presburger_example : forall m n o p,
- m + n <= n + o /\ o + 3 = p + 3 ->
- m <= p.
-Proof.
- intros. omega.
-Qed.
-
-(** Liebniz wrote, "It is unworthy of excellent men to lose
- hours like slaves in the labor of calculation which could be
- relegated to anyone else if machines were used." We recommend
- using the omega tactic whenever possible. *)
-
-(* ####################################################### *)
-(** ** A Few More Handy Tactics *)
-
-(** Finally, here are some miscellaneous tactics that you may find
- convenient.
-
- - [clear H]: Delete hypothesis [H] from the context.
-
- - [subst x]: Find an assumption [x = e] or [e = x] in the
- context, replace [x] with [e] throughout the context and
- current goal, and clear the assumption.
-
- - [subst]: Substitute away _all_ assumptions of the form [x = e]
- or [e = x].
-
- - [rename... into...]: Change the name of a hypothesis in the
- proof context. For example, if the context includes a variable
- named [x], then [rename x into y] will change all occurrences
- of [x] to [y].
-
- - [assumption]: Try to find a hypothesis [H] in the context that
- exactly matches the goal; if one is found, behave just like
- [apply H].
-
- - [contradiction]: Try to find a hypothesis [H] in the current
- context that is logically equivalent to [False]. If one is
- found, solve the goal.
-
- - [constructor]: Try to find a constructor [c] (from some
- [Inductive] definition in the current environment) that can be
- applied to solve the current goal. If one is found, behave
- like [apply c]. *)
-
-(** We'll see many examples of these in the proofs below. *)
-
-(* ####################################################### *)
-(** * Evaluation as a Relation *)
-
-(** We have presented [aeval] and [beval] as functions defined by
- [Fixpoints]. Another way to think about evaluation -- one that we
- will see is often more flexible -- is as a _relation_ between
- expressions and their values. This leads naturally to [Inductive]
- definitions like the following one for arithmetic
- expressions... *)
-
-Module aevalR_first_try.
-
-Inductive aevalR : aexp -> nat -> Prop :=
- | E_ANum : forall (n: nat),
- aevalR (ANum n) n
- | E_APlus : forall (e1 e2: aexp) (n1 n2: nat),
- aevalR e1 n1 ->
- aevalR e2 n2 ->
- aevalR (APlus e1 e2) (n1 + n2)
- | E_AMinus: forall (e1 e2: aexp) (n1 n2: nat),
- aevalR e1 n1 ->
- aevalR e2 n2 ->
- aevalR (AMinus e1 e2) (n1 - n2)
- | E_AMult : forall (e1 e2: aexp) (n1 n2: nat),
- aevalR e1 n1 ->
- aevalR e2 n2 ->
- aevalR (AMult e1 e2) (n1 * n2).
-
-(** As is often the case with relations, we'll find it
- convenient to define infix notation for [aevalR]. We'll write [e
- || n] to mean that arithmetic expression [e] evaluates to value
- [n]. (This notation is one place where the limitation to ASCII
- symbols becomes a little bothersome. The standard notation for
- the evaluation relation is a double down-arrow. We'll typeset it
- like this in the HTML version of the notes and use a double
- vertical bar as the closest approximation in [.v] files.) *)
-
-Notation "e '||' n" := (aevalR e n) : type_scope.
-
-End aevalR_first_try.
-
-(** In fact, Coq provides a way to use this notation in the definition
- of [aevalR] itself. This avoids situations where we're working on
- a proof involving statements in the form [e || n] but we have to
- refer back to a definition written using the form [aevalR e n].
-
- We do this by first "reserving" the notation, then giving the
- definition together with a declaration of what the notation
- means. *)
-
-Reserved Notation "e '||' n" (at level 50, left associativity).
-
-Inductive aevalR : aexp -> nat -> Prop :=
- | E_ANum : forall (n:nat),
- (ANum n) || n
- | E_APlus : forall (e1 e2: aexp) (n1 n2 : nat),
- (e1 || n1) -> (e2 || n2) -> (APlus e1 e2) || (n1 + n2)
- | E_AMinus : forall (e1 e2: aexp) (n1 n2 : nat),
- (e1 || n1) -> (e2 || n2) -> (AMinus e1 e2) || (n1 - n2)
- | E_AMult : forall (e1 e2: aexp) (n1 n2 : nat),
- (e1 || n1) -> (e2 || n2) -> (AMult e1 e2) || (n1 * n2)
-
- where "e '||' n" := (aevalR e n) : type_scope.
-
-Tactic Notation "aevalR_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_ANum" | Case_aux c "E_APlus"
- | Case_aux c "E_AMinus" | Case_aux c "E_AMult" ].
-
-(* ####################################################### *)
-(** ** Inference Rule Notation *)
-
-(** In informal discussions, it is convenient write the rules for
- [aevalR] and similar relations in the more readable graphical form
- of _inference rules_, where the premises above the line justify
- the conclusion below the line (we have already seen them in the
- Prop chapter). *)
-
-(** For example, the constructor [E_APlus]...
- | E_APlus : forall (e1 e2: aexp) (n1 n2: nat),
- aevalR e1 n1 ->
- aevalR e2 n2 ->
- aevalR (APlus e1 e2) (n1 + n2)
- ...would be written like this as an inference rule:
- e1 || n1
- e2 || n2
- -------------------- (E_APlus)
- APlus e1 e2 || n1+n2
-*)
-
-(** Formally, there is nothing very deep about inference rules:
- they are just implications. You can read the rule name on the
- right as the name of the constructor and read each of the
- linebreaks between the premises above the line and the line itself
- as [->]. All the variables mentioned in the rule ([e1], [n1],
- etc.) are implicitly bound by universal quantifiers at the
- beginning. (Such variables are often called _metavariables_ to
- distinguish them from the variables of the language we are
- defining. At the moment, our arithmetic expressions don't include
- variables, but we'll soon be adding them.) The whole collection
- of rules is understood as being wrapped in an [Inductive]
- declaration (informally, this is either elided or else indicated
- by saying something like "Let [aevalR] be the smallest relation
- closed under the following rules..."). *)
-
-(** For example, [||] is the smallest relation closed under these
- rules:
- ----------- (E_ANum)
- ANum n || n
-
- e1 || n1
- e2 || n2
- -------------------- (E_APlus)
- APlus e1 e2 || n1+n2
-
- e1 || n1
- e2 || n2
- --------------------- (E_AMinus)
- AMinus e1 e2 || n1-n2
-
- e1 || n1
- e2 || n2
- -------------------- (E_AMult)
- AMult e1 e2 || n1*n2
-*)
-
-
-
-(* ####################################################### *)
-(** ** Equivalence of the Definitions *)
-
-(** It is straightforward to prove that the relational and functional
- definitions of evaluation agree on all possible arithmetic
- expressions... *)
-
-Theorem aeval_iff_aevalR : forall a n,
- (a || n) <-> aeval a = n.
-Proof.
- split.
- Case "->".
- intros H.
- aevalR_cases (induction H) SCase; simpl.
- SCase "E_ANum".
- reflexivity.
- SCase "E_APlus".
- rewrite IHaevalR1. rewrite IHaevalR2. reflexivity.
- SCase "E_AMinus".
- rewrite IHaevalR1. rewrite IHaevalR2. reflexivity.
- SCase "E_AMult".
- rewrite IHaevalR1. rewrite IHaevalR2. reflexivity.
- Case "<-".
- generalize dependent n.
- aexp_cases (induction a) SCase;
- simpl; intros; subst.
- SCase "ANum".
- apply E_ANum.
- SCase "APlus".
- apply E_APlus.
- apply IHa1. reflexivity.
- apply IHa2. reflexivity.
- SCase "AMinus".
- apply E_AMinus.
- apply IHa1. reflexivity.
- apply IHa2. reflexivity.
- SCase "AMult".
- apply E_AMult.
- apply IHa1. reflexivity.
- apply IHa2. reflexivity.
-Qed.
-
-(** Note: if you're reading the HTML file, you'll see an empty square box instead
-of a proof for this theorem.
-You can click on this box to "unfold" the text to see the proof.
-Click on the unfolded to text to "fold" it back up to a box. We'll be using
-this style frequently from now on to help keep the HTML easier to read.
-The full proofs always appear in the .v files. *)
-
-(** We can make the proof quite a bit shorter by making more
- use of tacticals... *)
-
-Theorem aeval_iff_aevalR' : forall a n,
- (a || n) <-> aeval a = n.
-Proof.
- (* WORKED IN CLASS *)
- split.
- Case "->".
- intros H; induction H; subst; reflexivity.
- Case "<-".
- generalize dependent n.
- induction a; simpl; intros; subst; constructor;
- try apply IHa1; try apply IHa2; reflexivity.
-Qed.
-
-(** **** Exercise: 3 stars (bevalR) *)
-(** Write a relation [bevalR] in the same style as
- [aevalR], and prove that it is equivalent to [beval].*)
-
-(*
-Inductive bevalR:
-(* FILL IN HERE *)
-*)
-(** [] *)
-End AExp.
-
-(* ####################################################### *)
-(** ** Computational vs. Relational Definitions *)
-
-(** For the definitions of evaluation for arithmetic and boolean
- expressions, the choice of whether to use functional or relational
- definitions is mainly a matter of taste. In general, Coq has
- somewhat better support for working with relations. On the other
- hand, in some sense function definitions carry more information,
- because functions are necessarily deterministic and defined on all
- arguments; for a relation we have to show these properties
- explicitly if we need them. Functions also take advantage of Coq's
- computations mechanism.
-
- However, there are circumstances where relational definitions of
- evaluation are preferable to functional ones. *)
-
-Module aevalR_division.
-
-(** For example, suppose that we wanted to extend the arithmetic
- operations by considering also a division operation:*)
-
-Inductive aexp : Type :=
- | ANum : nat -> aexp
- | APlus : aexp -> aexp -> aexp
- | AMinus : aexp -> aexp -> aexp
- | AMult : aexp -> aexp -> aexp
- | ADiv : aexp -> aexp -> aexp. (* <--- new *)
-
-(** Extending the definition of [aeval] to handle this new operation
- would not be straightforward (what should we return as the result
- of [ADiv (ANum 5) (ANum 0)]?). But extending [aevalR] is
- straightforward. *)
-
-Inductive aevalR : aexp -> nat -> Prop :=
- | E_ANum : forall (n:nat),
- (ANum n) || n
- | E_APlus : forall (a1 a2: aexp) (n1 n2 : nat),
- (a1 || n1) -> (a2 || n2) -> (APlus a1 a2) || (n1 + n2)
- | E_AMinus : forall (a1 a2: aexp) (n1 n2 : nat),
- (a1 || n1) -> (a2 || n2) -> (AMinus a1 a2) || (n1 - n2)
- | E_AMult : forall (a1 a2: aexp) (n1 n2 : nat),
- (a1 || n1) -> (a2 || n2) -> (AMult a1 a2) || (n1 * n2)
- | E_ADiv : forall (a1 a2: aexp) (n1 n2 n3: nat),
- (a1 || n1) -> (a2 || n2) -> (mult n2 n3 = n1) -> (ADiv a1 a2) || n3
-
-where "a '||' n" := (aevalR a n) : type_scope.
-
-End aevalR_division.
-Module aevalR_extended.
-
-
-(** *** Adding nondeterminism *)
-(* /TERSE *)
-(** Suppose, instead, that we want to extend the arithmetic operations
- by a nondeterministic number generator [any]:*)
-
-Inductive aexp : Type :=
- | AAny : aexp (* <--- NEW *)
- | ANum : nat -> aexp
- | APlus : aexp -> aexp -> aexp
- | AMinus : aexp -> aexp -> aexp
- | AMult : aexp -> aexp -> aexp.
-
-(** Again, extending [aeval] would be tricky (because evaluation is
- _not_ a deterministic function from expressions to numbers), but
- extending [aevalR] is no problem: *)
-
-Inductive aevalR : aexp -> nat -> Prop :=
- | E_Any : forall (n:nat),
- AAny || n (* <--- new *)
- | E_ANum : forall (n:nat),
- (ANum n) || n
- | E_APlus : forall (a1 a2: aexp) (n1 n2 : nat),
- (a1 || n1) -> (a2 || n2) -> (APlus a1 a2) || (n1 + n2)
- | E_AMinus : forall (a1 a2: aexp) (n1 n2 : nat),
- (a1 || n1) -> (a2 || n2) -> (AMinus a1 a2) || (n1 - n2)
- | E_AMult : forall (a1 a2: aexp) (n1 n2 : nat),
- (a1 || n1) -> (a2 || n2) -> (AMult a1 a2) || (n1 * n2)
-
-where "a '||' n" := (aevalR a n) : type_scope.
-
-End aevalR_extended.
-
-(** * Expressions With Variables *)
-
-(** Let's turn our attention back to defining Imp. The next thing we
- need to do is to enrich our arithmetic and boolean expressions
- with variables. To keep things simple, we'll assume that all
- variables are global and that they only hold numbers. *)
-
-(* ##################################################### *)
-(** ** Identifiers *)
-
-(** To begin, we'll need to formalize _identifiers_ such as program
- variables. We could use strings for this -- or, in a real
- compiler, fancier structures like pointers into a symbol table.
- But for simplicity let's just use natural numbers as identifiers. *)
-
-(** (We hide this section in a module because these definitions are
- actually in [SfLib], but we want to repeat them here so that we
- can explain them.) *)
-
-Module Id.
-
-(** We define a new inductive datatype [Id] so that we won't confuse
- identifiers and numbers. We use [sumbool] to define a computable
- equality operator on [Id]. *)
-
-Inductive id : Type :=
- Id : nat -> id.
-
-Theorem eq_id_dec : forall id1 id2 : id, {id1 = id2} + {id1 <> id2}.
-Proof.
- intros id1 id2.
- destruct id1 as [n1]. destruct id2 as [n2].
- destruct (eq_nat_dec n1 n2) as [Heq | Hneq].
- Case "n1 = n2".
- left. rewrite Heq. reflexivity.
- Case "n1 <> n2".
- right. intros contra. inversion contra. apply Hneq. apply H0.
-Defined.
-
-
-(** The following lemmas will be useful for rewriting terms involving [eq_id_dec]. *)
-
-Lemma eq_id : forall (T:Type) x (p q:T),
- (if eq_id_dec x x then p else q) = p.
-Proof.
- intros.
- destruct (eq_id_dec x x).
- Case "x = x".
- reflexivity.
- Case "x <> x (impossible)".
- apply ex_falso_quodlibet; apply n; reflexivity. Qed.
-
-(** **** Exercise: 1 star, optional (neq_id) *)
-Lemma neq_id : forall (T:Type) x y (p q:T), x <> y ->
- (if eq_id_dec x y then p else q) = q.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-End Id.
-
-(* ####################################################### *)
-(** ** States *)
-
-(** A _state_ represents the current values of _all_ the variables at
- some point in the execution of a program. *)
-(** For simplicity (to avoid dealing with partial functions), we
- let the state be defined for _all_ variables, even though any
- given program is only going to mention a finite number of them.
- The state captures all of the information stored in memory. For Imp
- programs, because each variable stores only a natural number, we
- can represent the state as a mapping from identifiers to [nat].
- For more complex programming languages, the state might have more
- structure.
-*)
-
-Definition state := id -> nat.
-
-Definition empty_state : state :=
- fun _ => 0.
-
-Definition update (st : state) (x : id) (n : nat) : state :=
- fun x' => if eq_id_dec x x' then n else st x'.
-
-(** For proofs involving states, we'll need several simple properties
- of [update]. *)
-
-(** **** Exercise: 1 star (update_eq) *)
-Theorem update_eq : forall n x st,
- (update st x n) x = n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star (update_neq) *)
-Theorem update_neq : forall x2 x1 n st,
- x2 <> x1 ->
- (update st x2 n) x1 = (st x1).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star (update_example) *)
-(** Before starting to play with tactics, make sure you understand
- exactly what the theorem is saying! *)
-
-Theorem update_example : forall (n:nat),
- (update empty_state (Id 2) n) (Id 3) = 0.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star (update_shadow) *)
-Theorem update_shadow : forall n1 n2 x1 x2 (st : state),
- (update (update st x2 n1) x2 n2) x1 = (update st x2 n2) x1.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (update_same) *)
-Theorem update_same : forall n1 x1 x2 (st : state),
- st x1 = n1 ->
- (update st x1 n1) x2 = st x2.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (update_permute) *)
-Theorem update_permute : forall n1 n2 x1 x2 x3 st,
- x2 <> x1 ->
- (update (update st x2 n1) x1 n2) x3 = (update (update st x1 n2) x2 n1) x3.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ################################################### *)
-(** ** Syntax *)
-
-(** We can add variables to the arithmetic expressions we had before by
- simply adding one more constructor: *)
-
-Inductive aexp : Type :=
- | ANum : nat -> aexp
- | AId : id -> aexp (* <----- NEW *)
- | APlus : aexp -> aexp -> aexp
- | AMinus : aexp -> aexp -> aexp
- | AMult : aexp -> aexp -> aexp.
-
-Tactic Notation "aexp_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ANum" | Case_aux c "AId" | Case_aux c "APlus"
- | Case_aux c "AMinus" | Case_aux c "AMult" ].
-
-(** Defining a few variable names as notational shorthands will make
- examples easier to read: *)
-
-Definition X : id := Id 0.
-Definition Y : id := Id 1.
-Definition Z : id := Id 2.
-
-(** (This convention for naming program variables ([X], [Y],
- [Z]) clashes a bit with our earlier use of uppercase letters for
- types. Since we're not using polymorphism heavily in this part of
- the course, this overloading should not cause confusion.) *)
-
-(** The definition of [bexp]s is the same as before (using the new
- [aexp]s): *)
-
-Inductive bexp : Type :=
- | BTrue : bexp
- | BFalse : bexp
- | BEq : aexp -> aexp -> bexp
- | BLe : aexp -> aexp -> bexp
- | BNot : bexp -> bexp
- | BAnd : bexp -> bexp -> bexp.
-
-Tactic Notation "bexp_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "BTrue" | Case_aux c "BFalse" | Case_aux c "BEq"
- | Case_aux c "BLe" | Case_aux c "BNot" | Case_aux c "BAnd" ].
-
-(* ################################################### *)
-(** ** Evaluation *)
-
-(** The arith and boolean evaluators can be extended to handle
- variables in the obvious way: *)
-
-Fixpoint aeval (st : state) (a : aexp) : nat :=
- match a with
- | ANum n => n
- | AId x => st x (* <----- NEW *)
- | APlus a1 a2 => (aeval st a1) + (aeval st a2)
- | AMinus a1 a2 => (aeval st a1) - (aeval st a2)
- | AMult a1 a2 => (aeval st a1) * (aeval st a2)
- end.
-
-Fixpoint beval (st : state) (b : bexp) : bool :=
- match b with
- | BTrue => true
- | BFalse => false
- | BEq a1 a2 => beq_nat (aeval st a1) (aeval st a2)
- | BLe a1 a2 => ble_nat (aeval st a1) (aeval st a2)
- | BNot b1 => negb (beval st b1)
- | BAnd b1 b2 => andb (beval st b1) (beval st b2)
- end.
-
-Example aexp1 :
- aeval (update empty_state X 5)
- (APlus (ANum 3) (AMult (AId X) (ANum 2)))
- = 13.
-Proof. reflexivity. Qed.
-
-Example bexp1 :
- beval (update empty_state X 5)
- (BAnd BTrue (BNot (BLe (AId X) (ANum 4))))
- = true.
-Proof. reflexivity. Qed.
-
-(* ####################################################### *)
-(** * Commands *)
-
-(** Now we are ready define the syntax and behavior of Imp
- _commands_ (often called _statements_). *)
-
-(* ################################################### *)
-(** ** Syntax *)
-
-(** Informally, commands [c] are described by the following BNF
- grammar:
- c ::= SKIP
- | x ::= a
- | c ;; c
- | WHILE b DO c END
- | IFB b THEN c ELSE c FI
-]]
-*)
-(**
- For example, here's the factorial function in Imp.
- Z ::= X;;
- Y ::= 1;;
- WHILE not (Z = 0) DO
- Y ::= Y * Z;;
- Z ::= Z - 1
- END
- When this command terminates, the variable [Y] will contain the
- factorial of the initial value of [X].
-*)
-
-(** Here is the formal definition of the syntax of commands: *)
-
-Inductive com : Type :=
- | CSkip : com
- | CAss : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com.
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";;"
- | Case_aux c "IFB" | Case_aux c "WHILE" ].
-
-(** As usual, we can use a few [Notation] declarations to make things
- more readable. We need to be a bit careful to avoid conflicts
- with Coq's built-in notations, so we'll keep this light -- in
- particular, we won't introduce any notations for [aexps] and
- [bexps] to avoid confusion with the numerical and boolean
- operators we've already defined. We use the keyword [IFB] for
- conditionals instead of [IF], for similar reasons. *)
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "x '::=' a" :=
- (CAss x a) (at level 60).
-Notation "c1 ;; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' c1 'THEN' c2 'ELSE' c3 'FI'" :=
- (CIf c1 c2 c3) (at level 80, right associativity).
-
-(** For example, here is the factorial function again, written as a
- formal definition to Coq: *)
-
-Definition fact_in_coq : com :=
- Z ::= AId X;;
- Y ::= ANum 1;;
- WHILE BNot (BEq (AId Z) (ANum 0)) DO
- Y ::= AMult (AId Y) (AId Z);;
- Z ::= AMinus (AId Z) (ANum 1)
- END.
-
-(* ####################################################### *)
-(** ** Examples *)
-
-(** Assignment: *)
-
-Definition plus2 : com :=
- X ::= (APlus (AId X) (ANum 2)).
-
-Definition XtimesYinZ : com :=
- Z ::= (AMult (AId X) (AId Y)).
-
-Definition subtract_slowly_body : com :=
- Z ::= AMinus (AId Z) (ANum 1) ;;
- X ::= AMinus (AId X) (ANum 1).
-
-
-(** *** Loops *)
-
-Definition subtract_slowly : com :=
- WHILE BNot (BEq (AId X) (ANum 0)) DO
- subtract_slowly_body
- END.
-
-Definition subtract_3_from_5_slowly : com :=
- X ::= ANum 3 ;;
- Z ::= ANum 5 ;;
- subtract_slowly.
-
-
-(** *** An infinite loop: *)
-
-Definition loop : com :=
- WHILE BTrue DO
- SKIP
- END.
-
-(* ################################################################ *)
-(** * Evaluation *)
-
-(** Next we need to define what it means to evaluate an Imp command.
- The fact that [WHILE] loops don't necessarily terminate makes defining
- an evaluation function tricky... *)
-
-(* #################################### *)
-(** ** Evaluation as a Function (Failed Attempt) *)
-
-(** Here's an attempt at defining an evaluation function for commands,
- omitting the [WHILE] case. *)
-
-Fixpoint ceval_fun_no_while (st : state) (c : com) : state :=
- match c with
- | SKIP =>
- st
- | x ::= a1 =>
- update st x (aeval st a1)
- | c1 ;; c2 =>
- let st' := ceval_fun_no_while st c1 in
- ceval_fun_no_while st' c2
- | IFB b THEN c1 ELSE c2 FI =>
- if (beval st b)
- then ceval_fun_no_while st c1
- else ceval_fun_no_while st c2
- | WHILE b DO c END =>
- st (* bogus *)
- end.
-(** In a traditional functional programming language like ML or
- Haskell we could write the [WHILE] case as follows:
-<<
- Fixpoint ceval_fun (st : state) (c : com) : state :=
- match c with
- ...
- | WHILE b DO c END =>
- if (beval st b1)
- then ceval_fun st (c1; WHILE b DO c END)
- else st
- end.
->>
- Coq doesn't accept such a definition ("Error: Cannot guess
- decreasing argument of fix") because the function we want to
- define is not guaranteed to terminate. Indeed, it doesn't always
- terminate: for example, the full version of the [ceval_fun]
- function applied to the [loop] program above would never
- terminate. Since Coq is not just a functional programming
- language, but also a consistent logic, any potentially
- non-terminating function needs to be rejected. Here is
- an (invalid!) Coq program showing what would go wrong if Coq
- allowed non-terminating recursive functions:
-<<
- Fixpoint loop_false (n : nat) : False := loop_false n.
->>
- That is, propositions like [False] would become provable
- (e.g. [loop_false 0] would be a proof of [False]), which
- would be a disaster for Coq's logical consistency.
-
- Thus, because it doesn't terminate on all inputs, the full version
- of [ceval_fun] cannot be written in Coq -- at least not without
- additional tricks (see chapter [ImpCEvalFun] if curious). *)
-
-(* #################################### *)
-(** ** Evaluation as a Relation *)
-
-(** Here's a better way: we define [ceval] as a _relation_ rather than
- a _function_ -- i.e., we define it in [Prop] instead of [Type], as
- we did for [aevalR] above. *)
-
-(** This is an important change. Besides freeing us from the awkward
- workarounds that would be needed to define evaluation as a
- function, it gives us a lot more flexibility in the definition.
- For example, if we added concurrency features to the language,
- we'd want the definition of evaluation to be non-deterministic --
- i.e., not only would it not be total, it would not even be a
- partial function! *)
-(** We'll use the notation [c / st || st'] for our [ceval] relation:
- [c / st || st'] means that executing program [c] in a starting
- state [st] results in an ending state [st']. This can be
- pronounced "[c] takes state [st] to [st']".
-
-*)
-(** *** Operational Semantics
- ---------------- (E_Skip)
- SKIP / st || st
-
- aeval st a1 = n
- -------------------------------- (E_Ass)
- x := a1 / st || (update st x n)
-
- c1 / st || st'
- c2 / st' || st''
- ------------------- (E_Seq)
- c1;;c2 / st || st''
-
- beval st b1 = true
- c1 / st || st'
- ------------------------------------- (E_IfTrue)
- IF b1 THEN c1 ELSE c2 FI / st || st'
-
- beval st b1 = false
- c2 / st || st'
- ------------------------------------- (E_IfFalse)
- IF b1 THEN c1 ELSE c2 FI / st || st'
-
- beval st b1 = false
- ------------------------------ (E_WhileEnd)
- WHILE b DO c END / st || st
-
- beval st b1 = true
- c / st || st'
- WHILE b DO c END / st' || st''
- --------------------------------- (E_WhileLoop)
- WHILE b DO c END / st || st''
-*)
-
-(** Here is the formal definition. (Make sure you understand
- how it corresponds to the inference rules.) *)
-
-Reserved Notation "c1 '/' st '||' st'" (at level 40, st at level 39).
-
-Inductive ceval : com -> state -> state -> Prop :=
- | E_Skip : forall st,
- SKIP / st || st
- | E_Ass : forall st a1 n x,
- aeval st a1 = n ->
- (x ::= a1) / st || (update st x n)
- | E_Seq : forall c1 c2 st st' st'',
- c1 / st || st' ->
- c2 / st' || st'' ->
- (c1 ;; c2) / st || st''
- | E_IfTrue : forall st st' b c1 c2,
- beval st b = true ->
- c1 / st || st' ->
- (IFB b THEN c1 ELSE c2 FI) / st || st'
- | E_IfFalse : forall st st' b c1 c2,
- beval st b = false ->
- c2 / st || st' ->
- (IFB b THEN c1 ELSE c2 FI) / st || st'
- | E_WhileEnd : forall b st c,
- beval st b = false ->
- (WHILE b DO c END) / st || st
- | E_WhileLoop : forall st st' st'' b c,
- beval st b = true ->
- c / st || st' ->
- (WHILE b DO c END) / st' || st'' ->
- (WHILE b DO c END) / st || st''
-
- where "c1 '/' st '||' st'" := (ceval c1 st st').
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip" | Case_aux c "E_Ass" | Case_aux c "E_Seq"
- | Case_aux c "E_IfTrue" | Case_aux c "E_IfFalse"
- | Case_aux c "E_WhileEnd" | Case_aux c "E_WhileLoop" ].
-
-(** *** *)
-(** The cost of defining evaluation as a relation instead of a
- function is that we now need to construct _proofs_ that some
- program evaluates to some result state, rather than just letting
- Coq's computation mechanism do it for us. *)
-
-Example ceval_example1:
- (X ::= ANum 2;;
- IFB BLe (AId X) (ANum 1)
- THEN Y ::= ANum 3
- ELSE Z ::= ANum 4
- FI)
- / empty_state
- || (update (update empty_state X 2) Z 4).
-Proof.
- (* We must supply the intermediate state *)
- apply E_Seq with (update empty_state X 2).
- Case "assignment command".
- apply E_Ass. reflexivity.
- Case "if command".
- apply E_IfFalse.
- reflexivity.
- apply E_Ass. reflexivity. Qed.
-
-(** **** Exercise: 2 stars (ceval_example2) *)
-Example ceval_example2:
- (X ::= ANum 0;; Y ::= ANum 1;; Z ::= ANum 2) / empty_state ||
- (update (update (update empty_state X 0) Y 1) Z 2).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (pup_to_n) *)
-(** Write an Imp program that sums the numbers from [1] to
- [X] (inclusive: [1 + 2 + ... + X]) in the variable [Y].
- Prove that this program executes as intended for X = 2
- (this latter part is trickier than you might expect). *)
-
-Definition pup_to_n : com :=
- (* FILL IN HERE *) admit.
-
-Theorem pup_to_2_ceval :
- pup_to_n / (update empty_state X 2) ||
- update (update (update (update (update (update empty_state
- X 2) Y 0) Y 2) X 1) Y 3) X 0.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ####################################################### *)
-(** ** Determinism of Evaluation *)
-
-(** Changing from a computational to a relational definition of
- evaluation is a good move because it allows us to escape from the
- artificial requirement (imposed by Coq's restrictions on
- [Fixpoint] definitions) that evaluation should be a total
- function. But it also raises a question: Is the second definition
- of evaluation actually a partial function? That is, is it
- possible that, beginning from the same state [st], we could
- evaluate some command [c] in different ways to reach two different
- output states [st'] and [st'']?
-
- In fact, this cannot happen: [ceval] is a partial function.
- Here's the proof: *)
-
-Theorem ceval_deterministic: forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 E1 E2.
- generalize dependent st2.
- ceval_cases (induction E1) Case;
- intros st2 E2; inversion E2; subst.
- Case "E_Skip". reflexivity.
- Case "E_Ass". reflexivity.
- Case "E_Seq".
- assert (st' = st'0) as EQ1.
- SCase "Proof of assertion". apply IHE1_1; assumption.
- subst st'0.
- apply IHE1_2. assumption.
- Case "E_IfTrue".
- SCase "b1 evaluates to true".
- apply IHE1. assumption.
- SCase "b1 evaluates to false (contradiction)".
- rewrite H in H5. inversion H5.
- Case "E_IfFalse".
- SCase "b1 evaluates to true (contradiction)".
- rewrite H in H5. inversion H5.
- SCase "b1 evaluates to false".
- apply IHE1. assumption.
- Case "E_WhileEnd".
- SCase "b1 evaluates to false".
- reflexivity.
- SCase "b1 evaluates to true (contradiction)".
- rewrite H in H2. inversion H2.
- Case "E_WhileLoop".
- SCase "b1 evaluates to false (contradiction)".
- rewrite H in H4. inversion H4.
- SCase "b1 evaluates to true".
- assert (st' = st'0) as EQ1.
- SSCase "Proof of assertion". apply IHE1_1; assumption.
- subst st'0.
- apply IHE1_2. assumption. Qed.
-
-(* ####################################################### *)
-(** * Reasoning About Imp Programs *)
-
-(** We'll get much deeper into systematic techniques for reasoning
- about Imp programs in the following chapters, but we can do quite
- a bit just working with the bare definitions. *)
-
-(* This section explores some examples. *)
-
-Theorem plus2_spec : forall st n st',
- st X = n ->
- plus2 / st || st' ->
- st' X = n + 2.
-Proof.
- intros st n st' HX Heval.
- (* Inverting Heval essentially forces Coq to expand one
- step of the ceval computation - in this case revealing
- that st' must be st extended with the new value of X,
- since plus2 is an assignment *)
- inversion Heval. subst. clear Heval. simpl.
- apply update_eq. Qed.
-
-(** **** Exercise: 3 stars (XtimesYinZ_spec) *)
-(** State and prove a specification of [XtimesYinZ]. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars (loop_never_stops) *)
-Theorem loop_never_stops : forall st st',
- ~(loop / st || st').
-Proof.
- intros st st' contra. unfold loop in contra.
- remember (WHILE BTrue DO SKIP END) as loopdef eqn:Heqloopdef.
- (* Proceed by induction on the assumed derivation showing that
- [loopdef] terminates. Most of the cases are immediately
- contradictory (and so can be solved in one step with
- [inversion]). *)
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (no_whilesR) *)
-(** Consider the definition of the [no_whiles] property below: *)
-
-Fixpoint no_whiles (c : com) : bool :=
- match c with
- | SKIP => true
- | _ ::= _ => true
- | c1 ;; c2 => andb (no_whiles c1) (no_whiles c2)
- | IFB _ THEN ct ELSE cf FI => andb (no_whiles ct) (no_whiles cf)
- | WHILE _ DO _ END => false
- end.
-
-(** This property yields [true] just on programs that
- have no while loops. Using [Inductive], write a property
- [no_whilesR] such that [no_whilesR c] is provable exactly when [c]
- is a program with no while loops. Then prove its equivalence
- with [no_whiles]. *)
-
-Inductive no_whilesR: com -> Prop :=
- (* FILL IN HERE *)
- .
-
-Theorem no_whiles_eqv:
- forall c, no_whiles c = true <-> no_whilesR c.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 4 stars (no_whiles_terminating) *)
-(** Imp programs that don't involve while loops always terminate.
- State and prove a theorem that says this. *)
-(** (Use either [no_whiles] or [no_whilesR], as you prefer.) *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ####################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 3 stars (stack_compiler) *)
-(** HP Calculators, programming languages like Forth and Postscript,
- and abstract machines like the Java Virtual Machine all evaluate
- arithmetic expressions using a stack. For instance, the expression
-<<
- (2*3)+(3*(4-2))
->>
- would be entered as
-<<
- 2 3 * 3 4 2 - * +
->>
- and evaluated like this:
-<<
- [] | 2 3 * 3 4 2 - * +
- [2] | 3 * 3 4 2 - * +
- [3, 2] | * 3 4 2 - * +
- [6] | 3 4 2 - * +
- [3, 6] | 4 2 - * +
- [4, 3, 6] | 2 - * +
- [2, 4, 3, 6] | - * +
- [2, 3, 6] | * +
- [6, 6] | +
- [12] |
->>
-
- The task of this exercise is to write a small compiler that
- translates [aexp]s into stack machine instructions.
-
- The instruction set for our stack language will consist of the
- following instructions:
- - [SPush n]: Push the number [n] on the stack.
- - [SLoad x]: Load the identifier [x] from the store and push it
- on the stack
- - [SPlus]: Pop the two top numbers from the stack, add them, and
- push the result onto the stack.
- - [SMinus]: Similar, but subtract.
- - [SMult]: Similar, but multiply. *)
-
-Inductive sinstr : Type :=
-| SPush : nat -> sinstr
-| SLoad : id -> sinstr
-| SPlus : sinstr
-| SMinus : sinstr
-| SMult : sinstr.
-
-(** Write a function to evaluate programs in the stack language. It
- takes as input a state, a stack represented as a list of
- numbers (top stack item is the head of the list), and a program
- represented as a list of instructions, and returns the stack after
- executing the program. Test your function on the examples below.
-
- Note that the specification leaves unspecified what to do when
- encountering an [SPlus], [SMinus], or [SMult] instruction if the
- stack contains less than two elements. In a sense, it is
- immaterial what we do, since our compiler will never emit such a
- malformed program. *)
-
-Fixpoint s_execute (st : state) (stack : list nat)
- (prog : list sinstr)
- : list nat :=
-(* FILL IN HERE *) admit.
-
-
-Example s_execute1 :
- s_execute empty_state []
- [SPush 5; SPush 3; SPush 1; SMinus]
- = [2; 5].
-(* FILL IN HERE *) Admitted.
-
-Example s_execute2 :
- s_execute (update empty_state X 3) [3;4]
- [SPush 4; SLoad X; SMult; SPlus]
- = [15; 4].
-(* FILL IN HERE *) Admitted.
-
-(** Next, write a function which compiles an [aexp] into a stack
- machine program. The effect of running the program should be the
- same as pushing the value of the expression on the stack. *)
-
-Fixpoint s_compile (e : aexp) : list sinstr :=
-(* FILL IN HERE *) admit.
-
-(** After you've defined [s_compile], uncomment the following to test
- that it works. *)
-
-(*
-Example s_compile1 :
- s_compile (AMinus (AId X) (AMult (ANum 2) (AId Y)))
- = [SLoad X; SPush 2; SLoad Y; SMult; SMinus].
-Proof. reflexivity. Qed.
-*)
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (stack_compiler_correct) *)
-(** The task of this exercise is to prove the correctness of the
- calculator implemented in the previous exercise. Remember that
- the specification left unspecified what to do when encountering an
- [SPlus], [SMinus], or [SMult] instruction if the stack contains
- less than two elements. (In order to make your correctness proof
- easier you may find it useful to go back and change your
- implementation!)
-
- Prove the following theorem, stating that the [compile] function
- behaves correctly. You will need to start by stating a more
- general lemma to get a usable induction hypothesis; the main
- theorem will then be a simple corollary of this lemma. *)
-
-
-Theorem s_compile_correct : forall (st : state) (e : aexp),
- s_execute st [] (s_compile e) = [ aeval st e ].
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 5 stars, advanced (break_imp) *)
-Module BreakImp.
-
-(** Imperative languages such as C or Java often have a [break] or
- similar statement for interrupting the execution of loops. In this
- exercise we will consider how to add [break] to Imp.
-
- First, we need to enrich the language of commands with an
- additional case. *)
-
-Inductive com : Type :=
- | CSkip : com
- | CBreak : com
- | CAss : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com.
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "BREAK" | Case_aux c "::=" | Case_aux c ";"
- | Case_aux c "IFB" | Case_aux c "WHILE" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "'BREAK'" :=
- CBreak.
-Notation "x '::=' a" :=
- (CAss x a) (at level 60).
-Notation "c1 ; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' c1 'THEN' c2 'ELSE' c3 'FI'" :=
- (CIf c1 c2 c3) (at level 80, right associativity).
-
-(** Next, we need to define the behavior of [BREAK]. Informally,
- whenever [BREAK] is executed in a sequence of commands, it stops
- the execution of that sequence and signals that the innermost
- enclosing loop (if any) should terminate. If there aren't any
- enclosing loops, then the whole program simply terminates. The
- final state should be the same as the one in which the [BREAK]
- statement was executed.
-
- One important point is what to do when there are multiple loops
- enclosing a given [BREAK]. In those cases, [BREAK] should only
- terminate the _innermost_ loop where it occurs. Thus, after
- executing the following piece of code...
- X ::= 0;
- Y ::= 1;
- WHILE 0 <> Y DO
- WHILE TRUE DO
- BREAK
- END;
- X ::= 1;
- Y ::= Y - 1
- END
- ... the value of [X] should be [1], and not [0].
-
- One way of expressing this behavior is to add another parameter to
- the evaluation relation that specifies whether evaluation of a
- command executes a [BREAK] statement: *)
-
-Inductive status : Type :=
- | SContinue : status
- | SBreak : status.
-
-Reserved Notation "c1 '/' st '||' s '/' st'"
- (at level 40, st, s at level 39).
-
-(** Intuitively, [c / st || s / st'] means that, if [c] is started in
- state [st], then it terminates in state [st'] and either signals
- that any surrounding loop (or the whole program) should exit
- immediately ([s = SBreak]) or that execution should continue
- normally ([s = SContinue]).
-
- The definition of the "[c / st || s / st']" relation is very
- similar to the one we gave above for the regular evaluation
- relation ([c / st || s / st']) -- we just need to handle the
- termination signals appropriately:
-
- - If the command is [SKIP], then the state doesn't change, and
- execution of any enclosing loop can continue normally.
-
- - If the command is [BREAK], the state stays unchanged, but we
- signal a [SBreak].
-
- - If the command is an assignment, then we update the binding for
- that variable in the state accordingly and signal that execution
- can continue normally.
-
- - If the command is of the form [IF b THEN c1 ELSE c2 FI], then
- the state is updated as in the original semantics of Imp, except
- that we also propagate the signal from the execution of
- whichever branch was taken.
-
- - If the command is a sequence [c1 ; c2], we first execute
- [c1]. If this yields a [SBreak], we skip the execution of [c2]
- and propagate the [SBreak] signal to the surrounding context;
- the resulting state should be the same as the one obtained by
- executing [c1] alone. Otherwise, we execute [c2] on the state
- obtained after executing [c1], and propagate the signal that was
- generated there.
-
- - Finally, for a loop of the form [WHILE b DO c END], the
- semantics is almost the same as before. The only difference is
- that, when [b] evaluates to true, we execute [c] and check the
- signal that it raises. If that signal is [SContinue], then the
- execution proceeds as in the original semantics. Otherwise, we
- stop the execution of the loop, and the resulting state is the
- same as the one resulting from the execution of the current
- iteration. In either case, since [BREAK] only terminates the
- innermost loop, [WHILE] signals [SContinue]. *)
-
-(** Based on the above description, complete the definition of the
- [ceval] relation. *)
-
-Inductive ceval : com -> state -> status -> state -> Prop :=
- | E_Skip : forall st,
- CSkip / st || SContinue / st
- (* FILL IN HERE *)
-
- where "c1 '/' st '||' s '/' st'" := (ceval c1 st s st').
-
-Tactic Notation "ceval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Skip"
- (* FILL IN HERE *)
- ].
-
-(** Now the following properties of your definition of [ceval]: *)
-
-Theorem break_ignore : forall c st st' s,
- (BREAK; c) / st || s / st' ->
- st = st'.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem while_continue : forall b c st st' s,
- (WHILE b DO c END) / st || s / st' ->
- s = SContinue.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem while_stops_on_break : forall b c st st',
- beval st b = true ->
- c / st || SBreak / st' ->
- (WHILE b DO c END) / st || SContinue / st'.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 3 stars, advanced, optional (while_break_true) *)
-Theorem while_break_true : forall b c st st',
- (WHILE b DO c END) / st || SContinue / st' ->
- beval st' b = true ->
- exists st'', c / st'' || SBreak / st'.
-Proof.
-(* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 4 stars, advanced, optional (ceval_deterministic) *)
-Theorem ceval_deterministic: forall (c:com) st st1 st2 s1 s2,
- c / st || s1 / st1 ->
- c / st || s2 / st2 ->
- st1 = st2 /\ s1 = s2.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-End BreakImp.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (short_circuit) *)
-(** Most modern programming languages use a "short-circuit" evaluation
- rule for boolean [and]: to evaluate [BAnd b1 b2], first evaluate
- [b1]. If it evaluates to [false], then the entire [BAnd]
- expression evaluates to [false] immediately, without evaluating
- [b2]. Otherwise, [b2] is evaluated to determine the result of the
- [BAnd] expression.
-
- Write an alternate version of [beval] that performs short-circuit
- evaluation of [BAnd] in this manner, and prove that it is
- equivalent to [beval]. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 4 stars, optional (add_for_loop) *)
-(** Add C-style [for] loops to the language of commands, update the
- [ceval] definition to define the semantics of [for] loops, and add
- cases for [for] loops as needed so that all the proofs in this file
- are accepted by Coq.
-
- A [for] loop should be parameterized by (a) a statement executed
- initially, (b) a test that is run on each iteration of the loop to
- determine whether the loop should continue, (c) a statement
- executed at the end of each loop iteration, and (d) a statement
- that makes up the body of the loop. (You don't need to worry
- about making up a concrete Notation for [for] loops, but feel free
- to play with this too if you like.) *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-(* <$Date: 2014-02-22 09:43:41 -0500 (Sat, 22 Feb 2014) $ *)
-
diff --git a/ImpCEvalFun.html b/ImpCEvalFun.html
deleted file mode 100644
index c2c97b3..0000000
--- a/ImpCEvalFun.html
+++ /dev/null
@@ -1,508 +0,0 @@
-
-
-
-
-
-ImpCEvalFun: Evaluation Function for Imp
-
-
-
-
-
-
-
-In a traditional functional programming language like ML or
- Haskell we could write the WHILE case as follows:
-
- | WHILE b1 DO c1 END =>
- if (beval st b1)
- then ceval_step1 st (c1;; WHILE b1 DO c1 END)
- else st
-
- Coq doesn't accept such a definition (Error:Cannotguess
- decreasingargumentoffix) because the function we want to
- define is not guaranteed to terminate. Indeed, the changed
- ceval_step1 function applied to the loop program from Imp.v would
- never terminate. Since Coq is not just a functional programming
- language, but also a consistent logic, any potentially
- non-terminating function needs to be rejected. Here is an
- invalid(!) Coq program showing what would go wrong if Coq allowed
- non-terminating recursive functions:
-
- That is, propositions like False would become
- provable (e.g. loop_false0 would be a proof of False), which
- would be a disaster for Coq's logical consistency.
-
-
-
- Thus, because it doesn't terminate on all inputs, the full version
- of ceval_step1 cannot be written in Coq — at least not
- without one additional trick...
-
-
- Second try, using an extra numeric argument as a "step index" to
- ensure that evaluation always terminates.
-
-Note: It is tempting to think that the index i here is
- counting the "number of steps of evaluation." But if you look
- closely you'll see that this is not the case: for example, in the
- rule for sequencing, the same i is passed to both recursive
- calls. Understanding the exact way that i is treated will be
- important in the proof of ceval__ceval_step, which is given as
- an exercise below.
-
-
- Third try, returning an optionstate instead of just a state
- so that we can distinguish between normal and abnormal
- termination.
-
-
-
-
-Fixpointceval_step3 (st : state) (c : com) (i : nat)
- : optionstate :=
- matchiwith
- | O ⇒ None
- | Si' ⇒
- matchcwith
- | SKIP ⇒
- Somest
- | l ::= a1 ⇒
- Some (updatestl (aevalsta1))
- | c1 ;; c2 ⇒
- match (ceval_step3stc1i') with
- | Somest' ⇒ ceval_step3st'c2i'
- | None ⇒ None
- end
- | IFBbTHENc1ELSEc2FI ⇒
- if (bevalstb)
- thenceval_step3stc1i'
- elseceval_step3stc2i'
- | WHILEb1DOc1END ⇒
- if (bevalstb1)
- thenmatch (ceval_step3stc1i') with
- | Somest' ⇒ ceval_step3st'ci'
- | None ⇒ None
- end
- elseSomest
- end
- end.
-
-
-
-
-
-We can improve the readability of this definition by introducing a
- bit of auxiliary notation to hide the "plumbing" involved in
- repeatedly matching against optional states.
-
- Write an Imp program that sums the numbers from 1 to
- X (inclusive: 1+2+...+X) in the variable Y. Make sure
- your solution satisfies the test that follows.
-
-
-
-
-Definitionpup_to_n : com :=
- (* FILL IN HERE *)admit.
-
-
-(*
-Example pup_to_n_1 :
- test_ceval (update empty_state X 5) pup_to_n
- = Some (0, 15, 0).
-Proof. reflexivity. Qed.
-*)
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (peven)
- Write a While program that sets Z to 0 if X is even and
- sets Z to 1 otherwise. Use ceval_test to test your
- program.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Equivalence of Relational and Step-Indexed Evaluation
-
-
-
- As with arithmetic and boolean expressions, we'd hope that
- the two alternative definitions of evaluation actually boil down
- to the same thing. This section shows that this is the case.
- Make sure you understand the statements of the theorems and can
- follow the structure of the proofs.
-
- Write an informal proof of ceval_step__ceval, following the
- usual template. (The template for case analysis on an inductively
- defined value should look the same as for induction, except that
- there is no induction hypothesis.) Make your proof communicate
- the main ideas to a human reader; do not simply transcribe the
- steps of the formal proof.
-
-
-
- Here's a slicker proof showing that the evaluation relation is
- deterministic, using the fact that the relational and step-indexed
- definition of evaluation are the same.
-
-
-
-
\ No newline at end of file
diff --git a/ImpCEvalFun.v b/ImpCEvalFun.v
deleted file mode 100644
index 1ddc4d2..0000000
--- a/ImpCEvalFun.v
+++ /dev/null
@@ -1,362 +0,0 @@
-(** * ImpCEvalFun: Evaluation Function for Imp *)
-
-(* $Date: 2013-07-01 18:48:47 -0400 (Mon, 01 Jul 2013) $ *)
-
-(* #################################### *)
-(** ** Evaluation Function *)
-
-Require Import Imp.
-
-(** Here's a first try at an evaluation function for commands,
- omitting [WHILE]. *)
-
-Fixpoint ceval_step1 (st : state) (c : com) : state :=
- match c with
- | SKIP =>
- st
- | l ::= a1 =>
- update st l (aeval st a1)
- | c1 ;; c2 =>
- let st' := ceval_step1 st c1 in
- ceval_step1 st' c2
- | IFB b THEN c1 ELSE c2 FI =>
- if (beval st b)
- then ceval_step1 st c1
- else ceval_step1 st c2
- | WHILE b1 DO c1 END =>
- st (* bogus *)
- end.
-
-(** In a traditional functional programming language like ML or
- Haskell we could write the WHILE case as follows:
-<<
- | WHILE b1 DO c1 END =>
- if (beval st b1)
- then ceval_step1 st (c1;; WHILE b1 DO c1 END)
- else st
->>
- Coq doesn't accept such a definition ([Error: Cannot guess
- decreasing argument of fix]) because the function we want to
- define is not guaranteed to terminate. Indeed, the changed
- [ceval_step1] function applied to the [loop] program from [Imp.v] would
- never terminate. Since Coq is not just a functional programming
- language, but also a consistent logic, any potentially
- non-terminating function needs to be rejected. Here is an
- invalid(!) Coq program showing what would go wrong if Coq allowed
- non-terminating recursive functions:
-<<
- Fixpoint loop_false (n : nat) : False := loop_false n.
->>
- That is, propositions like [False] would become
- provable (e.g. [loop_false 0] would be a proof of [False]), which
- would be a disaster for Coq's logical consistency.
-
- Thus, because it doesn't terminate on all inputs, the full version
- of [ceval_step1] cannot be written in Coq -- at least not
- without one additional trick... *)
-
-
-(** Second try, using an extra numeric argument as a "step index" to
- ensure that evaluation always terminates. *)
-
-Fixpoint ceval_step2 (st : state) (c : com) (i : nat) : state :=
- match i with
- | O => empty_state
- | S i' =>
- match c with
- | SKIP =>
- st
- | l ::= a1 =>
- update st l (aeval st a1)
- | c1 ;; c2 =>
- let st' := ceval_step2 st c1 i' in
- ceval_step2 st' c2 i'
- | IFB b THEN c1 ELSE c2 FI =>
- if (beval st b)
- then ceval_step2 st c1 i'
- else ceval_step2 st c2 i'
- | WHILE b1 DO c1 END =>
- if (beval st b1)
- then let st' := ceval_step2 st c1 i' in
- ceval_step2 st' c i'
- else st
- end
- end.
-
-(** _Note_: It is tempting to think that the index [i] here is
- counting the "number of steps of evaluation." But if you look
- closely you'll see that this is not the case: for example, in the
- rule for sequencing, the same [i] is passed to both recursive
- calls. Understanding the exact way that [i] is treated will be
- important in the proof of [ceval__ceval_step], which is given as
- an exercise below. *)
-
-(** Third try, returning an [option state] instead of just a [state]
- so that we can distinguish between normal and abnormal
- termination. *)
-
-Fixpoint ceval_step3 (st : state) (c : com) (i : nat)
- : option state :=
- match i with
- | O => None
- | S i' =>
- match c with
- | SKIP =>
- Some st
- | l ::= a1 =>
- Some (update st l (aeval st a1))
- | c1 ;; c2 =>
- match (ceval_step3 st c1 i') with
- | Some st' => ceval_step3 st' c2 i'
- | None => None
- end
- | IFB b THEN c1 ELSE c2 FI =>
- if (beval st b)
- then ceval_step3 st c1 i'
- else ceval_step3 st c2 i'
- | WHILE b1 DO c1 END =>
- if (beval st b1)
- then match (ceval_step3 st c1 i') with
- | Some st' => ceval_step3 st' c i'
- | None => None
- end
- else Some st
- end
- end.
-
-(** We can improve the readability of this definition by introducing a
- bit of auxiliary notation to hide the "plumbing" involved in
- repeatedly matching against optional states. *)
-
-Notation "'LETOPT' x <== e1 'IN' e2"
- := (match e1 with
- | Some x => e2
- | None => None
- end)
- (right associativity, at level 60).
-
-Fixpoint ceval_step (st : state) (c : com) (i : nat)
- : option state :=
- match i with
- | O => None
- | S i' =>
- match c with
- | SKIP =>
- Some st
- | l ::= a1 =>
- Some (update st l (aeval st a1))
- | c1 ;; c2 =>
- LETOPT st' <== ceval_step st c1 i' IN
- ceval_step st' c2 i'
- | IFB b THEN c1 ELSE c2 FI =>
- if (beval st b)
- then ceval_step st c1 i'
- else ceval_step st c2 i'
- | WHILE b1 DO c1 END =>
- if (beval st b1)
- then LETOPT st' <== ceval_step st c1 i' IN
- ceval_step st' c i'
- else Some st
- end
- end.
-
-Definition test_ceval (st:state) (c:com) :=
- match ceval_step st c 500 with
- | None => None
- | Some st => Some (st X, st Y, st Z)
- end.
-
-(* Eval compute in
- (test_ceval empty_state
- (X ::= ANum 2;;
- IFB BLe (AId X) (ANum 1)
- THEN Y ::= ANum 3
- ELSE Z ::= ANum 4
- FI)).
- ====>
- Some (2, 0, 4) *)
-
-(** **** Exercise: 2 stars (pup_to_n) *)
-(** Write an Imp program that sums the numbers from [1] to
- [X] (inclusive: [1 + 2 + ... + X]) in the variable [Y]. Make sure
- your solution satisfies the test that follows. *)
-
-Definition pup_to_n : com :=
- (* FILL IN HERE *) admit.
-
-(*
-Example pup_to_n_1 :
- test_ceval (update empty_state X 5) pup_to_n
- = Some (0, 15, 0).
-Proof. reflexivity. Qed.
-*)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (peven) *)
-(** Write a [While] program that sets [Z] to [0] if [X] is even and
- sets [Z] to [1] otherwise. Use [ceval_test] to test your
- program. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ################################################################ *)
-(** ** Equivalence of Relational and Step-Indexed Evaluation *)
-
-(** As with arithmetic and boolean expressions, we'd hope that
- the two alternative definitions of evaluation actually boil down
- to the same thing. This section shows that this is the case.
- Make sure you understand the statements of the theorems and can
- follow the structure of the proofs. *)
-
-Theorem ceval_step__ceval: forall c st st',
- (exists i, ceval_step st c i = Some st') ->
- c / st || st'.
-Proof.
- intros c st st' H.
- inversion H as [i E].
- clear H.
- generalize dependent st'.
- generalize dependent st.
- generalize dependent c.
- induction i as [| i' ].
-
- Case "i = 0 -- contradictory".
- intros c st st' H. inversion H.
-
- Case "i = S i'".
- intros c st st' H.
- com_cases (destruct c) SCase;
- simpl in H; inversion H; subst; clear H.
- SCase "SKIP". apply E_Skip.
- SCase "::=". apply E_Ass. reflexivity.
-
- SCase ";;".
- destruct (ceval_step st c1 i') eqn:Heqr1.
- SSCase "Evaluation of r1 terminates normally".
- apply E_Seq with s.
- apply IHi'. rewrite Heqr1. reflexivity.
- apply IHi'. simpl in H1. assumption.
- SSCase "Otherwise -- contradiction".
- inversion H1.
-
- SCase "IFB".
- destruct (beval st b) eqn:Heqr.
- SSCase "r = true".
- apply E_IfTrue. rewrite Heqr. reflexivity.
- apply IHi'. assumption.
- SSCase "r = false".
- apply E_IfFalse. rewrite Heqr. reflexivity.
- apply IHi'. assumption.
-
- SCase "WHILE". destruct (beval st b) eqn :Heqr.
- SSCase "r = true".
- destruct (ceval_step st c i') eqn:Heqr1.
- SSSCase "r1 = Some s".
- apply E_WhileLoop with s. rewrite Heqr. reflexivity.
- apply IHi'. rewrite Heqr1. reflexivity.
- apply IHi'. simpl in H1. assumption.
- SSSCase "r1 = None".
- inversion H1.
- SSCase "r = false".
- inversion H1.
- apply E_WhileEnd.
- rewrite <- Heqr. subst. reflexivity. Qed.
-
-(** **** Exercise: 4 stars (ceval_step__ceval_inf) *)
-(** Write an informal proof of [ceval_step__ceval], following the
- usual template. (The template for case analysis on an inductively
- defined value should look the same as for induction, except that
- there is no induction hypothesis.) Make your proof communicate
- the main ideas to a human reader; do not simply transcribe the
- steps of the formal proof.
-
-(* FILL IN HERE *)
-[]
-*)
-
-Theorem ceval_step_more: forall i1 i2 st st' c,
- i1 <= i2 ->
- ceval_step st c i1 = Some st' ->
- ceval_step st c i2 = Some st'.
-Proof.
-induction i1 as [|i1']; intros i2 st st' c Hle Hceval.
- Case "i1 = 0".
- simpl in Hceval. inversion Hceval.
- Case "i1 = S i1'".
- destruct i2 as [|i2']. inversion Hle.
- assert (Hle': i1' <= i2') by omega.
- com_cases (destruct c) SCase.
- SCase "SKIP".
- simpl in Hceval. inversion Hceval.
- reflexivity.
- SCase "::=".
- simpl in Hceval. inversion Hceval.
- reflexivity.
- SCase ";;".
- simpl in Hceval. simpl.
- destruct (ceval_step st c1 i1') eqn:Heqst1'o.
- SSCase "st1'o = Some".
- apply (IHi1' i2') in Heqst1'o; try assumption.
- rewrite Heqst1'o. simpl. simpl in Hceval.
- apply (IHi1' i2') in Hceval; try assumption.
- SSCase "st1'o = None".
- inversion Hceval.
-
- SCase "IFB".
- simpl in Hceval. simpl.
- destruct (beval st b); apply (IHi1' i2') in Hceval; assumption.
-
- SCase "WHILE".
- simpl in Hceval. simpl.
- destruct (beval st b); try assumption.
- destruct (ceval_step st c i1') eqn: Heqst1'o.
- SSCase "st1'o = Some".
- apply (IHi1' i2') in Heqst1'o; try assumption.
- rewrite -> Heqst1'o. simpl. simpl in Hceval.
- apply (IHi1' i2') in Hceval; try assumption.
- SSCase "i1'o = None".
- simpl in Hceval. inversion Hceval. Qed.
-
-(** **** Exercise: 3 stars (ceval__ceval_step) *)
-(** Finish the following proof. You'll need [ceval_step_more] in a
- few places, as well as some basic facts about [<=] and [plus]. *)
-
-Theorem ceval__ceval_step: forall c st st',
- c / st || st' ->
- exists i, ceval_step st c i = Some st'.
-Proof.
- intros c st st' Hce.
- ceval_cases (induction Hce) Case.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-Theorem ceval_and_ceval_step_coincide: forall c st st',
- c / st || st'
- <-> exists i, ceval_step st c i = Some st'.
-Proof.
- intros c st st'.
- split. apply ceval__ceval_step. apply ceval_step__ceval.
-Qed.
-
-(* ####################################################### *)
-(** ** Determinism of Evaluation (Simpler Proof) *)
-
-(** Here's a slicker proof showing that the evaluation relation is
- deterministic, using the fact that the relational and step-indexed
- definition of evaluation are the same. *)
-
-Theorem ceval_deterministic' : forall c st st1 st2,
- c / st || st1 ->
- c / st || st2 ->
- st1 = st2.
-Proof.
- intros c st st1 st2 He1 He2.
- apply ceval__ceval_step in He1.
- apply ceval__ceval_step in He2.
- inversion He1 as [i1 E1].
- inversion He2 as [i2 E2].
- apply ceval_step_more with (i2 := i1 + i2) in E1.
- apply ceval_step_more with (i2 := i1 + i2) in E2.
- rewrite E1 in E2. inversion E2. reflexivity.
- omega. omega. Qed.
diff --git a/ImpParser.html b/ImpParser.html
deleted file mode 100644
index e5cb327..0000000
--- a/ImpParser.html
+++ /dev/null
@@ -1,580 +0,0 @@
-
-
-
-
-
-ImpParser: Lexing and Parsing in Coq
-
-
-
-
-
-
-
-The development of the Imp language in Imp.v completely ignores
- issues of concrete syntax — how an ascii string that a programmer
- might write gets translated into the abstract syntax trees defined
- by the datatypes aexp, bexp, and com. In this file we
- illustrate how the rest of the story can be filled in by building
- a simple lexical analyzer and parser using Coq's functional
- programming facilities.
-
-
-
- This development is not intended to be understood in detail: the
- explanations are fairly terse and there are no exercises. The
- main point is simply to demonstrate that it can be done. You are
- invited to look through the code — most of it is not very
- complicated, though the parser relies on some "monadic"
- programming idioms that may require a little work to make out —
- but most readers will probably want to just skip down to the
- Examples section at the very end to get the punchline.
-
-
-
-(* Build a mapping from tokens to nats. A real parser would do
- this incrementally as it encountered new symbols, but passing
- around the symbol table inside the parsing functions is a bit
- inconvenient, so instead we do it as a first pass. *)
-Fixpointbuild_symtable (xs : listtoken) (n : nat) : (token→nat) :=
- matchxswith
- | [] ⇒ (funs ⇒ n)
- | x::xs ⇒
- if (forallbisLowerAlpha (list_of_stringx))
- then (funs ⇒ ifstring_decsxthennelse (build_symtablexs (Sn) s))
- elsebuild_symtablexsn
- end.
-
-
-
-
-
-
Generic Combinators for Building Parsers
-
-
-
-
-
-OpenScopestring_scope.
-
-
-Definitionparser (T : Type) :=
- listtoken→optionE (T × listtoken).
-
-
-Fixpointmany_helper {T} (p : parserT) accstepsxs :=
-matchsteps, pxswith
-| 0, _ ⇒ NoneE "Too many recursive calls"
-| _, NoneE_ ⇒ SomeE ((revacc), xs)
-| Ssteps', SomeE (t, xs') ⇒ many_helperp (t::acc) steps'xs'
-end.
-
-
-(* A (step-indexed) parser which expects zero or more ps *)
-Fixpointmany {T} (p : parserT) (steps : nat) : parser (listT) :=
- many_helperp [] steps.
-
-
-(* A parser which expects a given token, followed by p *)
-DefinitionfirstExpect {T} (t : token) (p : parserT) : parserT :=
- funxs ⇒ matchxswith
- | x::xs' ⇒ ifstring_decxt
- thenpxs'
- elseNoneE ("expected '" ++ t ++ "'.")
- | [] ⇒ NoneE ("expected '" ++ t ++ "'.")
- end.
-
-
-(* A parser which expects a particular token *)
-Definitionexpect (t : token) : parserunit :=
- firstExpectt (funxs ⇒ SomeE(tt, xs)).
-
-
-
-
-
-(*
-Eval compute in parse "
- IF x == y + 1 + 2 - y * 6 + 3 THEN
- x := x * 1;;
- y := 0
- ELSE
- SKIP
- END ".
-====>
- SomeE
- (IFB BEq (AId (Id 0))
- (APlus
- (AMinus (APlus (APlus (AId (Id 1)) (ANum 1)) (ANum 2))
- (AMult (AId (Id 1)) (ANum 6)))
- (ANum 3))
- THEN Id 0 ::= AMult (AId (Id 0)) (ANum 1);; Id 1 ::= ANum 0
- ELSE SKIP FI, )
-*)
-
-
-(*
-Eval compute in parse "
- SKIP;;
- z:=x*y*(x*x);;
- WHILE x==x DO
- IF z <= z*z && not x == 2 THEN
- x := z;;
- y := z
- ELSE
- SKIP
- END;;
- SKIP
- END;;
- x:=z ".
-====>
- SomeE
- (SKIP;;
- Id 0 ::= AMult (AMult (AId (Id 1)) (AId (Id 2)))
- (AMult (AId (Id 1)) (AId (Id 1)));;
- WHILE BEq (AId (Id 1)) (AId (Id 1)) DO
- IFB BAnd (BLe (AId (Id 0)) (AMult (AId (Id 0)) (AId (Id 0))))
- (BNot (BEq (AId (Id 1)) (ANum 2)))
- THEN Id 1 ::= AId (Id 0);; Id 2 ::= AId (Id 0)
- ELSE SKIP FI;;
- SKIP
- END;;
- Id 1 ::= AId (Id 0),
- )
-*)
-
-
-(*
-Eval compute in parse "
- SKIP;;
- z:=x*y*(x*x);;
- WHILE x==x DO
- IF z <= z*z && not x == 2 THEN
- x := z;;
- y := z
- ELSE
- SKIP
- END;;
- SKIP
- END;;
- x:=z ".
-=====>
- SomeE
- (SKIP;;
- Id 0 ::= AMult (AMult (AId (Id 1)) (AId (Id 2)))
- (AMult (AId (Id 1)) (AId (Id 1)));;
- WHILE BEq (AId (Id 1)) (AId (Id 1)) DO
- IFB BAnd (BLe (AId (Id 0)) (AMult (AId (Id 0)) (AId (Id 0))))
- (BNot (BEq (AId (Id 1)) (ANum 2)))
- THEN Id 1 ::= AId (Id 0);;
- Id 2 ::= AId (Id 0)
- ELSE SKIP
- FI;;
- SKIP
- END;;
- Id 1 ::= AId (Id 0),
- ).
-*)
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/ImpParser.v b/ImpParser.v
deleted file mode 100644
index 4a2829b..0000000
--- a/ImpParser.v
+++ /dev/null
@@ -1,444 +0,0 @@
-(** * ImpParser: Lexing and Parsing in Coq *)
-
-(* $Date: 2013-07-01 18:48:47 -0400 (Mon, 01 Jul 2013) $ *)
-
-(** The development of the [Imp] language in Imp.v completely ignores
- issues of concrete syntax -- how an ascii string that a programmer
- might write gets translated into the abstract syntax trees defined
- by the datatypes [aexp], [bexp], and [com]. In this file we
- illustrate how the rest of the story can be filled in by building
- a simple lexical analyzer and parser using Coq's functional
- programming facilities.
-
- This development is not intended to be understood in detail: the
- explanations are fairly terse and there are no exercises. The
- main point is simply to demonstrate that it can be done. You are
- invited to look through the code -- most of it is not very
- complicated, though the parser relies on some "monadic"
- programming idioms that may require a little work to make out --
- but most readers will probably want to just skip down to the
- Examples section at the very end to get the punchline. *)
-
-(* ####################################################### *)
-(** * Internals *)
-
-Require Import SfLib.
-Require Import Imp.
-
-Require Import String.
-Require Import Ascii.
-
-Open Scope list_scope.
-
-(* ####################################################### *)
-(** ** Lexical Analysis *)
-
-Definition isWhite (c : ascii) : bool :=
- let n := nat_of_ascii c in
- orb (orb (beq_nat n 32) (* space *)
- (beq_nat n 9)) (* tab *)
- (orb (beq_nat n 10) (* linefeed *)
- (beq_nat n 13)). (* Carriage return. *)
-
-Notation "x '<=?' y" := (ble_nat x y)
- (at level 70, no associativity) : nat_scope.
-
-Definition isLowerAlpha (c : ascii) : bool :=
- let n := nat_of_ascii c in
- andb (97 <=? n) (n <=? 122).
-
-Definition isAlpha (c : ascii) : bool :=
- let n := nat_of_ascii c in
- orb (andb (65 <=? n) (n <=? 90))
- (andb (97 <=? n) (n <=? 122)).
-
-Definition isDigit (c : ascii) : bool :=
- let n := nat_of_ascii c in
- andb (48 <=? n) (n <=? 57).
-
-Inductive chartype := white | alpha | digit | other.
-
-Definition classifyChar (c : ascii) : chartype :=
- if isWhite c then
- white
- else if isAlpha c then
- alpha
- else if isDigit c then
- digit
- else
- other.
-
-Fixpoint list_of_string (s : string) : list ascii :=
- match s with
- | EmptyString => []
- | String c s => c :: (list_of_string s)
- end.
-
-Fixpoint string_of_list (xs : list ascii) : string :=
- fold_right String EmptyString xs.
-
-Definition token := string.
-
-Fixpoint tokenize_helper (cls : chartype) (acc xs : list ascii)
- : list (list ascii) :=
- let tk := match acc with [] => [] | _::_ => [rev acc] end in
- match xs with
- | [] => tk
- | (x::xs') =>
- match cls, classifyChar x, x with
- | _, _, "(" => tk ++ ["("]::(tokenize_helper other [] xs')
- | _, _, ")" => tk ++ [")"]::(tokenize_helper other [] xs')
- | _, white, _ => tk ++ (tokenize_helper white [] xs')
- | alpha,alpha,x => tokenize_helper alpha (x::acc) xs'
- | digit,digit,x => tokenize_helper digit (x::acc) xs'
- | other,other,x => tokenize_helper other (x::acc) xs'
- | _,tp,x => tk ++ (tokenize_helper tp [x] xs')
- end
- end %char.
-
-Definition tokenize (s : string) : list string :=
- map string_of_list (tokenize_helper white [] (list_of_string s)).
-
-Example tokenize_ex1 :
- tokenize "abc12==3 223*(3+(a+c))" %string
- = ["abc"; "12"; "=="; "3"; "223";
- "*"; "("; "3"; "+"; "(";
- "a"; "+"; "c"; ")"; ")"]%string.
-Proof. reflexivity. Qed.
-
-(* ####################################################### *)
-(** ** Parsing *)
-
-(* ####################################################### *)
-(** *** Options with Errors *)
-
-(* An option with error messages. *)
-Inductive optionE (X:Type) : Type :=
- | SomeE : X -> optionE X
- | NoneE : string -> optionE X.
-
-Implicit Arguments SomeE [[X]].
-Implicit Arguments NoneE [[X]].
-
-(* Some syntactic sugar to make writing nested match-expressions on
- optionE more convenient. *)
-
-Notation "'DO' ( x , y ) <== e1 ; e2"
- := (match e1 with
- | SomeE (x,y) => e2
- | NoneE err => NoneE err
- end)
- (right associativity, at level 60).
-
-Notation "'DO' ( x , y ) <-- e1 ; e2 'OR' e3"
- := (match e1 with
- | SomeE (x,y) => e2
- | NoneE err => e3
- end)
- (right associativity, at level 60, e2 at next level).
-
-(* ####################################################### *)
-(** *** Symbol Table *)
-
-(* Build a mapping from [tokens] to [nats]. A real parser would do
- this incrementally as it encountered new symbols, but passing
- around the symbol table inside the parsing functions is a bit
- inconvenient, so instead we do it as a first pass. *)
-Fixpoint build_symtable (xs : list token) (n : nat) : (token -> nat) :=
- match xs with
- | [] => (fun s => n)
- | x::xs =>
- if (forallb isLowerAlpha (list_of_string x))
- then (fun s => if string_dec s x then n else (build_symtable xs (S n) s))
- else build_symtable xs n
- end.
-
-(* ####################################################### *)
-(** *** Generic Combinators for Building Parsers *)
-
-Open Scope string_scope.
-
-Definition parser (T : Type) :=
- list token -> optionE (T * list token).
-
-Fixpoint many_helper {T} (p : parser T) acc steps xs :=
-match steps, p xs with
-| 0, _ => NoneE "Too many recursive calls"
-| _, NoneE _ => SomeE ((rev acc), xs)
-| S steps', SomeE (t, xs') => many_helper p (t::acc) steps' xs'
-end.
-
-(* A (step-indexed) parser which expects zero or more [p]s *)
-Fixpoint many {T} (p : parser T) (steps : nat) : parser (list T) :=
- many_helper p [] steps.
-
-(* A parser which expects a given token, followed by p *)
-Definition firstExpect {T} (t : token) (p : parser T) : parser T :=
- fun xs => match xs with
- | x::xs' => if string_dec x t
- then p xs'
- else NoneE ("expected '" ++ t ++ "'.")
- | [] => NoneE ("expected '" ++ t ++ "'.")
- end.
-
-(* A parser which expects a particular token *)
-Definition expect (t : token) : parser unit :=
- firstExpect t (fun xs => SomeE(tt, xs)).
-
-(* ####################################################### *)
-(** *** A Recursive-Descent Parser for Imp *)
-
-(* Identifiers *)
-Definition parseIdentifier (symtable :string->nat) (xs : list token)
- : optionE (id * list token) :=
-match xs with
-| [] => NoneE "Expected identifier"
-| x::xs' =>
- if forallb isLowerAlpha (list_of_string x) then
- SomeE (Id (symtable x), xs')
- else
- NoneE ("Illegal identifier:'" ++ x ++ "'")
-end.
-
-(* Numbers *)
-Definition parseNumber (xs : list token) : optionE (nat * list token) :=
-match xs with
-| [] => NoneE "Expected number"
-| x::xs' =>
- if forallb isDigit (list_of_string x) then
- SomeE (fold_left (fun n d =>
- 10 * n + (nat_of_ascii d - nat_of_ascii "0"%char))
- (list_of_string x)
- 0,
- xs')
- else
- NoneE "Expected number"
-end.
-
-(* Parse arithmetic expressions *)
-Fixpoint parsePrimaryExp (steps:nat) symtable (xs : list token)
- : optionE (aexp * list token) :=
- match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (i, rest) <-- parseIdentifier symtable xs ;
- SomeE (AId i, rest)
- OR DO (n, rest) <-- parseNumber xs ;
- SomeE (ANum n, rest)
- OR (DO (e, rest) <== firstExpect "(" (parseSumExp steps' symtable) xs;
- DO (u, rest') <== expect ")" rest ;
- SomeE(e,rest'))
- end
-with parseProductExp (steps:nat) symtable (xs : list token) :=
- match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (e, rest) <==
- parsePrimaryExp steps' symtable xs ;
- DO (es, rest') <==
- many (firstExpect "*" (parsePrimaryExp steps' symtable)) steps' rest;
- SomeE (fold_left AMult es e, rest')
- end
-with parseSumExp (steps:nat) symtable (xs : list token) :=
- match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (e, rest) <==
- parseProductExp steps' symtable xs ;
- DO (es, rest') <==
- many (fun xs =>
- DO (e,rest') <--
- firstExpect "+" (parseProductExp steps' symtable) xs;
- SomeE ( (true, e), rest')
- OR DO (e,rest') <==
- firstExpect "-" (parseProductExp steps' symtable) xs;
- SomeE ( (false, e), rest'))
- steps' rest;
- SomeE (fold_left (fun e0 term =>
- match term with
- (true, e) => APlus e0 e
- | (false, e) => AMinus e0 e
- end)
- es e,
- rest')
- end.
-
-Definition parseAExp := parseSumExp.
-
-(* Parsing boolean expressions. *)
-Fixpoint parseAtomicExp (steps:nat) (symtable : string->nat) (xs : list token) :=
-match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (u,rest) <-- expect "true" xs;
- SomeE (BTrue,rest)
- OR DO (u,rest) <-- expect "false" xs;
- SomeE (BFalse,rest)
- OR DO (e,rest) <-- firstExpect "not" (parseAtomicExp steps' symtable) xs;
- SomeE (BNot e, rest)
- OR DO (e,rest) <-- firstExpect "(" (parseConjunctionExp steps' symtable) xs;
- (DO (u,rest') <== expect ")" rest; SomeE (e, rest'))
- OR DO (e, rest) <== parseProductExp steps' symtable xs ;
- (DO (e', rest') <--
- firstExpect "==" (parseAExp steps' symtable) rest ;
- SomeE (BEq e e', rest')
- OR DO (e', rest') <--
- firstExpect "<=" (parseAExp steps' symtable) rest ;
- SomeE (BLe e e', rest')
- OR
- NoneE "Expected '==' or '<=' after arithmetic expression")
-end
-with parseConjunctionExp (steps:nat) (symtable : string->nat) (xs : list token) :=
- match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (e, rest) <==
- parseAtomicExp steps' symtable xs ;
- DO (es, rest') <==
- many (firstExpect "&&" (parseAtomicExp steps' symtable)) steps' rest;
- SomeE (fold_left BAnd es e, rest')
- end.
-
-Definition parseBExp := parseConjunctionExp.
-
-(*
-Eval compute in
- (parseProductExp 100 (tokenize "x*y*(x*x)*x")).
-
-Eval compute in
- (parseDisjunctionExp 100 (tokenize "not((x==x||x*x<=(x*x)*x)&&x==x)")).
-*)
-
-(* Parsing commands *)
-Fixpoint parseSimpleCommand (steps:nat) (symtable:string->nat) (xs : list token) :=
- match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (u, rest) <-- expect "SKIP" xs;
- SomeE (SKIP, rest)
- OR DO (e,rest) <--
- firstExpect "IF" (parseBExp steps' symtable) xs;
- DO (c,rest') <==
- firstExpect "THEN" (parseSequencedCommand steps' symtable) rest;
- DO (c',rest'') <==
- firstExpect "ELSE" (parseSequencedCommand steps' symtable) rest';
- DO (u,rest''') <==
- expect "END" rest'';
- SomeE(IFB e THEN c ELSE c' FI, rest''')
- OR DO (e,rest) <--
- firstExpect "WHILE" (parseBExp steps' symtable) xs;
- DO (c,rest') <==
- firstExpect "DO" (parseSequencedCommand steps' symtable) rest;
- DO (u,rest'') <==
- expect "END" rest';
- SomeE(WHILE e DO c END, rest'')
- OR DO (i, rest) <==
- parseIdentifier symtable xs;
- DO (e, rest') <==
- firstExpect ":=" (parseAExp steps' symtable) rest;
- SomeE(i ::= e, rest')
- end
-
-with parseSequencedCommand (steps:nat) (symtable:string->nat) (xs : list token) :=
- match steps with
- | 0 => NoneE "Too many recursive calls"
- | S steps' =>
- DO (c, rest) <==
- parseSimpleCommand steps' symtable xs;
- DO (c', rest') <--
- firstExpect ";;" (parseSequencedCommand steps' symtable) rest;
- SomeE(c ;; c', rest')
- OR
- SomeE(c, rest)
- end.
-
-Definition bignumber := 1000.
-
-Definition parse (str : string) : optionE (com * list token) :=
- let tokens := tokenize str in
- parseSequencedCommand bignumber (build_symtable tokens 0) tokens.
-
-(* ####################################################### *)
-(** * Examples *)
-
-
-(*
-Eval compute in parse "
- IF x == y + 1 + 2 - y * 6 + 3 THEN
- x := x * 1;;
- y := 0
- ELSE
- SKIP
- END ".
-====>
- SomeE
- (IFB BEq (AId (Id 0))
- (APlus
- (AMinus (APlus (APlus (AId (Id 1)) (ANum 1)) (ANum 2))
- (AMult (AId (Id 1)) (ANum 6)))
- (ANum 3))
- THEN Id 0 ::= AMult (AId (Id 0)) (ANum 1);; Id 1 ::= ANum 0
- ELSE SKIP FI, [])
-*)
-
-(*
-Eval compute in parse "
- SKIP;;
- z:=x*y*(x*x);;
- WHILE x==x DO
- IF z <= z*z && not x == 2 THEN
- x := z;;
- y := z
- ELSE
- SKIP
- END;;
- SKIP
- END;;
- x:=z ".
-====>
- SomeE
- (SKIP;;
- Id 0 ::= AMult (AMult (AId (Id 1)) (AId (Id 2)))
- (AMult (AId (Id 1)) (AId (Id 1)));;
- WHILE BEq (AId (Id 1)) (AId (Id 1)) DO
- IFB BAnd (BLe (AId (Id 0)) (AMult (AId (Id 0)) (AId (Id 0))))
- (BNot (BEq (AId (Id 1)) (ANum 2)))
- THEN Id 1 ::= AId (Id 0);; Id 2 ::= AId (Id 0)
- ELSE SKIP FI;;
- SKIP
- END;;
- Id 1 ::= AId (Id 0),
- [])
-*)
-
-(*
-Eval compute in parse "
- SKIP;;
- z:=x*y*(x*x);;
- WHILE x==x DO
- IF z <= z*z && not x == 2 THEN
- x := z;;
- y := z
- ELSE
- SKIP
- END;;
- SKIP
- END;;
- x:=z ".
-=====>
- SomeE
- (SKIP;;
- Id 0 ::= AMult (AMult (AId (Id 1)) (AId (Id 2)))
- (AMult (AId (Id 1)) (AId (Id 1)));;
- WHILE BEq (AId (Id 1)) (AId (Id 1)) DO
- IFB BAnd (BLe (AId (Id 0)) (AMult (AId (Id 0)) (AId (Id 0))))
- (BNot (BEq (AId (Id 1)) (ANum 2)))
- THEN Id 1 ::= AId (Id 0);;
- Id 2 ::= AId (Id 0)
- ELSE SKIP
- FI;;
- SKIP
- END;;
- Id 1 ::= AId (Id 0),
- []).
-*)
diff --git a/Induction.html b/Induction.html
deleted file mode 100644
index 884d877..0000000
--- a/Induction.html
+++ /dev/null
@@ -1,1035 +0,0 @@
-
-
-
-
-
-Induction: Proof by Induction
-
-
-
-
-
-
-
-
-
-
-
-
-
-
InductionProof by Induction
-
-
-
-
-
-
-
-
- The next line imports all of our definitions from the
- previous chapter.
-
-
-
-
-RequireExportBasics.
-
-
-
-
-
-For it to work, you need to use coqc to compile Basics.v
- into Basics.vo. (This is like making a .class file from a .java
- file, or a .o file from a .c file.)
-
-
-
- Here are two ways to compile your code:
-
-
-
-
-
CoqIDE:
-
-
-
- Open Basics.v.
- In the "Compile" menu, click on "Compile Buffer".
-
-
-
-
-
-
Command line:
-
-
-
- Run coqcBasics.v
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Naming Cases
-
-
-
- The fact that there is no explicit command for moving from
- one branch of a case analysis to the next can make proof scripts
- rather hard to read. In larger proofs, with nested case analyses,
- it can even become hard to stay oriented when you're sitting with
- Coq and stepping through the proof. (Imagine trying to remember
- that the first five subgoals belong to the inner case analysis and
- the remaining seven cases are what remains of the outer one...)
- Disciplined use of indentation and comments can help, but a better
- way is to use the Case tactic.
-
-
- Case is not built into Coq: we need to define it ourselves.
- There is no need to understand how it works — you can just skip
- over the definition to the example that follows. It uses some
- facilities of Coq that we have not discussed — the string
- library (just for the concrete syntax of quoted strings) and the
- Ltac command, which allows us to declare custom tactics. Kudos
- to Aaron Bohannon for this nice hack!
-
-Here's an example of how Case is used. Step through the
- following proof and observe how the context changes.
-
-
-
-
-Theoremandb_true_elim1 : ∀bc : bool,
- andbbc = true→b = true.
-Proof.
- introsbcH.
- destructb.
- Case "b = true". (* <----- here *)
- reflexivity.
- Case "b = false". (* <---- and here *)
- rewrite←H.
- reflexivity.
-Qed.
-
-
-
-
-
-Case does something very straightforward: It simply adds a
- string that we choose (tagged with the identifier "Case") to the
- context for the current goal. When subgoals are generated, this
- string is carried over into their contexts. When the last of
- these subgoals is finally proved and the next top-level goal
- becomes active, this string will no longer appear in the context
- and we will be able to see that the case where we introduced it is
- complete. Also, as a sanity check, if we try to execute a new
- Case tactic while the string left by the previous one is still
- in the context, we get a nice clear error message.
-
-
-
- For nested case analyses (e.g., when we want to use a destruct
- to solve a goal that has itself been generated by a destruct),
- there is an SCase ("subcase") tactic.
-
-
-
Exercise: 2 stars (andb_true_elim2)
- Prove andb_true_elim2, marking cases (and subcases) when
- you use destruct.
-
-
-
-
-Theoremandb_true_elim2 : ∀bc : bool,
- andbbc = true→c = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- There are no hard and fast rules for how proofs should be
- formatted in Coq — in particular, where lines should be broken
- and how sections of the proof should be indented to indicate their
- nested structure. However, if the places where multiple subgoals
- are generated are marked with explicit Case tactics placed at
- the beginning of lines, then the proof will be readable almost no
- matter what choices are made about other aspects of layout.
-
-
-
- This is a good place to mention one other piece of (possibly
- obvious) advice about line lengths. Beginning Coq users sometimes
- tend to the extremes, either writing each tactic on its own line
- or entire proofs on one line. Good style lies somewhere in the
- middle. In particular, one reasonable convention is to limit
- yourself to 80-character lines. Lines longer than this are hard
- to read and can be inconvenient to display and print. Many
- editors have features that help enforce this.
-
-
-
-
-
-
-
-
Proof by Induction
-
-
-
- We proved in the last chapter that 0 is a neutral element
- for + on the left using a simple argument. The fact that it is
- also a neutral element on the right...
-
-
-
-
-Theoremplus_0_r_firsttry : ∀n:nat,
- n + 0 = n.
-
-
-
-
-
-... cannot be proved in the same simple way. Just applying
- reflexivity doesn't work: the n in n+0 is an arbitrary
- unknown number, so the match in the definition of + can't be
- simplified.
-
-
- And reasoning by cases using destructn doesn't get us much
- further: the branch of the case analysis where we assume n=0
- goes through, but in the branch where n=Sn' for some n' we
- get stuck in exactly the same way. We could use destructn' to
- get one step further, but since n can be arbitrarily large, if we
- try to keep on like this we'll never be done.
-
-
-
-
-Theoremplus_0_r_secondtry : ∀n:nat,
- n + 0 = n.
-Proof.
- introsn. destructnas [| n'].
- Case "n = 0".
- reflexivity. (* so far so good... *)
- Case "n = S n'".
- simpl. (* ...but here we are stuck again *)
-Abort.
-
-
-
-
-
-
-
-
-
- To prove such facts — indeed, to prove most interesting
- facts about numbers, lists, and other inductively defined sets —
- we need a more powerful reasoning principle: induction.
-
-
-
- Recall (from high school) the principle of induction over natural
- numbers: If P(n) is some proposition involving a natural number
- n and we want to show that P holds for all numbers n, we can
- reason like this:
-
-
-
-
-
show that P(O) holds;
-
-
-
show that, for any n', if P(n') holds, then so does
- P(Sn');
-
-
-
conclude that P(n) holds for all n.
-
-
-
-
-
-
- In Coq, the steps are the same but the order is backwards: we
- begin with the goal of proving P(n) for all n and break it
- down (by applying the induction tactic) into two separate
- subgoals: first showing P(O) and then showing P(n')→P(S
- n'). Here's how this works for the theorem we are trying to
- prove at the moment:
-
-
-
-
-
-
-
-
-Theoremplus_0_r : ∀n:nat, n + 0 = n.
-Proof.
- introsn. inductionnas [| n'].
- Case "n = 0". reflexivity.
- Case "n = S n'". simpl. rewrite→IHn'. reflexivity. Qed.
-
-
-
-
-
-Like destruct, the induction tactic takes an as...
- clause that specifies the names of the variables to be introduced
- in the subgoals. In the first branch, n is replaced by 0 and
- the goal becomes 0+0=0, which follows by simplification. In
- the second, n is replaced by Sn' and the assumption n'+0=
- n' is added to the context (with the name IHn', i.e., the
- Induction Hypothesis for n'). The goal in this case becomes (S
- n')+0=Sn', which simplifies to S(n'+0)=Sn', which in
- turn follows from the induction hypothesis.
-
-
-
-
-Theoremminus_diag : ∀n,
- minusnn = 0.
-Proof.
- (* WORKED IN CLASS *)
- introsn. inductionnas [| n'].
- Case "n = 0".
- simpl. reflexivity.
- Case "n = S n'".
- simpl. rewrite→IHn'. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 2 stars (basic_induction)
-
-
-
- Prove the following lemmas using induction. You might need
- previously proven results.
-
-
-
-
-Theoremmult_0_r : ∀n:nat,
- n × 0 = 0.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremplus_n_Sm : ∀nm : nat,
- S (n + m) = n + (Sm).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremplus_comm : ∀nm : nat,
- n + m = m + n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremplus_assoc : ∀nmp : nat,
- n + (m + p) = (n + m) + p.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars (double_plus)
-
-
-
- Consider the following function, which doubles its argument:
-
-
-
-
-Fixpointdouble (n:nat) :=
- matchnwith
- | O ⇒ O
- | Sn' ⇒ S (S (doublen'))
- end.
-
-
-
-
-
-Use induction to prove this simple fact about double:
-
-
-
-
-Lemmadouble_plus : ∀n, doublen = n + n .
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star (destruct_induction)
- Briefly explain the difference between the tactics
- destruct and induction.
-
-
-
-(* FILL IN HERE *)
-
-
-
- ☐
-
-
-
-
-
-
-
-
Proofs Within Proofs
-
-
-
- In Coq, as in informal mathematics, large proofs are very
- often broken into a sequence of theorems, with later proofs
- referring to earlier theorems. Occasionally, however, a proof
- will need some miscellaneous fact that is too trivial (and of too
- little general interest) to bother giving it its own top-level
- name. In such cases, it is convenient to be able to simply state
- and prove the needed "sub-theorem" right at the point where it is
- used. The assert tactic allows us to do this. For example, our
- earlier proof of the mult_0_plus theorem referred to a previous
- theorem named plus_O_n. We can also use assert to state and
- prove plus_O_n in-line:
-
-
-
-
-Theoremmult_0_plus' : ∀nm : nat,
- (0 + n) × m = n × m.
-Proof.
- introsnm.
- assert (H: 0 + n = n).
- Case "Proof of assertion". reflexivity.
- rewrite→H.
- reflexivity. Qed.
-
-
-
-
-
-The assert tactic introduces two sub-goals. The first is
- the assertion itself; by prefixing it with H: we name the
- assertion H. (Note that we could also name the assertion with
- as just as we did above with destruct and induction, i.e.,
- assert(0+n=n)asH. Also note that we mark the proof of
- this assertion with a Case, both for readability and so that,
- when using Coq interactively, we can see when we're finished
- proving the assertion by observing when the "Proofofassertion"
- string disappears from the context.) The second goal is the same
- as the one at the point where we invoke assert, except that, in
- the context, we have the assumption H that 0+n=n. That
- is, assert generates one subgoal where we must prove the
- asserted fact and a second subgoal where we can use the asserted
- fact to make progress on whatever we were trying to prove in the
- first place.
-
-
- Actually, assert will turn out to be handy in many sorts of
- situations. For example, suppose we want to prove that (n+m)
- +(p+q)=(m+n)+(p+q). The only difference between the
- two sides of the = is that the arguments m and n to the
- first inner + are swapped, so it seems we should be able to
- use the commutativity of addition (plus_comm) to rewrite one
- into the other. However, the rewrite tactic is a little stupid
- about where it applies the rewrite. There are three uses of
- + here, and it turns out that doing rewrite→plus_comm
- will affect only the outer one.
-
-
-
-
-Theoremplus_rearrange_firsttry : ∀nmpq : nat,
- (n + m) + (p + q) = (m + n) + (p + q).
-Proof.
- introsnmpq.
- (* We just need to swap (n + m) for (m + n)...
- it seems like plus_comm should do the trick! *)
- rewrite→plus_comm.
- (* Doesn't work...Coq rewrote the wrong plus! *)
-Abort.
-
-
-
-
-
-To get plus_comm to apply at the point where we want it, we can
- introduce a local lemma stating that n+m=m+n (for
- the particular m and n that we are talking about here), prove
- this lemma using plus_comm, and then use this lemma to do the
- desired rewrite.
-
-
-
-
-Theoremplus_rearrange : ∀nmpq : nat,
- (n + m) + (p + q) = (m + n) + (p + q).
-Proof.
- introsnmpq.
- assert (H: n + m = m + n).
- Case "Proof of assertion".
- rewrite→plus_comm. reflexivity.
- rewrite→H. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 4 stars (mult_comm)
- Use assert to help prove this theorem. You shouldn't need to
- use induction.
-
-
-
-
-Theoremplus_swap : ∀nmp : nat,
- n + (m + p) = m + (n + p).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Now prove commutativity of multiplication. (You will probably
- need to define and prove a separate subsidiary theorem to be used
- in the proof of this one.) You may find that plus_swap comes in
- handy.
-
-
-
-
-Theoremmult_comm : ∀mn : nat,
- m × n = n × m.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (evenb_n__oddb_Sn)
-
-
-
- Prove the following simple fact:
-
-
-
-
-Theoremevenb_n__oddb_Sn : ∀n : nat,
- evenbn = negb (evenb (Sn)).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
More Exercises
-
-
-
-
Exercise: 3 stars, optional (more_exercises)
- Take a piece of paper. For each of the following theorems, first
- think about whether (a) it can be proved using only
- simplification and rewriting, (b) it also requires case
- analysis (destruct), or (c) it also requires induction. Write
- down your prediction. Then fill in the proof. (There is no need
- to turn in your piece of paper; this is just to encourage you to
- reflect before hacking!)
-
-
-
-
-Theoremble_nat_refl : ∀n:nat,
- true = ble_natnn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremzero_nbeq_S : ∀n:nat,
- beq_nat 0 (Sn) = false.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremandb_false_r : ∀b : bool,
- andbbfalse = false.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremplus_ble_compat_l : ∀nmp : nat,
- ble_natnm = true→ble_nat (p + n) (p + m) = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-TheoremS_nbeq_0 : ∀n:nat,
- beq_nat (Sn) 0 = false.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremmult_1_l : ∀n:nat, 1 × n = n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremall3_spec : ∀bc : bool,
- orb
- (andbbc)
- (orb (negbb)
- (negbc))
- = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremmult_plus_distr_r : ∀nmp : nat,
- (n + m) × p = (n × p) + (m × p).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremmult_assoc : ∀nmp : nat,
- n × (m × p) = (n × m) × p.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (beq_nat_refl)
- Prove the following theorem. Putting true on the left-hand side
-of the equality may seem odd, but this is how the theorem is stated in
-the standard library, so we follow suit. Since rewriting
-works equally well in either direction, we will have no
-problem using the theorem no matter which way we state it.
-
-
-
-
-Theorembeq_nat_refl : ∀n : nat,
- true = beq_natnn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (plus_swap')
- The replace tactic allows you to specify a particular subterm to
- rewrite and what you want it rewritten to. More precisely,
- replace(t)with(u) replaces (all copies of) expression t in
- the goal by expression u, and generates t=u as an additional
- subgoal. This is often useful when a plain rewrite acts on the wrong
- part of the goal.
-
-
-
- Use the replace tactic to do a proof of plus_swap', just like
- plus_swap but without needing assert(n+m=m+n).
-
-
-
-
-
-Theoremplus_swap' : ∀nmp : nat,
- n + (m + p) = m + (n + p).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (binary_commute)
- Recall the increment and binary-to-unary functions that you
- wrote for the binary exercise in the Basics chapter. Prove
- that these functions commute — that is, incrementing a binary
- number and then converting it to unary yields the same result as
- first converting it to unary and then incrementing.
-
-
-
- (Before you start working on this exercise, please copy the
- definitions from your solution to the binary exercise here so
- that this file can be graded on its own. If you find yourself
- wanting to change your original definitions to make the property
- easier to prove, feel free to do so.)
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 5 stars, advanced (binary_inverse)
- This exercise is a continuation of the previous exercise about
- binary numbers. You will need your definitions and theorems from
- the previous exercise to complete this one.
-
-
-
- (a) First, write a function to convert natural numbers to binary
- numbers. Then prove that starting with any natural number,
- converting to binary, then converting back yields the same
- natural number you started with.
-
-
-
- (b) You might naturally think that we should also prove the
- opposite direction: that starting with a binary number,
- converting to a natural, and then back to binary yields the
- same number we started with. However, it is not true!
- Explain what the problem is.
-
-
-
- (c) Define a function normalize from binary numbers to binary
- numbers such that for any binary number b, converting to a
- natural and then back to binary yields (normalizeb). Prove
- it.
-
-
-
- Again, feel free to change your earlier definitions if this helps
- here.
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Advanced Material
-
-
-
-
Formal vs. Informal Proof
-
-
-
- "Informal proofs are algorithms; formal proofs are code."
-
-
- The question of what, exactly, constitutes a "proof" of a
- mathematical claim has challenged philosophers for millennia. A
- rough and ready definition, though, could be this: a proof of a
- mathematical proposition P is a written (or spoken) text that
- instills in the reader or hearer the certainty that P is true.
- That is, a proof is an act of communication.
-
-
-
- Now, acts of communication may involve different sorts of readers.
- On one hand, the "reader" can be a program like Coq, in which case
- the "belief" that is instilled is a simple mechanical check that
- P can be derived from a certain set of formal logical rules, and
- the proof is a recipe that guides the program in performing this
- check. Such recipes are formal proofs.
-
-
-
- Alternatively, the reader can be a human being, in which case the
- proof will be written in English or some other natural language,
- thus necessarily informal. Here, the criteria for success are
- less clearly specified. A "good" proof is one that makes the
- reader believe P. But the same proof may be read by many
- different readers, some of whom may be convinced by a particular
- way of phrasing the argument, while others may not be. One reader
- may be particularly pedantic, inexperienced, or just plain
- thick-headed; the only way to convince them will be to make the
- argument in painstaking detail. But another reader, more familiar
- in the area, may find all this detail so overwhelming that they
- lose the overall thread. All they want is to be told the main
- ideas, because it is easier to fill in the details for themselves.
- Ultimately, there is no universal standard, because there is no
- single way of writing an informal proof that is guaranteed to
- convince every conceivable reader. In practice, however,
- mathematicians have developed a rich set of conventions and idioms
- for writing about complex mathematical objects that, within a
- certain community, make communication fairly reliable. The
- conventions of this stylized form of communication give a fairly
- clear standard for judging proofs good or bad.
-
-
-
- Because we are using Coq in this course, we will be working
- heavily with formal proofs. But this doesn't mean we can ignore
- the informal ones! Formal proofs are useful in many ways, but
- they are not very efficient ways of communicating ideas between
- human beings.
-
-
- For example, here is a proof that addition is associative:
-
-Coq is perfectly happy with this as a proof. For a human,
- however, it is difficult to make much sense of it. If you're used
- to Coq you can probably step through the tactics one after the
- other in your mind and imagine the state of the context and goal
- stack at each point, but if the proof were even a little bit more
- complicated this would be next to impossible. Instead, a
- mathematician might write it something like this:
-
-
-
-
Theorem: For any n, m and p,
-
-
-
-
- n + (m + p) = (n + m) + p.
-
-
-
- Proof: By induction on n.
-
-
-
-
-
First, suppose n=0. We must show
-
-
-
-
- 0 + (m + p) = (0 + m) + p.
-
-
-
- This follows directly from the definition of +.
-
-
-
-
-
-
Next, suppose n=Sn', where
-
-
-
-
- n' + (m + p) = (n' + m) + p.
-
-
-
- We must show
-
-
-
-
- (Sn') + (m + p) = ((Sn') + m) + p.
-
-
-
- By the definition of +, this follows from
-
-
-
-
- S (n' + (m + p)) = S ((n' + m) + p),
-
-
-
- which is immediate from the induction hypothesis. ☐
-
-
-
-
-
-
-
-
- The overall form of the proof is basically similar. This is
- no accident: Coq has been designed so that its induction tactic
- generates the same sub-goals, in the same order, as the bullet
- points that a mathematician would write. But there are
- significant differences of detail: the formal proof is much more
- explicit in some ways (e.g., the use of reflexivity) but much
- less explicit in others (in particular, the "proof state" at any
- given point in the Coq proof is completely implicit, whereas the
- informal proof reminds the reader several times where things
- stand).
-
-
- Here is a formal proof that shows the structure more
- clearly:
-
-
-
-
-Theoremplus_assoc'' : ∀nmp : nat,
- n + (m + p) = (n + m) + p.
-Proof.
- introsnmp. inductionnas [| n'].
- Case "n = 0".
- reflexivity.
- Case "n = S n'".
- simpl. rewrite→IHn'. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 2 stars, advanced (plus_comm_informal)
- Translate your solution for plus_comm into an informal proof.
-
- Write an informal proof of the following theorem, using the
- informal proof of plus_assoc as a model. Don't just
- paraphrase the Coq tactics into English!
-
-
-
-
-
\ No newline at end of file
diff --git a/Induction.v b/Induction.v
deleted file mode 100644
index 35559a9..0000000
--- a/Induction.v
+++ /dev/null
@@ -1,643 +0,0 @@
-(** * Induction: Proof by Induction *)
-
-
-(** The next line imports all of our definitions from the
- previous chapter. *)
-
-Require Export Basics.
-
-(** For it to work, you need to use [coqc] to compile [Basics.v]
- into [Basics.vo]. (This is like making a .class file from a .java
- file, or a .o file from a .c file.)
-
- Here are two ways to compile your code:
-
- - CoqIDE:
-
- Open [Basics.v].
- In the "Compile" menu, click on "Compile Buffer".
-
- - Command line:
-
- Run [coqc Basics.v]
-
- *)
-
-(* ###################################################################### *)
-(** * Naming Cases *)
-
-(** The fact that there is no explicit command for moving from
- one branch of a case analysis to the next can make proof scripts
- rather hard to read. In larger proofs, with nested case analyses,
- it can even become hard to stay oriented when you're sitting with
- Coq and stepping through the proof. (Imagine trying to remember
- that the first five subgoals belong to the inner case analysis and
- the remaining seven cases are what remains of the outer one...)
- Disciplined use of indentation and comments can help, but a better
- way is to use the [Case] tactic. *)
-
-(** [Case] is not built into Coq: we need to define it ourselves.
- There is no need to understand how it works -- you can just skip
- over the definition to the example that follows. It uses some
- facilities of Coq that we have not discussed -- the string
- library (just for the concrete syntax of quoted strings) and the
- [Ltac] command, which allows us to declare custom tactics. Kudos
- to Aaron Bohannon for this nice hack! *)
-
-Require String. Open Scope string_scope.
-
-Ltac move_to_top x :=
- match reverse goal with
- | H : _ |- _ => try move x after H
- end.
-
-Tactic Notation "assert_eq" ident(x) constr(v) :=
- let H := fresh in
- assert (x = v) as H by reflexivity;
- clear H.
-
-Tactic Notation "Case_aux" ident(x) constr(name) :=
- first [
- set (x := name); move_to_top x
- | assert_eq x name; move_to_top x
- | fail 1 "because we are working on a different case" ].
-
-Tactic Notation "Case" constr(name) := Case_aux Case name.
-Tactic Notation "SCase" constr(name) := Case_aux SCase name.
-Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
-Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
-Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
-Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.
-Tactic Notation "SSSSSSCase" constr(name) := Case_aux SSSSSSCase name.
-Tactic Notation "SSSSSSSCase" constr(name) := Case_aux SSSSSSSCase name.
-(** Here's an example of how [Case] is used. Step through the
- following proof and observe how the context changes. *)
-
-Theorem andb_true_elim1 : forall b c : bool,
- andb b c = true -> b = true.
-Proof.
- intros b c H.
- destruct b.
- Case "b = true". (* <----- here *)
- reflexivity.
- Case "b = false". (* <---- and here *)
- rewrite <- H.
- reflexivity.
-Qed.
-
-(** [Case] does something very straightforward: It simply adds a
- string that we choose (tagged with the identifier "Case") to the
- context for the current goal. When subgoals are generated, this
- string is carried over into their contexts. When the last of
- these subgoals is finally proved and the next top-level goal
- becomes active, this string will no longer appear in the context
- and we will be able to see that the case where we introduced it is
- complete. Also, as a sanity check, if we try to execute a new
- [Case] tactic while the string left by the previous one is still
- in the context, we get a nice clear error message.
-
- For nested case analyses (e.g., when we want to use a [destruct]
- to solve a goal that has itself been generated by a [destruct]),
- there is an [SCase] ("subcase") tactic. *)
-
-(** **** Exercise: 2 stars (andb_true_elim2) *)
-(** Prove [andb_true_elim2], marking cases (and subcases) when
- you use [destruct]. *)
-
-Theorem andb_true_elim2 : forall b c : bool,
- andb b c = true -> c = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** There are no hard and fast rules for how proofs should be
- formatted in Coq -- in particular, where lines should be broken
- and how sections of the proof should be indented to indicate their
- nested structure. However, if the places where multiple subgoals
- are generated are marked with explicit [Case] tactics placed at
- the beginning of lines, then the proof will be readable almost no
- matter what choices are made about other aspects of layout.
-
- This is a good place to mention one other piece of (possibly
- obvious) advice about line lengths. Beginning Coq users sometimes
- tend to the extremes, either writing each tactic on its own line
- or entire proofs on one line. Good style lies somewhere in the
- middle. In particular, one reasonable convention is to limit
- yourself to 80-character lines. Lines longer than this are hard
- to read and can be inconvenient to display and print. Many
- editors have features that help enforce this. *)
-
-(* ###################################################################### *)
-(** * Proof by Induction *)
-
-(** We proved in the last chapter that [0] is a neutral element
- for [+] on the left using a simple argument. The fact that it is
- also a neutral element on the _right_... *)
-
-Theorem plus_0_r_firsttry : forall n:nat,
- n + 0 = n.
-
-(** ... cannot be proved in the same simple way. Just applying
- [reflexivity] doesn't work: the [n] in [n + 0] is an arbitrary
- unknown number, so the [match] in the definition of [+] can't be
- simplified. *)
-
-Proof.
- intros n.
- simpl. (* Does nothing! *)
-Abort.
-
-(** *** *)
-
-(** And reasoning by cases using [destruct n] doesn't get us much
- further: the branch of the case analysis where we assume [n = 0]
- goes through, but in the branch where [n = S n'] for some [n'] we
- get stuck in exactly the same way. We could use [destruct n'] to
- get one step further, but since [n] can be arbitrarily large, if we
- try to keep on like this we'll never be done. *)
-
-Theorem plus_0_r_secondtry : forall n:nat,
- n + 0 = n.
-Proof.
- intros n. destruct n as [| n'].
- Case "n = 0".
- reflexivity. (* so far so good... *)
- Case "n = S n'".
- simpl. (* ...but here we are stuck again *)
-Abort.
-
-(** *** *)
-
-(** To prove such facts -- indeed, to prove most interesting
- facts about numbers, lists, and other inductively defined sets --
- we need a more powerful reasoning principle: _induction_.
-
- Recall (from high school) the principle of induction over natural
- numbers: If [P(n)] is some proposition involving a natural number
- [n] and we want to show that P holds for _all_ numbers [n], we can
- reason like this:
- - show that [P(O)] holds;
- - show that, for any [n'], if [P(n')] holds, then so does
- [P(S n')];
- - conclude that [P(n)] holds for all [n].
-
- In Coq, the steps are the same but the order is backwards: we
- begin with the goal of proving [P(n)] for all [n] and break it
- down (by applying the [induction] tactic) into two separate
- subgoals: first showing [P(O)] and then showing [P(n') -> P(S
- n')]. Here's how this works for the theorem we are trying to
- prove at the moment: *)
-
-(** *** *)
-
-Theorem plus_0_r : forall n:nat, n + 0 = n.
-Proof.
- intros n. induction n as [| n'].
- Case "n = 0". reflexivity.
- Case "n = S n'". simpl. rewrite -> IHn'. reflexivity. Qed.
-
-(** Like [destruct], the [induction] tactic takes an [as...]
- clause that specifies the names of the variables to be introduced
- in the subgoals. In the first branch, [n] is replaced by [0] and
- the goal becomes [0 + 0 = 0], which follows by simplification. In
- the second, [n] is replaced by [S n'] and the assumption [n' + 0 =
- n'] is added to the context (with the name [IHn'], i.e., the
- Induction Hypothesis for [n']). The goal in this case becomes [(S
- n') + 0 = S n'], which simplifies to [S (n' + 0) = S n'], which in
- turn follows from the induction hypothesis. *)
-
-Theorem minus_diag : forall n,
- minus n n = 0.
-Proof.
- (* WORKED IN CLASS *)
- intros n. induction n as [| n'].
- Case "n = 0".
- simpl. reflexivity.
- Case "n = S n'".
- simpl. rewrite -> IHn'. reflexivity. Qed.
-
-(** **** Exercise: 2 stars (basic_induction) *)
-
-(** Prove the following lemmas using induction. You might need
- previously proven results. *)
-
-Theorem mult_0_r : forall n:nat,
- n * 0 = 0.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem plus_n_Sm : forall n m : nat,
- S (n + m) = n + (S m).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-Theorem plus_comm : forall n m : nat,
- n + m = m + n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-Theorem plus_assoc : forall n m p : nat,
- n + (m + p) = (n + m) + p.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (double_plus) *)
-
-(** Consider the following function, which doubles its argument: *)
-
-Fixpoint double (n:nat) :=
- match n with
- | O => O
- | S n' => S (S (double n'))
- end.
-
-(** Use induction to prove this simple fact about [double]: *)
-
-Lemma double_plus : forall n, double n = n + n .
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(** **** Exercise: 1 star (destruct_induction) *)
-(** Briefly explain the difference between the tactics
- [destruct] and [induction].
-
-(* FILL IN HERE *)
-
-*)
-(** [] *)
-
-
-(* ###################################################################### *)
-(** * Proofs Within Proofs *)
-
-
-(** In Coq, as in informal mathematics, large proofs are very
- often broken into a sequence of theorems, with later proofs
- referring to earlier theorems. Occasionally, however, a proof
- will need some miscellaneous fact that is too trivial (and of too
- little general interest) to bother giving it its own top-level
- name. In such cases, it is convenient to be able to simply state
- and prove the needed "sub-theorem" right at the point where it is
- used. The [assert] tactic allows us to do this. For example, our
- earlier proof of the [mult_0_plus] theorem referred to a previous
- theorem named [plus_O_n]. We can also use [assert] to state and
- prove [plus_O_n] in-line: *)
-
-Theorem mult_0_plus' : forall n m : nat,
- (0 + n) * m = n * m.
-Proof.
- intros n m.
- assert (H: 0 + n = n).
- Case "Proof of assertion". reflexivity.
- rewrite -> H.
- reflexivity. Qed.
-
-(** The [assert] tactic introduces two sub-goals. The first is
- the assertion itself; by prefixing it with [H:] we name the
- assertion [H]. (Note that we could also name the assertion with
- [as] just as we did above with [destruct] and [induction], i.e.,
- [assert (0 + n = n) as H]. Also note that we mark the proof of
- this assertion with a [Case], both for readability and so that,
- when using Coq interactively, we can see when we're finished
- proving the assertion by observing when the ["Proof of assertion"]
- string disappears from the context.) The second goal is the same
- as the one at the point where we invoke [assert], except that, in
- the context, we have the assumption [H] that [0 + n = n]. That
- is, [assert] generates one subgoal where we must prove the
- asserted fact and a second subgoal where we can use the asserted
- fact to make progress on whatever we were trying to prove in the
- first place. *)
-
-(** Actually, [assert] will turn out to be handy in many sorts of
- situations. For example, suppose we want to prove that [(n + m)
- + (p + q) = (m + n) + (p + q)]. The only difference between the
- two sides of the [=] is that the arguments [m] and [n] to the
- first inner [+] are swapped, so it seems we should be able to
- use the commutativity of addition ([plus_comm]) to rewrite one
- into the other. However, the [rewrite] tactic is a little stupid
- about _where_ it applies the rewrite. There are three uses of
- [+] here, and it turns out that doing [rewrite -> plus_comm]
- will affect only the _outer_ one. *)
-
-Theorem plus_rearrange_firsttry : forall n m p q : nat,
- (n + m) + (p + q) = (m + n) + (p + q).
-Proof.
- intros n m p q.
- (* We just need to swap (n + m) for (m + n)...
- it seems like plus_comm should do the trick! *)
- rewrite -> plus_comm.
- (* Doesn't work...Coq rewrote the wrong plus! *)
-Abort.
-
-(** To get [plus_comm] to apply at the point where we want it, we can
- introduce a local lemma stating that [n + m = m + n] (for
- the particular [m] and [n] that we are talking about here), prove
- this lemma using [plus_comm], and then use this lemma to do the
- desired rewrite. *)
-
-Theorem plus_rearrange : forall n m p q : nat,
- (n + m) + (p + q) = (m + n) + (p + q).
-Proof.
- intros n m p q.
- assert (H: n + m = m + n).
- Case "Proof of assertion".
- rewrite -> plus_comm. reflexivity.
- rewrite -> H. reflexivity. Qed.
-
-(** **** Exercise: 4 stars (mult_comm) *)
-(** Use [assert] to help prove this theorem. You shouldn't need to
- use induction. *)
-
-Theorem plus_swap : forall n m p : nat,
- n + (m + p) = m + (n + p).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-(** Now prove commutativity of multiplication. (You will probably
- need to define and prove a separate subsidiary theorem to be used
- in the proof of this one.) You may find that [plus_swap] comes in
- handy. *)
-
-Theorem mult_comm : forall m n : nat,
- m * n = n * m.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (evenb_n__oddb_Sn) *)
-
-(** Prove the following simple fact: *)
-
-Theorem evenb_n__oddb_Sn : forall n : nat,
- evenb n = negb (evenb (S n)).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** * More Exercises *)
-
-(** **** Exercise: 3 stars, optional (more_exercises) *)
-(** Take a piece of paper. For each of the following theorems, first
- _think_ about whether (a) it can be proved using only
- simplification and rewriting, (b) it also requires case
- analysis ([destruct]), or (c) it also requires induction. Write
- down your prediction. Then fill in the proof. (There is no need
- to turn in your piece of paper; this is just to encourage you to
- reflect before hacking!) *)
-
-Theorem ble_nat_refl : forall n:nat,
- true = ble_nat n n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem zero_nbeq_S : forall n:nat,
- beq_nat 0 (S n) = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem andb_false_r : forall b : bool,
- andb b false = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem plus_ble_compat_l : forall n m p : nat,
- ble_nat n m = true -> ble_nat (p + n) (p + m) = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem S_nbeq_0 : forall n:nat,
- beq_nat (S n) 0 = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem mult_1_l : forall n:nat, 1 * n = n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem all3_spec : forall b c : bool,
- orb
- (andb b c)
- (orb (negb b)
- (negb c))
- = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem mult_plus_distr_r : forall n m p : nat,
- (n + m) * p = (n * p) + (m * p).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem mult_assoc : forall n m p : nat,
- n * (m * p) = (n * m) * p.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (beq_nat_refl) *)
-(** Prove the following theorem. Putting [true] on the left-hand side
-of the equality may seem odd, but this is how the theorem is stated in
-the standard library, so we follow suit. Since rewriting
-works equally well in either direction, we will have no
-problem using the theorem no matter which way we state it. *)
-
-Theorem beq_nat_refl : forall n : nat,
- true = beq_nat n n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (plus_swap') *)
-(** The [replace] tactic allows you to specify a particular subterm to
- rewrite and what you want it rewritten to. More precisely,
- [replace (t) with (u)] replaces (all copies of) expression [t] in
- the goal by expression [u], and generates [t = u] as an additional
- subgoal. This is often useful when a plain [rewrite] acts on the wrong
- part of the goal.
-
- Use the [replace] tactic to do a proof of [plus_swap'], just like
- [plus_swap] but without needing [assert (n + m = m + n)].
-*)
-
-Theorem plus_swap' : forall n m p : nat,
- n + (m + p) = m + (n + p).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(** **** Exercise: 3 stars (binary_commute) *)
-(** Recall the [increment] and [binary-to-unary] functions that you
- wrote for the [binary] exercise in the [Basics] chapter. Prove
- that these functions commute -- that is, incrementing a binary
- number and then converting it to unary yields the same result as
- first converting it to unary and then incrementing.
-
- (Before you start working on this exercise, please copy the
- definitions from your solution to the [binary] exercise here so
- that this file can be graded on its own. If you find yourself
- wanting to change your original definitions to make the property
- easier to prove, feel free to do so.) *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-(** **** Exercise: 5 stars, advanced (binary_inverse) *)
-(** This exercise is a continuation of the previous exercise about
- binary numbers. You will need your definitions and theorems from
- the previous exercise to complete this one.
-
- (a) First, write a function to convert natural numbers to binary
- numbers. Then prove that starting with any natural number,
- converting to binary, then converting back yields the same
- natural number you started with.
-
- (b) You might naturally think that we should also prove the
- opposite direction: that starting with a binary number,
- converting to a natural, and then back to binary yields the
- same number we started with. However, it is not true!
- Explain what the problem is.
-
- (c) Define a function [normalize] from binary numbers to binary
- numbers such that for any binary number b, converting to a
- natural and then back to binary yields [(normalize b)]. Prove
- it.
-
- Again, feel free to change your earlier definitions if this helps
- here.
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ###################################################################### *)
-(** * Advanced Material *)
-
-(** ** Formal vs. Informal Proof *)
-
-(** "Informal proofs are algorithms; formal proofs are code." *)
-
-(** The question of what, exactly, constitutes a "proof" of a
- mathematical claim has challenged philosophers for millennia. A
- rough and ready definition, though, could be this: a proof of a
- mathematical proposition [P] is a written (or spoken) text that
- instills in the reader or hearer the certainty that [P] is true.
- That is, a proof is an act of communication.
-
- Now, acts of communication may involve different sorts of readers.
- On one hand, the "reader" can be a program like Coq, in which case
- the "belief" that is instilled is a simple mechanical check that
- [P] can be derived from a certain set of formal logical rules, and
- the proof is a recipe that guides the program in performing this
- check. Such recipes are _formal_ proofs.
-
- Alternatively, the reader can be a human being, in which case the
- proof will be written in English or some other natural language,
- thus necessarily _informal_. Here, the criteria for success are
- less clearly specified. A "good" proof is one that makes the
- reader believe [P]. But the same proof may be read by many
- different readers, some of whom may be convinced by a particular
- way of phrasing the argument, while others may not be. One reader
- may be particularly pedantic, inexperienced, or just plain
- thick-headed; the only way to convince them will be to make the
- argument in painstaking detail. But another reader, more familiar
- in the area, may find all this detail so overwhelming that they
- lose the overall thread. All they want is to be told the main
- ideas, because it is easier to fill in the details for themselves.
- Ultimately, there is no universal standard, because there is no
- single way of writing an informal proof that is guaranteed to
- convince every conceivable reader. In practice, however,
- mathematicians have developed a rich set of conventions and idioms
- for writing about complex mathematical objects that, within a
- certain community, make communication fairly reliable. The
- conventions of this stylized form of communication give a fairly
- clear standard for judging proofs good or bad.
-
- Because we are using Coq in this course, we will be working
- heavily with formal proofs. But this doesn't mean we can ignore
- the informal ones! Formal proofs are useful in many ways, but
- they are _not_ very efficient ways of communicating ideas between
- human beings. *)
-
-(** For example, here is a proof that addition is associative: *)
-
-Theorem plus_assoc' : forall n m p : nat,
- n + (m + p) = (n + m) + p.
-Proof. intros n m p. induction n as [| n']. reflexivity.
- simpl. rewrite -> IHn'. reflexivity. Qed.
-
-(** Coq is perfectly happy with this as a proof. For a human,
- however, it is difficult to make much sense of it. If you're used
- to Coq you can probably step through the tactics one after the
- other in your mind and imagine the state of the context and goal
- stack at each point, but if the proof were even a little bit more
- complicated this would be next to impossible. Instead, a
- mathematician might write it something like this: *)
-(** - _Theorem_: For any [n], [m] and [p],
- n + (m + p) = (n + m) + p.
- _Proof_: By induction on [n].
-
- - First, suppose [n = 0]. We must show
- 0 + (m + p) = (0 + m) + p.
- This follows directly from the definition of [+].
-
- - Next, suppose [n = S n'], where
- n' + (m + p) = (n' + m) + p.
- We must show
- (S n') + (m + p) = ((S n') + m) + p.
- By the definition of [+], this follows from
- S (n' + (m + p)) = S ((n' + m) + p),
- which is immediate from the induction hypothesis. [] *)
-
-(** The overall form of the proof is basically similar. This is
- no accident: Coq has been designed so that its [induction] tactic
- generates the same sub-goals, in the same order, as the bullet
- points that a mathematician would write. But there are
- significant differences of detail: the formal proof is much more
- explicit in some ways (e.g., the use of [reflexivity]) but much
- less explicit in others (in particular, the "proof state" at any
- given point in the Coq proof is completely implicit, whereas the
- informal proof reminds the reader several times where things
- stand). *)
-
-(** Here is a formal proof that shows the structure more
- clearly: *)
-
-Theorem plus_assoc'' : forall n m p : nat,
- n + (m + p) = (n + m) + p.
-Proof.
- intros n m p. induction n as [| n'].
- Case "n = 0".
- reflexivity.
- Case "n = S n'".
- simpl. rewrite -> IHn'. reflexivity. Qed.
-
-(** **** Exercise: 2 stars, advanced (plus_comm_informal) *)
-(** Translate your solution for [plus_comm] into an informal proof. *)
-
-(** Theorem: Addition is commutative.
-
- Proof: (* FILL IN HERE *)
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (beq_nat_refl_informal) *)
-(** Write an informal proof of the following theorem, using the
- informal proof of [plus_assoc] as a model. Don't just
- paraphrase the Coq tactics into English!
-
- Theorem: [true = beq_nat n n] for any [n].
-
- Proof: (* FILL IN HERE *)
-[]
- *)
-
-(* $Date: 2014-02-19 21:36:35 -0500 (Wed, 19 Feb 2014) $ *)
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 3dca8a2..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2012
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/Lists.html b/Lists.html
deleted file mode 100644
index 2dcf8d0..0000000
--- a/Lists.html
+++ /dev/null
@@ -1,1766 +0,0 @@
-
-
-
-
-
-Lists: Working with Structured Data
-
-
-
-
-
-
-
-
- In an Inductive type definition, each constructor can take
- any number of arguments — none (as with true and O), one (as
- with S), or more than one, as in this definition:
-
-This declaration can be read: "There is just one way to
- construct a pair of numbers: by applying the constructor pair to
- two arguments of type nat."
-
-
- We can construct an element of natprod like this:
-
-
-
-
-Check (pair 3 5).
-
-
-
-
-
-
-
-
-
- Here are two simple function definitions for extracting the
- first and second components of a pair. (The definitions also
- illustrate how to do pattern matching on two-argument
- constructors.)
-
-
- Since pairs are used quite a bit, it is nice to be able to
- write them with the standard mathematical notation (x,y) instead
- of pairxy. We can tell Coq to allow this with a Notation
- declaration.
-
-
-
-
-Notation "( x , y )" := (pairxy).
-
-
-
-
-
-The new notation can be used both in expressions and in
- pattern matches (indeed, we've seen it already in the previous
- chapter — this notation is provided as part of the standard
- library):
-
-
- Let's try and prove a few simple facts about pairs. If we
- state the lemmas in a particular (and slightly peculiar) way, we
- can prove them with just reflexivity (and its built-in
- simplification):
-
-Theoremsnd_fst_is_swap : ∀(p : natprod),
- (sndp, fstp) = swap_pairp.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (fst_swap_is_snd)
-
-
-
-Theoremfst_swap_is_snd : ∀(p : natprod),
- fst (swap_pairp) = sndp.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Lists of Numbers
-
-
-
- Generalizing the definition of pairs a little, we can
- describe the type of lists of numbers like this: "A list is
- either the empty list or else a pair of a number and another
- list."
-
- As with pairs, it is more convenient to write lists in
- familiar programming notation. The following two declarations
- allow us to use :: as an infix cons operator and square
- brackets as an "outfix" notation for constructing lists.
-
-It is not necessary to fully understand these declarations,
- but in case you are interested, here is roughly what's going on.
-
-
-
- The rightassociativity annotation tells Coq how to parenthesize
- expressions involving several uses of :: so that, for example,
- the next three declarations mean exactly the same thing:
-
-The atlevel60 part tells Coq how to parenthesize
- expressions that involve both :: and some other infix operator.
- For example, since we defined + as infix notation for the plus
- function at level 50,
-
-
- The + operator will bind tighter than ::, so 1+2::[3]
- will be parsed, as we'd expect, as (1+2)::[3] rather than 1
- +(2::[3]).
-
-
-
- (By the way, it's worth noting in passing that expressions like "1
- +2::[3]" can be a little confusing when you read them in a .v
- file. The inner brackets, around 3, indicate a list, but the outer
- brackets, which are invisible in the HTML rendering, are there to
- instruct the "coqdoc" tool that the bracketed part should be
- displayed as Coq code rather than running text.)
-
-
-
- The second and third Notation declarations above introduce the
- standard square-bracket notation for lists; the right-hand side of
- the third one illustrates Coq's syntax for declaring n-ary
- notations and translating them to nested sequences of binary
- constructors.
-
-
-
Repeat
- A number of functions are useful for manipulating lists.
- For example, the repeat function takes a number n and a
- count and returns a list of length count where every element
- is n.
-
-Here are two smaller examples of programming with lists.
- The hd function returns the first element (the "head") of the
- list, while tl returns everything but the first
- element (the "tail").
- Of course, the empty list has no first element, so we
- must pass a default value to be returned in that case.
-
-
-
Head (with default) and Tail
-
-
-
-Definitionhd (default:nat) (l:natlist) : nat :=
- matchlwith
- | nil ⇒ default
- | h :: t ⇒ h
- end.
-
-
-Definitiontl (l:natlist) : natlist :=
- matchlwith
- | nil ⇒ nil
- | h :: t ⇒ t
- end.
-
-
-Exampletest_hd1: hd 0 [1;2;3] = 1.
-Proof. reflexivity. Qed.
-Exampletest_hd2: hd 0 [] = 0.
-Proof. reflexivity. Qed.
-Exampletest_tl: tl [1;2;3] = [2;3].
-Proof. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 2 stars (list_funs)
- Complete the definitions of nonzeros, oddmembers and
- countoddmembers below. Have a look at the tests to understand
- what these functions should do.
-
-
-
-
-Fixpointnonzeros (l:natlist) : natlist :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_nonzeros: nonzeros [0;1;0;2;3;0;0] = [1;2;3].
- (* FILL IN HERE *)Admitted.
-
-
-Fixpointoddmembers (l:natlist) : natlist :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_oddmembers: oddmembers [0;1;0;2;3;0;0] = [1;3].
- (* FILL IN HERE *)Admitted.
-
-
-Fixpointcountoddmembers (l:natlist) : nat :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_countoddmembers1: countoddmembers [1;0;3;1;4;5] = 4.
- (* FILL IN HERE *)Admitted.
-Exampletest_countoddmembers2: countoddmembers [0;2;4] = 0.
- (* FILL IN HERE *)Admitted.
-Exampletest_countoddmembers3: countoddmembersnil = 0.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (alternate)
- Complete the definition of alternate, which "zips up" two lists
- into one, alternating between elements taken from the first list
- and elements from the second. See the tests below for more
- specific examples.
-
-
-
- Note: one natural and elegant way of writing alternate will fail
- to satisfy Coq's requirement that all Fixpoint definitions be
- "obviously terminating." If you find yourself in this rut, look
- for a slightly more verbose solution that considers elements of
- both lists at the same time. (One possible solution requires
- defining a new kind of pairs, but this is not the only way.)
-
-
-
-
-Fixpointalternate (l1l2 : natlist) : natlist :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_alternate1: alternate [1;2;3] [4;5;6] = [1;4;2;5;3;6].
- (* FILL IN HERE *)Admitted.
-Exampletest_alternate2: alternate [1] [4;5;6] = [1;4;5;6].
- (* FILL IN HERE *)Admitted.
-Exampletest_alternate3: alternate [1;2;3] [4] = [1;4;2;3].
- (* FILL IN HERE *)Admitted.
-Exampletest_alternate4: alternate [] [20;30] = [20;30].
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Bags via Lists
-
-
-
- A bag (or multiset) is like a set, but each element can appear
- multiple times instead of just once. One reasonable
- implementation of bags is to represent a bag of numbers as a
- list.
-
-
-
-
-Definitionbag := natlist.
-
-
-
-
-
-
Exercise: 3 stars (bag_functions)
- Complete the following definitions for the functions
- count, sum, add, and member for bags.
-
-
-
-
-Fixpointcount (v:nat) (s:bag) : nat :=
- (* FILL IN HERE *)admit.
-
-
-
-
-
-All these proofs can be done just by reflexivity.
-
-
-
-
-Exampletest_count1: count 1 [1;2;3;1;4;1] = 3.
- (* FILL IN HERE *)Admitted.
-Exampletest_count2: count 6 [1;2;3;1;4;1] = 0.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Multiset sum is similar to set union: sumab contains
- all the elements of a and of b. (Mathematicians usually
- define union on multisets a little bit differently, which
- is why we don't use that name for this operation.)
- For sum we're giving you a header that does not give explicit
- names to the arguments. Moreover, it uses the keyword
- Definition instead of Fixpoint, so even if you had names for
- the arguments, you wouldn't be able to process them recursively.
- The point of stating the question this way is to encourage you to
- think about whether sum can be implemented in another way —
- perhaps by using functions that have already been defined.
-
-
-
-
-Definitionsum : bag→bag→bag :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_sum1: count 1 (sum [1;2;3] [1;4;1]) = 3.
- (* FILL IN HERE *)Admitted.
-
-
-Definitionadd (v:nat) (s:bag) : bag :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_add1: count 1 (add 1 [1;4;1]) = 3.
- (* FILL IN HERE *)Admitted.
-Exampletest_add2: count 5 (add 1 [1;4;1]) = 0.
- (* FILL IN HERE *)Admitted.
-
-
-Definitionmember (v:nat) (s:bag) : bool :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_member1: member 1 [1;4;1] = true.
- (* FILL IN HERE *)Admitted.
-Exampletest_member2: member 2 [1;4;1] = false.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (bag_more_functions)
- Here are some more bag functions for you to practice with.
-
-
-
-
-Fixpointremove_one (v:nat) (s:bag) : bag :=
- (* When remove_one is applied to a bag without the number to remove,
- it should return the same bag unchanged. *)
- (* FILL IN HERE *)admit.
-
-
-Exampletest_remove_one1: count 5 (remove_one 5 [2;1;5;4;1]) = 0.
- (* FILL IN HERE *)Admitted.
-Exampletest_remove_one2: count 5 (remove_one 5 [2;1;4;1]) = 0.
- (* FILL IN HERE *)Admitted.
-Exampletest_remove_one3: count 4 (remove_one 5 [2;1;4;5;1;4]) = 2.
- (* FILL IN HERE *)Admitted.
-Exampletest_remove_one4: count 5 (remove_one 5 [2;1;5;4;5;1;4]) = 1.
- (* FILL IN HERE *)Admitted.
-
-
-Fixpointremove_all (v:nat) (s:bag) : bag :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_remove_all1: count 5 (remove_all 5 [2;1;5;4;1]) = 0.
- (* FILL IN HERE *)Admitted.
-Exampletest_remove_all2: count 5 (remove_all 5 [2;1;4;1]) = 0.
- (* FILL IN HERE *)Admitted.
-Exampletest_remove_all3: count 4 (remove_all 5 [2;1;4;5;1;4]) = 2.
- (* FILL IN HERE *)Admitted.
-Exampletest_remove_all4: count 5 (remove_all 5 [2;1;5;4;5;1;4;5;1;4]) = 0.
- (* FILL IN HERE *)Admitted.
-
-
-Fixpointsubset (s1:bag) (s2:bag) : bool :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_subset1: subset [1;2] [2;1;4;1] = true.
- (* FILL IN HERE *)Admitted.
-Exampletest_subset2: subset [1;2;2] [2;1;4;1] = false.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (bag_theorem)
- Write down an interesting theorem about bags involving the
- functions count and add, and prove it. Note that, since this
- problem is somewhat open-ended, it's possible that you may come up
- with a theorem which is true, but whose proof requires techniques
- you haven't learned yet. Feel free to ask for help if you get
- stuck!
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Reasoning About Lists
-
-
-
- Just as with numbers, simple facts about list-processing
- functions can sometimes be proved entirely by simplification. For
- example, the simplification performed by reflexivity is enough
- for this theorem...
-
-
-
-
-Theoremnil_app : ∀l:natlist,
- [] ++ l = l.
-Proof. reflexivity. Qed.
-
-
-
-
-
-... because the [] is substituted into the match position
- in the definition of app, allowing the match itself to be
- simplified.
-
-
- Also, as with numbers, it is sometimes helpful to perform case
- analysis on the possible shapes (empty or non-empty) of an unknown
- list.
-
-
-
-
-Theoremtl_length_pred : ∀l:natlist,
- pred (lengthl) = length (tll).
-Proof.
- introsl. destructlas [| nl'].
- Case "l = nil".
- reflexivity.
- Case "l = cons n l'".
- reflexivity. Qed.
-
-
-
-
-
-Here, the nil case works because we've chosen to define
- tlnil=nil. Notice that the as annotation on the destruct
- tactic here introduces two names, n and l', corresponding to
- the fact that the cons constructor for lists takes two
- arguments (the head and tail of the list it is constructing).
-
-
- Usually, though, interesting theorems about lists require
- induction for their proofs.
-
-
-
-
-
-
-
-
Micro-Sermon
-
-
-
- Simply reading example proofs will not get you very far! It is
- very important to work through the details of each one, using Coq
- and thinking about what each step of the proof achieves.
- Otherwise it is more or less guaranteed that the exercises will
- make no sense.
-
-
-
-
-
-
-
-
Induction on Lists
-
-
-
- Proofs by induction over datatypes like natlist are
- perhaps a little less familiar than standard natural number
- induction, but the basic idea is equally simple. Each Inductive
- declaration defines a set of data values that can be built up from
- the declared constructors: a boolean can be either true or
- false; a number can be either O or S applied to a number; a
- list can be either nil or cons applied to a number and a list.
-
-
-
- Moreover, applications of the declared constructors to one another
- are the only possible shapes that elements of an inductively
- defined set can have, and this fact directly gives rise to a way
- of reasoning about inductively defined sets: a number is either
- O or else it is S applied to some smaller number; a list is
- either nil or else it is cons applied to some number and some
- smaller list; etc. So, if we have in mind some proposition P
- that mentions a list l and we want to argue that P holds for
- all lists, we can reason as follows:
-
-
-
-
-
First, show that P is true of l when l is nil.
-
-
-
-
-
-
Then show that P is true of l when l is consnl' for
- some number n and some smaller list l', assuming that P
- is true for l'.
-
-
-
-
-
-
- Since larger lists can only be built up from smaller ones,
- eventually reaching nil, these two things together establish the
- truth of P for all lists l. Here's a concrete example:
-
-Again, this Coq proof is not especially illuminating as a
- static written document — it is easy to see what's going on if
- you are reading the proof in an interactive Coq session and you
- can see the current goal and context at each point, but this state
- is not visible in the written-down parts of the Coq proof. So a
- natural-language proof — one written for human readers — will
- need to include more explicit signposts; in particular, it will
- help the reader stay oriented if we remind them exactly what the
- induction hypothesis is in the second case.
-
-
-
Informal version
-
-
-
- Theorem: For all lists l1, l2, and l3,
- (l1++l2)++l3=l1++(l2++l3).
-
-
-
- Proof: By induction on l1.
-
-
-
-
-
First, suppose l1=[]. We must show
-
-
-
-
- ([] ++ l2) ++ l3 = [] ++ (l2 ++ l3),
-
-
-
- which follows directly from the definition of ++.
-
-
- Now let's prove some more list theorems using our newly
- defined snoc and rev. For something a little more challenging
- than the inductive proofs we've seen so far, let's prove that
- reversing a list does not change its length. Our first attempt at
- this proof gets stuck in the successor case...
-
-
-
-
-Theoremrev_length_firsttry : ∀l : natlist,
- length (revl) = lengthl.
-Proof.
- introsl. inductionlas [| nl'].
- Case "l = []".
- reflexivity.
- Case "l = n :: l'".
- (* This is the tricky case. Let's begin as usual
- by simplifying. *)
- simpl.
- (* Now we seem to be stuck: the goal is an equality
- involving snoc, but we don't have any equations
- in either the immediate context or the global
- environment that have anything to do with snoc!
-
- We can make a little progress by using the IH to
- rewrite the goal... *)
- rewrite←IHl'.
- (* ... but now we can't go any further. *)
-Abort.
-
-
-
-
-
-So let's take the equation about snoc that would have
- enabled us to make progress and prove it as a separate lemma.
-
-
- Note that we make the lemma as general as possible: in particular,
- we quantify over allnatlists, not just those that result
- from an application of rev. This should seem natural,
- because the truth of the goal clearly doesn't depend on
- the list having been reversed. Moreover, it is much easier
- to prove the more general property.
-
-
- By the definitions of length and snoc, this
- follows from
-
-
-
-
- S (length (snocl'n)) = S (S (lengthl')),
-
-
-
- which is immediate from the induction hypothesis. ☐
-
-
-
-
-
- Theorem: For all lists l, length(revl)=lengthl.
-
-
-
- Proof: By induction on l.
-
-
-
-
-
First, suppose l=[]. We must show
-
-
-
-
- length (rev []) = length [],
-
-
-
- which follows directly from the definitions of length
- and rev.
-
-
-
-
-
-
Next, suppose l=n::l', with
-
-
-
-
- length (revl') = lengthl'.
-
-
-
- We must show
-
-
-
-
- length (rev (n :: l')) = length (n :: l').
-
-
-
- By the definition of rev, this follows from
-
-
-
-
- length (snoc (revl') n) = S (lengthl')
-
-
-
- which, by the previous lemma, is the same as
-
-
-
-
- S (length (revl')) = S (lengthl').
-
-
-
- This is immediate from the induction hypothesis. ☐
-
-
-
-
-
- Obviously, the style of these proofs is rather longwinded
- and pedantic. After the first few, we might find it easier to
- follow proofs that give fewer details (since we can easily work
- them out in our own minds or on scratch paper if necessary) and
- just highlight the non-obvious steps. In this more compressed
- style, the above proof might look more like this:
-
-
- Theorem:
- For all lists l, length(revl)=lengthl.
-
-
-
- Proof: First, observe that
-
-
-
-
- length (snocln) = S (lengthl)
-
-
-
- for any l. This follows by a straightforward induction on l.
- The main property now follows by another straightforward
- induction on l, using the observation together with the
- induction hypothesis in the case where l=n'::l'. ☐
-
-
- Which style is preferable in a given situation depends on
- the sophistication of the expected audience and on how similar the
- proof at hand is to ones that the audience will already be
- familiar with. The more pedantic style is a good default for
- present purposes.
-
-
-
-
-
-
-
-
SearchAbout
-
-
-
- We've seen that proofs can make use of other theorems we've
- already proved, using rewrite, and later we will see other ways
- of reusing previous theorems. But in order to refer to a theorem,
- we need to know its name, and remembering the names of all the
- theorems we might ever want to use can become quite difficult! It
- is often hard even to remember what theorems have been proven,
- much less what they are named.
-
-
-
- Coq's SearchAbout command is quite helpful with this. Typing
- SearchAboutfoo will cause Coq to display a list of all theorems
- involving foo. For example, try uncommenting the following to
- see a list of theorems that we have proved about rev:
-
-
-
-
-(* SearchAbout rev. *)
-
-
-
-
-
-Keep SearchAbout in mind as you do the following exercises and
- throughout the rest of the course; it can save you a lot of time!
-
-
- Also, if you are using ProofGeneral, you can run SearchAbout
- with C-cC-aC-a. Pasting its response into your buffer can be
- accomplished with C-cC-;.
-
-
-
-
-
-
-
-
List Exercises, Part 1
-
-
-
-
Exercise: 3 stars (list_exercises)
- More practice with lists.
-
-
-
-
-Theoremapp_nil_end : ∀l : natlist,
- l ++ [] = l.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremrev_involutive : ∀l : natlist,
- rev (revl) = l.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-There is a short solution to the next exercise. If you find
- yourself getting tangled up, step back and try to look for a
- simpler way.
-
-
-
-
-Theoremapp_assoc4 : ∀l1l2l3l4 : natlist,
- l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremsnoc_append : ∀(l:natlist) (n:nat),
- snocln = l ++ [n].
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremdistr_rev : ∀l1l2 : natlist,
- rev (l1 ++ l2) = (revl2) ++ (revl1).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-An exercise about your implementation of nonzeros:
-
-
-
-
-Lemmanonzeros_app : ∀l1l2 : natlist,
- nonzeros (l1 ++ l2) = (nonzerosl1) ++ (nonzerosl2).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars (beq_natlist)
- Fill in the definition of beq_natlist, which compares
- lists of numbers for equality. Prove that beq_natlistll
- yields true for every list l.
-
-
-
-
-Fixpointbeq_natlist (l1l2 : natlist) : bool :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_beq_natlist1 : (beq_natlistnilnil = true).
- (* FILL IN HERE *)Admitted.
-Exampletest_beq_natlist2 : beq_natlist [1;2;3] [1;2;3] = true.
- (* FILL IN HERE *)Admitted.
-Exampletest_beq_natlist3 : beq_natlist [1;2;3] [1;2;4] = false.
- (* FILL IN HERE *)Admitted.
-
-
-Theorembeq_natlist_refl : ∀l:natlist,
- true = beq_natlistll.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
List Exercises, Part 2
-
-
-
-
Exercise: 2 stars (list_design)
- Design exercise:
-
-
-
-
-
Write down a non-trivial theorem involving cons
- (::), snoc, and app (++).
-
-
-
Prove it.
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (bag_proofs)
- Here are a couple of little theorems to prove about your
- definitions about bags earlier in the file.
-
-
-
-
-Theoremcount_member_nonzero : ∀(s : bag),
- ble_nat 1 (count 1 (1 :: s)) = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-The following lemma about ble_nat might help you in the next proof.
-
- Write down an interesting theorem about bags involving the
- functions count and sum, and prove it.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 4 stars, advanced (rev_injective)
- Prove that the rev function is injective, that is,
-
-
-
-
-
-
-
- ∀(l1l2 : natlist), revl1 = revl2→l1 = l2.
-
-
-
-
-
-
-There is a hard way and an easy way to solve this exercise.
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Options
-
-
-
- One use of natoption is as a way of returning "error
- codes" from functions. For example, suppose we want to write a
- function that returns the nth element of some list. If we give
- it type nat→natlist→nat, then we'll have to return some
- number when the list is too short!
-
-
-
-
-Fixpointindex_bad (n:nat) (l:natlist) : nat :=
- matchlwith
- | nil ⇒ 42 (* arbitrary! *)
- | a :: l' ⇒ matchbeq_natnOwith
- | true ⇒ a
- | false ⇒ index_bad (predn) l'
- end
- end.
-
-
-
-
-
-
- On the other hand, if we give it type nat→natlist→
- natoption, then we can return None when the list is too short
- and Somea when the list has enough members and a appears at
- position n.
-
-
-
-
-Inductivenatoption : Type :=
- | Some : nat→natoption
- | None : natoption.
-
-
-Fixpointindex (n:nat) (l:natlist) : natoption :=
- matchlwith
- | nil ⇒ None
- | a :: l' ⇒ matchbeq_natnOwith
- | true ⇒ Somea
- | false ⇒ index (predn) l'
- end
- end.
-
-
-Exampletest_index1 : index 0 [4;5;6;7] = Some 4.
-Proof. reflexivity. Qed.
-Exampletest_index2 : index 3 [4;5;6;7] = Some 7.
-Proof. reflexivity. Qed.
-Exampletest_index3 : index 10 [4;5;6;7] = None.
-Proof. reflexivity. Qed.
-
-
-
-
-
-This example is also an opportunity to introduce one more
- small feature of Coq's programming language: conditional
- expressions...
-
-
-
-
-
-
-
-
-Fixpointindex' (n:nat) (l:natlist) : natoption :=
- matchlwith
- | nil ⇒ None
- | a :: l' ⇒ ifbeq_natnOthenSomeaelseindex' (predn) l'
- end.
-
-
-
-
-
-Coq's conditionals are exactly like those found in any other
- language, with one small generalization. Since the boolean type
- is not built in, Coq actually allows conditional expressions over
- any inductively defined type with exactly two constructors. The
- guard is considered true if it evaluates to the first constructor
- in the Inductive definition and false if it evaluates to the
- second.
-
-
- The function below pulls the nat out of a natoption, returning
- a supplied default in the None case.
-
-
-
-
-Definitionoption_elim (d : nat) (o : natoption) : nat :=
- matchowith
- | Somen' ⇒ n'
- | None ⇒ d
- end.
-
-
-
-
-
-
Exercise: 2 stars (hd_opt)
- Using the same idea, fix the hd function from earlier so we don't
- have to pass a default element for the nil case.
-
-
-
-
-Definitionhd_opt (l : natlist) : natoption :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_hd_opt1 : hd_opt [] = None.
- (* FILL IN HERE *)Admitted.
-
-
-Exampletest_hd_opt2 : hd_opt [1] = Some 1.
- (* FILL IN HERE *)Admitted.
-
-
-Exampletest_hd_opt3 : hd_opt [5;6] = Some 5.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (option_elim_hd)
- This exercise relates your new hd_opt to the old hd.
-
-
-
-
-Theoremoption_elim_hd : ∀(l:natlist) (default:nat),
- hddefaultl = option_elimdefault (hd_optl).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Dictionaries
-
-
-
- As a final illustration of how fundamental data structures
- can be defined in Coq, here is the declaration of a simple
- dictionary data type, using numbers for both the keys and the
- values stored under these keys. (That is, a dictionary represents
- a finite map from numbers to numbers.)
-
-This declaration can be read: "There are two ways to construct a
- dictionary: either using the constructor empty to represent an
- empty dictionary, or by applying the constructor record to
- a key, a value, and an existing dictionary to construct a
- dictionary with an additional key to value mapping."
-
-Here is a function find that searches a dictionary for a
- given key. It evaluates evaluates to None if the key was not
- found and Someval if the key was mapped to val in the
- dictionary. If the same key is mapped to multiple values, find
- will return the first one it finds.
-
-
-
-
\ No newline at end of file
diff --git a/Lists.v b/Lists.v
deleted file mode 100644
index cd5cfe6..0000000
--- a/Lists.v
+++ /dev/null
@@ -1,1016 +0,0 @@
-(** * Lists: Working with Structured Data *)
-
-Require Export Induction.
-
-Module NatList.
-
-(* ###################################################### *)
-(** * Pairs of Numbers *)
-
-(** In an [Inductive] type definition, each constructor can take
- any number of arguments -- none (as with [true] and [O]), one (as
- with [S]), or more than one, as in this definition: *)
-
-Inductive natprod : Type :=
- pair : nat -> nat -> natprod.
-
-(** This declaration can be read: "There is just one way to
- construct a pair of numbers: by applying the constructor [pair] to
- two arguments of type [nat]." *)
-
-(** We can construct an element of [natprod] like this: *)
-
-Check (pair 3 5).
-
-(** *** *)
-
-(** Here are two simple function definitions for extracting the
- first and second components of a pair. (The definitions also
- illustrate how to do pattern matching on two-argument
- constructors.) *)
-
-Definition fst (p : natprod) : nat :=
- match p with
- | pair x y => x
- end.
-Definition snd (p : natprod) : nat :=
- match p with
- | pair x y => y
- end.
-
-Eval compute in (fst (pair 3 5)).
-(* ===> 3 *)
-
-(** *** *)
-
-(** Since pairs are used quite a bit, it is nice to be able to
- write them with the standard mathematical notation [(x,y)] instead
- of [pair x y]. We can tell Coq to allow this with a [Notation]
- declaration. *)
-
-Notation "( x , y )" := (pair x y).
-
-(** The new notation can be used both in expressions and in
- pattern matches (indeed, we've seen it already in the previous
- chapter -- this notation is provided as part of the standard
- library): *)
-
-Eval compute in (fst (3,5)).
-
-Definition fst' (p : natprod) : nat :=
- match p with
- | (x,y) => x
- end.
-Definition snd' (p : natprod) : nat :=
- match p with
- | (x,y) => y
- end.
-
-Definition swap_pair (p : natprod) : natprod :=
- match p with
- | (x,y) => (y,x)
- end.
-
-(** *** *)
-
-(** Let's try and prove a few simple facts about pairs. If we
- state the lemmas in a particular (and slightly peculiar) way, we
- can prove them with just reflexivity (and its built-in
- simplification): *)
-
-Theorem surjective_pairing' : forall (n m : nat),
- (n,m) = (fst (n,m), snd (n,m)).
-Proof.
- reflexivity. Qed.
-
-(** Note that [reflexivity] is not enough if we state the lemma in a
- more natural way: *)
-
-Theorem surjective_pairing_stuck : forall (p : natprod),
- p = (fst p, snd p).
-Proof.
- simpl. (* Doesn't reduce anything! *)
-Abort.
-
-(** *** *)
-(** We have to expose the structure of [p] so that [simpl] can
- perform the pattern match in [fst] and [snd]. We can do this with
- [destruct].
-
- Notice that, unlike for [nat]s, [destruct] doesn't generate an
- extra subgoal here. That's because [natprod]s can only be
- constructed in one way. *)
-
-Theorem surjective_pairing : forall (p : natprod),
- p = (fst p, snd p).
-Proof.
- intros p. destruct p as [n m]. simpl. reflexivity. Qed.
-
-(** **** Exercise: 1 star (snd_fst_is_swap) *)
-Theorem snd_fst_is_swap : forall (p : natprod),
- (snd p, fst p) = swap_pair p.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (fst_swap_is_snd) *)
-Theorem fst_swap_is_snd : forall (p : natprod),
- fst (swap_pair p) = snd p.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** * Lists of Numbers *)
-
-(** Generalizing the definition of pairs a little, we can
- describe the type of _lists_ of numbers like this: "A list is
- either the empty list or else a pair of a number and another
- list." *)
-
-Inductive natlist : Type :=
- | nil : natlist
- | cons : nat -> natlist -> natlist.
-
-(** For example, here is a three-element list: *)
-
-Definition mylist := cons 1 (cons 2 (cons 3 nil)).
-
-
-(** *** *)
-(** As with pairs, it is more convenient to write lists in
- familiar programming notation. The following two declarations
- allow us to use [::] as an infix [cons] operator and square
- brackets as an "outfix" notation for constructing lists. *)
-
-Notation "x :: l" := (cons x l) (at level 60, right associativity).
-Notation "[ ]" := nil.
-Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).
-
-(** It is not necessary to fully understand these declarations,
- but in case you are interested, here is roughly what's going on.
-
- The [right associativity] annotation tells Coq how to parenthesize
- expressions involving several uses of [::] so that, for example,
- the next three declarations mean exactly the same thing: *)
-
-Definition mylist1 := 1 :: (2 :: (3 :: nil)).
-Definition mylist2 := 1 :: 2 :: 3 :: nil.
-Definition mylist3 := [1;2;3].
-
-(** The [at level 60] part tells Coq how to parenthesize
- expressions that involve both [::] and some other infix operator.
- For example, since we defined [+] as infix notation for the [plus]
- function at level 50,
-Notation "x + y" := (plus x y)
- (at level 50, left associativity).
- The [+] operator will bind tighter than [::], so [1 + 2 :: [3]]
- will be parsed, as we'd expect, as [(1 + 2) :: [3]] rather than [1
- + (2 :: [3])].
-
- (By the way, it's worth noting in passing that expressions like "[1
- + 2 :: [3]]" can be a little confusing when you read them in a .v
- file. The inner brackets, around 3, indicate a list, but the outer
- brackets, which are invisible in the HTML rendering, are there to
- instruct the "coqdoc" tool that the bracketed part should be
- displayed as Coq code rather than running text.)
-
- The second and third [Notation] declarations above introduce the
- standard square-bracket notation for lists; the right-hand side of
- the third one illustrates Coq's syntax for declaring n-ary
- notations and translating them to nested sequences of binary
- constructors. *)
-
-(** *** Repeat *)
-(** A number of functions are useful for manipulating lists.
- For example, the [repeat] function takes a number [n] and a
- [count] and returns a list of length [count] where every element
- is [n]. *)
-
-Fixpoint repeat (n count : nat) : natlist :=
- match count with
- | O => nil
- | S count' => n :: (repeat n count')
- end.
-
-(** *** Length *)
-(** The [length] function calculates the length of a list. *)
-
-Fixpoint length (l:natlist) : nat :=
- match l with
- | nil => O
- | h :: t => S (length t)
- end.
-
-(** *** Append *)
-(** The [app] ("append") function concatenates two lists. *)
-
-Fixpoint app (l1 l2 : natlist) : natlist :=
- match l1 with
- | nil => l2
- | h :: t => h :: (app t l2)
- end.
-
-(** Actually, [app] will be used a lot in some parts of what
- follows, so it is convenient to have an infix operator for it. *)
-
-Notation "x ++ y" := (app x y)
- (right associativity, at level 60).
-
-Example test_app1: [1;2;3] ++ [4;5] = [1;2;3;4;5].
-Proof. reflexivity. Qed.
-Example test_app2: nil ++ [4;5] = [4;5].
-Proof. reflexivity. Qed.
-Example test_app3: [1;2;3] ++ nil = [1;2;3].
-Proof. reflexivity. Qed.
-
-(** Here are two smaller examples of programming with lists.
- The [hd] function returns the first element (the "head") of the
- list, while [tl] returns everything but the first
- element (the "tail").
- Of course, the empty list has no first element, so we
- must pass a default value to be returned in that case. *)
-
-(** *** Head (with default) and Tail *)
-Definition hd (default:nat) (l:natlist) : nat :=
- match l with
- | nil => default
- | h :: t => h
- end.
-
-Definition tl (l:natlist) : natlist :=
- match l with
- | nil => nil
- | h :: t => t
- end.
-
-Example test_hd1: hd 0 [1;2;3] = 1.
-Proof. reflexivity. Qed.
-Example test_hd2: hd 0 [] = 0.
-Proof. reflexivity. Qed.
-Example test_tl: tl [1;2;3] = [2;3].
-Proof. reflexivity. Qed.
-
-(** **** Exercise: 2 stars (list_funs) *)
-(** Complete the definitions of [nonzeros], [oddmembers] and
- [countoddmembers] below. Have a look at the tests to understand
- what these functions should do. *)
-
-Fixpoint nonzeros (l:natlist) : natlist :=
- (* FILL IN HERE *) admit.
-
-Example test_nonzeros: nonzeros [0;1;0;2;3;0;0] = [1;2;3].
- (* FILL IN HERE *) Admitted.
-
-Fixpoint oddmembers (l:natlist) : natlist :=
- (* FILL IN HERE *) admit.
-
-Example test_oddmembers: oddmembers [0;1;0;2;3;0;0] = [1;3].
- (* FILL IN HERE *) Admitted.
-
-Fixpoint countoddmembers (l:natlist) : nat :=
- (* FILL IN HERE *) admit.
-
-Example test_countoddmembers1: countoddmembers [1;0;3;1;4;5] = 4.
- (* FILL IN HERE *) Admitted.
-Example test_countoddmembers2: countoddmembers [0;2;4] = 0.
- (* FILL IN HERE *) Admitted.
-Example test_countoddmembers3: countoddmembers nil = 0.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (alternate) *)
-(** Complete the definition of [alternate], which "zips up" two lists
- into one, alternating between elements taken from the first list
- and elements from the second. See the tests below for more
- specific examples.
-
- Note: one natural and elegant way of writing [alternate] will fail
- to satisfy Coq's requirement that all [Fixpoint] definitions be
- "obviously terminating." If you find yourself in this rut, look
- for a slightly more verbose solution that considers elements of
- both lists at the same time. (One possible solution requires
- defining a new kind of pairs, but this is not the only way.) *)
-
-
-Fixpoint alternate (l1 l2 : natlist) : natlist :=
- (* FILL IN HERE *) admit.
-
-
-Example test_alternate1: alternate [1;2;3] [4;5;6] = [1;4;2;5;3;6].
- (* FILL IN HERE *) Admitted.
-Example test_alternate2: alternate [1] [4;5;6] = [1;4;5;6].
- (* FILL IN HERE *) Admitted.
-Example test_alternate3: alternate [1;2;3] [4] = [1;4;2;3].
- (* FILL IN HERE *) Admitted.
-Example test_alternate4: alternate [] [20;30] = [20;30].
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** ** Bags via Lists *)
-
-(** A [bag] (or [multiset]) is like a set, but each element can appear
- multiple times instead of just once. One reasonable
- implementation of bags is to represent a bag of numbers as a
- list. *)
-
-Definition bag := natlist.
-
-(** **** Exercise: 3 stars (bag_functions) *)
-(** Complete the following definitions for the functions
- [count], [sum], [add], and [member] for bags. *)
-
-Fixpoint count (v:nat) (s:bag) : nat :=
- (* FILL IN HERE *) admit.
-
-(** All these proofs can be done just by [reflexivity]. *)
-
-Example test_count1: count 1 [1;2;3;1;4;1] = 3.
- (* FILL IN HERE *) Admitted.
-Example test_count2: count 6 [1;2;3;1;4;1] = 0.
- (* FILL IN HERE *) Admitted.
-
-(** Multiset [sum] is similar to set [union]: [sum a b] contains
- all the elements of [a] and of [b]. (Mathematicians usually
- define [union] on multisets a little bit differently, which
- is why we don't use that name for this operation.)
- For [sum] we're giving you a header that does not give explicit
- names to the arguments. Moreover, it uses the keyword
- [Definition] instead of [Fixpoint], so even if you had names for
- the arguments, you wouldn't be able to process them recursively.
- The point of stating the question this way is to encourage you to
- think about whether [sum] can be implemented in another way --
- perhaps by using functions that have already been defined. *)
-
-Definition sum : bag -> bag -> bag :=
- (* FILL IN HERE *) admit.
-
-Example test_sum1: count 1 (sum [1;2;3] [1;4;1]) = 3.
- (* FILL IN HERE *) Admitted.
-
-Definition add (v:nat) (s:bag) : bag :=
- (* FILL IN HERE *) admit.
-
-Example test_add1: count 1 (add 1 [1;4;1]) = 3.
- (* FILL IN HERE *) Admitted.
-Example test_add2: count 5 (add 1 [1;4;1]) = 0.
- (* FILL IN HERE *) Admitted.
-
-Definition member (v:nat) (s:bag) : bool :=
- (* FILL IN HERE *) admit.
-
-Example test_member1: member 1 [1;4;1] = true.
- (* FILL IN HERE *) Admitted.
-Example test_member2: member 2 [1;4;1] = false.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (bag_more_functions) *)
-(** Here are some more bag functions for you to practice with. *)
-
-Fixpoint remove_one (v:nat) (s:bag) : bag :=
- (* When remove_one is applied to a bag without the number to remove,
- it should return the same bag unchanged. *)
- (* FILL IN HERE *) admit.
-
-Example test_remove_one1: count 5 (remove_one 5 [2;1;5;4;1]) = 0.
- (* FILL IN HERE *) Admitted.
-Example test_remove_one2: count 5 (remove_one 5 [2;1;4;1]) = 0.
- (* FILL IN HERE *) Admitted.
-Example test_remove_one3: count 4 (remove_one 5 [2;1;4;5;1;4]) = 2.
- (* FILL IN HERE *) Admitted.
-Example test_remove_one4: count 5 (remove_one 5 [2;1;5;4;5;1;4]) = 1.
- (* FILL IN HERE *) Admitted.
-
-Fixpoint remove_all (v:nat) (s:bag) : bag :=
- (* FILL IN HERE *) admit.
-
-Example test_remove_all1: count 5 (remove_all 5 [2;1;5;4;1]) = 0.
- (* FILL IN HERE *) Admitted.
-Example test_remove_all2: count 5 (remove_all 5 [2;1;4;1]) = 0.
- (* FILL IN HERE *) Admitted.
-Example test_remove_all3: count 4 (remove_all 5 [2;1;4;5;1;4]) = 2.
- (* FILL IN HERE *) Admitted.
-Example test_remove_all4: count 5 (remove_all 5 [2;1;5;4;5;1;4;5;1;4]) = 0.
- (* FILL IN HERE *) Admitted.
-
-Fixpoint subset (s1:bag) (s2:bag) : bool :=
- (* FILL IN HERE *) admit.
-
-Example test_subset1: subset [1;2] [2;1;4;1] = true.
- (* FILL IN HERE *) Admitted.
-Example test_subset2: subset [1;2;2] [2;1;4;1] = false.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (bag_theorem) *)
-(** Write down an interesting theorem about bags involving the
- functions [count] and [add], and prove it. Note that, since this
- problem is somewhat open-ended, it's possible that you may come up
- with a theorem which is true, but whose proof requires techniques
- you haven't learned yet. Feel free to ask for help if you get
- stuck! *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ###################################################### *)
-(** * Reasoning About Lists *)
-
-(** Just as with numbers, simple facts about list-processing
- functions can sometimes be proved entirely by simplification. For
- example, the simplification performed by [reflexivity] is enough
- for this theorem... *)
-
-Theorem nil_app : forall l:natlist,
- [] ++ l = l.
-Proof. reflexivity. Qed.
-
-(** ... because the [[]] is substituted into the match position
- in the definition of [app], allowing the match itself to be
- simplified. *)
-
-(** Also, as with numbers, it is sometimes helpful to perform case
- analysis on the possible shapes (empty or non-empty) of an unknown
- list. *)
-
-Theorem tl_length_pred : forall l:natlist,
- pred (length l) = length (tl l).
-Proof.
- intros l. destruct l as [| n l'].
- Case "l = nil".
- reflexivity.
- Case "l = cons n l'".
- reflexivity. Qed.
-
-(** Here, the [nil] case works because we've chosen to define
- [tl nil = nil]. Notice that the [as] annotation on the [destruct]
- tactic here introduces two names, [n] and [l'], corresponding to
- the fact that the [cons] constructor for lists takes two
- arguments (the head and tail of the list it is constructing). *)
-
-(** Usually, though, interesting theorems about lists require
- induction for their proofs. *)
-
-(* ###################################################### *)
-(** ** Micro-Sermon *)
-
-(** Simply reading example proofs will not get you very far! It is
- very important to work through the details of each one, using Coq
- and thinking about what each step of the proof achieves.
- Otherwise it is more or less guaranteed that the exercises will
- make no sense. *)
-
-(* ###################################################### *)
-(** ** Induction on Lists *)
-
-(** Proofs by induction over datatypes like [natlist] are
- perhaps a little less familiar than standard natural number
- induction, but the basic idea is equally simple. Each [Inductive]
- declaration defines a set of data values that can be built up from
- the declared constructors: a boolean can be either [true] or
- [false]; a number can be either [O] or [S] applied to a number; a
- list can be either [nil] or [cons] applied to a number and a list.
-
- Moreover, applications of the declared constructors to one another
- are the _only_ possible shapes that elements of an inductively
- defined set can have, and this fact directly gives rise to a way
- of reasoning about inductively defined sets: a number is either
- [O] or else it is [S] applied to some _smaller_ number; a list is
- either [nil] or else it is [cons] applied to some number and some
- _smaller_ list; etc. So, if we have in mind some proposition [P]
- that mentions a list [l] and we want to argue that [P] holds for
- _all_ lists, we can reason as follows:
-
- - First, show that [P] is true of [l] when [l] is [nil].
-
- - Then show that [P] is true of [l] when [l] is [cons n l'] for
- some number [n] and some smaller list [l'], assuming that [P]
- is true for [l'].
-
- Since larger lists can only be built up from smaller ones,
- eventually reaching [nil], these two things together establish the
- truth of [P] for all lists [l]. Here's a concrete example: *)
-
-Theorem app_assoc : forall l1 l2 l3 : natlist,
- (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
-Proof.
- intros l1 l2 l3. induction l1 as [| n l1'].
- Case "l1 = nil".
- reflexivity.
- Case "l1 = cons n l1'".
- simpl. rewrite -> IHl1'. reflexivity. Qed.
-
-(** Again, this Coq proof is not especially illuminating as a
- static written document -- it is easy to see what's going on if
- you are reading the proof in an interactive Coq session and you
- can see the current goal and context at each point, but this state
- is not visible in the written-down parts of the Coq proof. So a
- natural-language proof -- one written for human readers -- will
- need to include more explicit signposts; in particular, it will
- help the reader stay oriented if we remind them exactly what the
- induction hypothesis is in the second case. *)
-
-(** *** Informal version *)
-
-(** _Theorem_: For all lists [l1], [l2], and [l3],
- [(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3)].
-
- _Proof_: By induction on [l1].
-
- - First, suppose [l1 = []]. We must show
- ([] ++ l2) ++ l3 = [] ++ (l2 ++ l3),
- which follows directly from the definition of [++].
-
- - Next, suppose [l1 = n::l1'], with
- (l1' ++ l2) ++ l3 = l1' ++ (l2 ++ l3)
- (the induction hypothesis). We must show
- ((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ (l2 ++ l3).
-]]
- By the definition of [++], this follows from
- n :: ((l1' ++ l2) ++ l3) = n :: (l1' ++ (l2 ++ l3)),
- which is immediate from the induction hypothesis. []
-*)
-
-(** *** Another example *)
-(**
- Here is a similar example to be worked together in class: *)
-
-Theorem app_length : forall l1 l2 : natlist,
- length (l1 ++ l2) = (length l1) + (length l2).
-Proof.
- (* WORKED IN CLASS *)
- intros l1 l2. induction l1 as [| n l1'].
- Case "l1 = nil".
- reflexivity.
- Case "l1 = cons".
- simpl. rewrite -> IHl1'. reflexivity. Qed.
-
-
-(** *** Reversing a list *)
-(** For a slightly more involved example of an inductive proof
- over lists, suppose we define a "cons on the right" function
- [snoc] like this... *)
-
-Fixpoint snoc (l:natlist) (v:nat) : natlist :=
- match l with
- | nil => [v]
- | h :: t => h :: (snoc t v)
- end.
-
-(** ... and use it to define a list-reversing function [rev]
- like this: *)
-
-Fixpoint rev (l:natlist) : natlist :=
- match l with
- | nil => nil
- | h :: t => snoc (rev t) h
- end.
-
-Example test_rev1: rev [1;2;3] = [3;2;1].
-Proof. reflexivity. Qed.
-Example test_rev2: rev nil = nil.
-Proof. reflexivity. Qed.
-
-(** *** Proofs about reverse *)
-(** Now let's prove some more list theorems using our newly
- defined [snoc] and [rev]. For something a little more challenging
- than the inductive proofs we've seen so far, let's prove that
- reversing a list does not change its length. Our first attempt at
- this proof gets stuck in the successor case... *)
-
-Theorem rev_length_firsttry : forall l : natlist,
- length (rev l) = length l.
-Proof.
- intros l. induction l as [| n l'].
- Case "l = []".
- reflexivity.
- Case "l = n :: l'".
- (* This is the tricky case. Let's begin as usual
- by simplifying. *)
- simpl.
- (* Now we seem to be stuck: the goal is an equality
- involving [snoc], but we don't have any equations
- in either the immediate context or the global
- environment that have anything to do with [snoc]!
-
- We can make a little progress by using the IH to
- rewrite the goal... *)
- rewrite <- IHl'.
- (* ... but now we can't go any further. *)
-Abort.
-
-(** So let's take the equation about [snoc] that would have
- enabled us to make progress and prove it as a separate lemma.
-*)
-
-Theorem length_snoc : forall n : nat, forall l : natlist,
- length (snoc l n) = S (length l).
-Proof.
- intros n l. induction l as [| n' l'].
- Case "l = nil".
- reflexivity.
- Case "l = cons n' l'".
- simpl. rewrite -> IHl'. reflexivity. Qed.
-
-(**
- Note that we make the lemma as _general_ as possible: in particular,
- we quantify over _all_ [natlist]s, not just those that result
- from an application of [rev]. This should seem natural,
- because the truth of the goal clearly doesn't depend on
- the list having been reversed. Moreover, it is much easier
- to prove the more general property.
-*)
-
-(** Now we can complete the original proof. *)
-
-Theorem rev_length : forall l : natlist,
- length (rev l) = length l.
-Proof.
- intros l. induction l as [| n l'].
- Case "l = nil".
- reflexivity.
- Case "l = cons".
- simpl. rewrite -> length_snoc.
- rewrite -> IHl'. reflexivity. Qed.
-
-(** For comparison, here are informal proofs of these two theorems:
-
- _Theorem_: For all numbers [n] and lists [l],
- [length (snoc l n) = S (length l)].
-
- _Proof_: By induction on [l].
-
- - First, suppose [l = []]. We must show
- length (snoc [] n) = S (length []),
- which follows directly from the definitions of
- [length] and [snoc].
-
- - Next, suppose [l = n'::l'], with
- length (snoc l' n) = S (length l').
- We must show
- length (snoc (n' :: l') n) = S (length (n' :: l')).
- By the definitions of [length] and [snoc], this
- follows from
- S (length (snoc l' n)) = S (S (length l')),
-]]
- which is immediate from the induction hypothesis. [] *)
-
-(** _Theorem_: For all lists [l], [length (rev l) = length l].
-
- _Proof_: By induction on [l].
-
- - First, suppose [l = []]. We must show
- length (rev []) = length [],
- which follows directly from the definitions of [length]
- and [rev].
-
- - Next, suppose [l = n::l'], with
- length (rev l') = length l'.
- We must show
- length (rev (n :: l')) = length (n :: l').
- By the definition of [rev], this follows from
- length (snoc (rev l') n) = S (length l')
- which, by the previous lemma, is the same as
- S (length (rev l')) = S (length l').
- This is immediate from the induction hypothesis. [] *)
-
-(** Obviously, the style of these proofs is rather longwinded
- and pedantic. After the first few, we might find it easier to
- follow proofs that give fewer details (since we can easily work
- them out in our own minds or on scratch paper if necessary) and
- just highlight the non-obvious steps. In this more compressed
- style, the above proof might look more like this: *)
-
-(** _Theorem_:
- For all lists [l], [length (rev l) = length l].
-
- _Proof_: First, observe that
- length (snoc l n) = S (length l)
- for any [l]. This follows by a straightforward induction on [l].
- The main property now follows by another straightforward
- induction on [l], using the observation together with the
- induction hypothesis in the case where [l = n'::l']. [] *)
-
-(** Which style is preferable in a given situation depends on
- the sophistication of the expected audience and on how similar the
- proof at hand is to ones that the audience will already be
- familiar with. The more pedantic style is a good default for
- present purposes. *)
-
-(* ###################################################### *)
-(** ** [SearchAbout] *)
-
-(** We've seen that proofs can make use of other theorems we've
- already proved, using [rewrite], and later we will see other ways
- of reusing previous theorems. But in order to refer to a theorem,
- we need to know its name, and remembering the names of all the
- theorems we might ever want to use can become quite difficult! It
- is often hard even to remember what theorems have been proven,
- much less what they are named.
-
- Coq's [SearchAbout] command is quite helpful with this. Typing
- [SearchAbout foo] will cause Coq to display a list of all theorems
- involving [foo]. For example, try uncommenting the following to
- see a list of theorems that we have proved about [rev]: *)
-
-(* SearchAbout rev. *)
-
-(** Keep [SearchAbout] in mind as you do the following exercises and
- throughout the rest of the course; it can save you a lot of time! *)
-
-(** Also, if you are using ProofGeneral, you can run [SearchAbout]
- with [C-c C-a C-a]. Pasting its response into your buffer can be
- accomplished with [C-c C-;]. *)
-
-(* ###################################################### *)
-(** ** List Exercises, Part 1 *)
-
-(** **** Exercise: 3 stars (list_exercises) *)
-(** More practice with lists. *)
-
-Theorem app_nil_end : forall l : natlist,
- l ++ [] = l.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-Theorem rev_involutive : forall l : natlist,
- rev (rev l) = l.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** There is a short solution to the next exercise. If you find
- yourself getting tangled up, step back and try to look for a
- simpler way. *)
-
-Theorem app_assoc4 : forall l1 l2 l3 l4 : natlist,
- l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem snoc_append : forall (l:natlist) (n:nat),
- snoc l n = l ++ [n].
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-Theorem distr_rev : forall l1 l2 : natlist,
- rev (l1 ++ l2) = (rev l2) ++ (rev l1).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** An exercise about your implementation of [nonzeros]: *)
-
-Lemma nonzeros_app : forall l1 l2 : natlist,
- nonzeros (l1 ++ l2) = (nonzeros l1) ++ (nonzeros l2).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (beq_natlist) *)
-(** Fill in the definition of [beq_natlist], which compares
- lists of numbers for equality. Prove that [beq_natlist l l]
- yields [true] for every list [l]. *)
-
-Fixpoint beq_natlist (l1 l2 : natlist) : bool :=
- (* FILL IN HERE *) admit.
-
-Example test_beq_natlist1 : (beq_natlist nil nil = true).
- (* FILL IN HERE *) Admitted.
-Example test_beq_natlist2 : beq_natlist [1;2;3] [1;2;3] = true.
- (* FILL IN HERE *) Admitted.
-Example test_beq_natlist3 : beq_natlist [1;2;3] [1;2;4] = false.
- (* FILL IN HERE *) Admitted.
-
-Theorem beq_natlist_refl : forall l:natlist,
- true = beq_natlist l l.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** ** List Exercises, Part 2 *)
-
-(** **** Exercise: 2 stars (list_design) *)
-(** Design exercise:
- - Write down a non-trivial theorem involving [cons]
- ([::]), [snoc], and [app] ([++]).
- - Prove it. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (bag_proofs) *)
-(** Here are a couple of little theorems to prove about your
- definitions about bags earlier in the file. *)
-
-Theorem count_member_nonzero : forall (s : bag),
- ble_nat 1 (count 1 (1 :: s)) = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** The following lemma about [ble_nat] might help you in the next proof. *)
-
-Theorem ble_n_Sn : forall n,
- ble_nat n (S n) = true.
-Proof.
- intros n. induction n as [| n'].
- Case "0".
- simpl. reflexivity.
- Case "S n'".
- simpl. rewrite IHn'. reflexivity. Qed.
-
-Theorem remove_decreases_count: forall (s : bag),
- ble_nat (count 0 (remove_one 0 s)) (count 0 s) = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (bag_count_sum) *)
-(** Write down an interesting theorem about bags involving the
- functions [count] and [sum], and prove it.*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (rev_injective) *)
-(** Prove that the [rev] function is injective, that is,
-
- forall (l1 l2 : natlist), rev l1 = rev l2 -> l1 = l2.
-
-There is a hard way and an easy way to solve this exercise.
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-(* ###################################################### *)
-(** * Options *)
-
-
-(** One use of [natoption] is as a way of returning "error
- codes" from functions. For example, suppose we want to write a
- function that returns the [n]th element of some list. If we give
- it type [nat -> natlist -> nat], then we'll have to return some
- number when the list is too short! *)
-
-Fixpoint index_bad (n:nat) (l:natlist) : nat :=
- match l with
- | nil => 42 (* arbitrary! *)
- | a :: l' => match beq_nat n O with
- | true => a
- | false => index_bad (pred n) l'
- end
- end.
-
-(** *** *)
-(** On the other hand, if we give it type [nat -> natlist ->
- natoption], then we can return [None] when the list is too short
- and [Some a] when the list has enough members and [a] appears at
- position [n]. *)
-
-Inductive natoption : Type :=
- | Some : nat -> natoption
- | None : natoption.
-
-
-Fixpoint index (n:nat) (l:natlist) : natoption :=
- match l with
- | nil => None
- | a :: l' => match beq_nat n O with
- | true => Some a
- | false => index (pred n) l'
- end
- end.
-
-Example test_index1 : index 0 [4;5;6;7] = Some 4.
-Proof. reflexivity. Qed.
-Example test_index2 : index 3 [4;5;6;7] = Some 7.
-Proof. reflexivity. Qed.
-Example test_index3 : index 10 [4;5;6;7] = None.
-Proof. reflexivity. Qed.
-
-(** This example is also an opportunity to introduce one more
- small feature of Coq's programming language: conditional
- expressions... *)
-
-(** *** *)
-
-Fixpoint index' (n:nat) (l:natlist) : natoption :=
- match l with
- | nil => None
- | a :: l' => if beq_nat n O then Some a else index' (pred n) l'
- end.
-
-(** Coq's conditionals are exactly like those found in any other
- language, with one small generalization. Since the boolean type
- is not built in, Coq actually allows conditional expressions over
- _any_ inductively defined type with exactly two constructors. The
- guard is considered true if it evaluates to the first constructor
- in the [Inductive] definition and false if it evaluates to the
- second. *)
-
-(** The function below pulls the [nat] out of a [natoption], returning
- a supplied default in the [None] case. *)
-
-Definition option_elim (d : nat) (o : natoption) : nat :=
- match o with
- | Some n' => n'
- | None => d
- end.
-
-(** **** Exercise: 2 stars (hd_opt) *)
-(** Using the same idea, fix the [hd] function from earlier so we don't
- have to pass a default element for the [nil] case. *)
-
-Definition hd_opt (l : natlist) : natoption :=
- (* FILL IN HERE *) admit.
-
-Example test_hd_opt1 : hd_opt [] = None.
- (* FILL IN HERE *) Admitted.
-
-Example test_hd_opt2 : hd_opt [1] = Some 1.
- (* FILL IN HERE *) Admitted.
-
-Example test_hd_opt3 : hd_opt [5;6] = Some 5.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (option_elim_hd) *)
-(** This exercise relates your new [hd_opt] to the old [hd]. *)
-
-Theorem option_elim_hd : forall (l:natlist) (default:nat),
- hd default l = option_elim default (hd_opt l).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** * Dictionaries *)
-
-(** As a final illustration of how fundamental data structures
- can be defined in Coq, here is the declaration of a simple
- [dictionary] data type, using numbers for both the keys and the
- values stored under these keys. (That is, a dictionary represents
- a finite map from numbers to numbers.) *)
-
-Module Dictionary.
-
-Inductive dictionary : Type :=
- | empty : dictionary
- | record : nat -> nat -> dictionary -> dictionary.
-
-(** This declaration can be read: "There are two ways to construct a
- [dictionary]: either using the constructor [empty] to represent an
- empty dictionary, or by applying the constructor [record] to
- a key, a value, and an existing [dictionary] to construct a
- [dictionary] with an additional key to value mapping." *)
-
-Definition insert (key value : nat) (d : dictionary) : dictionary :=
- (record key value d).
-
-(** Here is a function [find] that searches a [dictionary] for a
- given key. It evaluates evaluates to [None] if the key was not
- found and [Some val] if the key was mapped to [val] in the
- dictionary. If the same key is mapped to multiple values, [find]
- will return the first one it finds. *)
-
-Fixpoint find (key : nat) (d : dictionary) : natoption :=
- match d with
- | empty => None
- | record k v d' => if (beq_nat key k)
- then (Some v)
- else (find key d')
- end.
-
-
-
-(** **** Exercise: 1 star (dictionary_invariant1) *)
-(** Complete the following proof. *)
-
-Theorem dictionary_invariant1' : forall (d : dictionary) (k v: nat),
- (find k (insert k v d)) = Some v.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star (dictionary_invariant2) *)
-(** Complete the following proof. *)
-
-Theorem dictionary_invariant2' : forall (d : dictionary) (m n o: nat),
- beq_nat m n = false -> find m d = find m (insert n o d).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-End Dictionary.
-
-End NatList.
-
-(* $Date: 2014-01-28 13:19:45 -0500 (Tue, 28 Jan 2014) $ *)
-
diff --git a/Logic.html b/Logic.html
deleted file mode 100644
index 07cf275..0000000
--- a/Logic.html
+++ /dev/null
@@ -1,1194 +0,0 @@
-
-
-
-
-
-Logic: Logic in Coq
-
-
-
-
-
-
-
-
-
-
-
-
-
-
LogicLogic in Coq
-
-
-
-
-
-
-
-
-
-
-RequireExportMoreCoq.
-
-
-
-
-
-Coq's built-in logic is very small: the only primitives are
- Inductive definitions, universal quantification (∀), and
- implication (→), while all the other familiar logical
- connectives — conjunction, disjunction, negation, existential
- quantification, even equality — can be encoded using just these.
-
-
-
- This chapter explains the encodings and shows how the tactics
- we've seen can be used to carry out standard forms of logical
- reasoning involving these connectives.
-
-
-
-
-
-
-
-
Propositions
-
-
-
- In previous chapters, we have seen many examples of factual
- claims (propositions) and ways of presenting evidence of their
- truth (proofs). In particular, we have worked extensively with
- equality propositions of the form e1=e2, with
- implications (P→Q), and with quantified propositions
- (∀x,P).
-
-
-
- In Coq, the type of things that can (potentially)
- be proven is Prop.
-
-
- Here is an example of a provable proposition:
-
-
-
-
-Check (3 = 3).
-(* ===> Prop *)
-
-
-
-
-
-Here is an example of an unprovable proposition:
-
-Recall that Check asks Coq to tell us the type of the indicated
- expression.
-
-
-
-
-
-
-
-
Proofs and Evidence
-
-
-
- In Coq, propositions have the same status as other types, such as
- nat. Just as the natural numbers 0, 1, 2, etc. inhabit
- the type nat, a Coq proposition P is inhabited by its
- proofs. We will refer to such inhabitants as proof term or
- proof object or evidence for the truth of P.
-
-
-
- In Coq, when we state and then prove a lemma such as:
-
-
-
- the tactics we use within the Proof...Qed keywords tell Coq
- how to construct a proof term that inhabits the proposition. In
- this case, the proposition 0×3=0 is justified by a
- combination of the definition of mult, which says that 0×3
- simplifies to just 0, and the reflexive principle of
- equality, which says that 0=0.
-
-
-
- Just as we can create user-defined inductive types (like the
- lists, binary representations of natural numbers, etc., that we
- seen before), we can also create user-defined propositions.
-
-
-
- Question: How do you define the meaning of a proposition?
-
-
-
-
-
-
-
- The meaning of a proposition is given by rules and definitions
- that say how to construct evidence for the truth of the
- proposition from other evidence.
-
-
-
-
-
Typically, rules are defined inductively, just like any other datatype.
-
-
-
-
-
-
Sometimes a proposition is declared to be true without substantiating evidence. Such propositions are called axioms.
-
-
-
-
-
-
-
-
-
- In this, and subsequence chapters, we'll see more about how these
- proof terms work in more detail.
-
-
-
-
-
-
-
-
-
Conjunction (Logical "and")
-
-
-
- The logical conjunction of propositions P and Q can be
- represented using an Inductive definition with one
- constructor.
-
-The intuition behind this definition is simple: to
- construct evidence for andPQ, we must provide evidence
- for P and evidence for Q. More precisely:
-
-
-
-
-
conjpq can be taken as evidence for andPQ if p
- is evidence for P and q is evidence for Q; and
-
-
-
-
-
-
this is the only way to give evidence for andPQ —
- that is, if someone gives us evidence for andPQ, we
- know it must have the form conjpq, where p is
- evidence for P and q is evidence for Q.
-
-
-
-
-
-
- Since we'll be using conjunction a lot, let's introduce a more
- familiar-looking infix notation for it.
-
-(The type_scope annotation tells Coq that this notation
- will be appearing in propositions, not values.)
-
-
- Consider the "type" of the constructor conj:
-
-
-
-
-Checkconj.
-(* ===> forall P Q : Prop, P -> Q -> P /\ Q *)
-
-
-
-
-
-Notice that it takes 4 inputs — namely the propositions P
- and Q and evidence for P and Q — and returns as output the
- evidence of P∧Q.
-
-
-
"Introducing" Conjuctions
- Besides the elegance of building everything up from a tiny
- foundation, what's nice about defining conjunction this way is
- that we can prove statements involving conjunction using the
- tactics that we already know. For example, if the goal statement
- is a conjuction, we can prove it by applying the single
- constructor conj, which (as can be seen from the type of conj)
- solves the current goal and leaves the two parts of the
- conjunction as subgoals to be proved separately.
-
-
-
-
-Theoremand_example :
- (0 = 0) ∧ (4 = mult 2 2).
-Proof.
- applyconj.
- Case "left". reflexivity.
- Case "right". reflexivity. Qed.
-
-
-
-
-
-Just for convenience, we can use the tactic split as a shorthand for
- applyconj.
-
-
-
-
-Theoremand_example' :
- (0 = 0) ∧ (4 = mult 2 2).
-Proof.
- split.
- Case "left". reflexivity.
- Case "right". reflexivity. Qed.
-
-
-
-
-
-
"Eliminating" conjunctions
- Conversely, the inversion tactic can be used to take a
- conjunction hypothesis in the context, calculate what evidence
- must have been used to build it, and add variables representing
- this evidence to the proof context.
-
-Theoremproj2 : ∀PQ : Prop,
- P∧Q→Q.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-Theoremand_commut : ∀PQ : Prop,
- P∧Q→Q∧P.
-Proof.
- (* WORKED IN CLASS *)
- introsPQH.
- inversionHas [HPHQ].
- split.
- Case "left". applyHQ.
- Case "right". applyHP. Qed.
-
-
-
-
-
-
Exercise: 2 stars (and_assoc)
- In the following proof, notice how the nested pattern in the
- inversion breaks the hypothesis H:P∧(Q∧R) down into
- HP:P, HQ:Q, and HR:R. Finish the proof from there:
-
-
-
-
-Theoremand_assoc : ∀PQR : Prop,
- P∧ (Q∧R) → (P∧Q) ∧R.
-Proof.
- introsPQRH.
- inversionHas [HP [HQHR]].
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Iff
-
-
-
- The handy "if and only if" connective is just the conjunction of
- two implications.
-
- Using the above proof that ↔ is symmetric (iff_sym) as
- a guide, prove that it is also reflexive and transitive.
-
-
-
-
-Theoremiff_refl : ∀P : Prop,
- P↔P.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremiff_trans : ∀PQR : Prop,
- (P↔Q) → (Q↔R) → (P↔R).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Hint: If you have an iff hypothesis in the context, you can use
- inversion to break it into two separate implications. (Think
- about why this works.) ☐
-
-
- Some of Coq's tactics treat iff statements specially, thus
- avoiding the need for some low-level manipulation when reasoning
- with them. In particular, rewrite can be used with iff
- statements, not just equalities.
-
-
-
-
-
-
-
-
Disjunction (Logical "or")
-
-
-
-
Implementing Disjunction
-
-
-
- Disjunction ("logical or") can also be defined as an
- inductive proposition.
-
-Consider the "type" of the constructor or_introl:
-
-
-
-
-Checkor_introl.
-(* ===> forall P Q : Prop, P -> P \/ Q *)
-
-
-
-
-
-It takes 3 inputs, namely the propositions P, Q and
- evidence of P, and returns, as output, the evidence of P∨Q.
- Next, look at the type of or_intror:
-
-
-
-
-Checkor_intror.
-(* ===> forall P Q : Prop, Q -> P \/ Q *)
-
-
-
-
-
-It is like or_introl but it requires evidence of Q
- instead of evidence of P.
-
-
- Intuitively, there are two ways of giving evidence for P∨Q:
-
-
-
-
-
give evidence for P (and say that it is P you are giving
- evidence for — this is the function of the or_introl
- constructor), or
-
-
-
-
-
-
give evidence for Q, tagged with the or_intror
- constructor.
-
-
-
-
-
-
- Since P∨Q has two constructors, doing inversion on a
- hypothesis of type P∨Q yields two subgoals.
-
-Theoremor_distributes_over_and : ∀PQR : Prop,
- P∨ (Q∧R) ↔ (P∨Q) ∧ (P∨R).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Relating ∧ and ∨ with andb and orb (advanced)
-
-
-
- We've already seen several places where analogous structures
- can be found in Coq's computational (Type) and logical (Prop)
- worlds. Here is one more: the boolean operators andb and orb
- are clearly analogs of the logical connectives ∧ and ∨.
- This analogy can be made more precise by the following theorems,
- which show how to translate knowledge about andb and orb's
- behaviors on certain inputs into propositional facts about those
- inputs.
-
-
-
-
-Theoremandb_prop : ∀bc,
- andbbc = true→b = true∧c = true.
-Proof.
- (* WORKED IN CLASS *)
- introsbcH.
- destructb.
- Case "b = true". destructc.
- SCase "c = true". applyconj. reflexivity. reflexivity.
- SCase "c = false". inversionH.
- Case "b = false". inversionH. Qed.
-
-
-Theoremandb_true_intro : ∀bc,
- b = true∧c = true→andbbc = true.
-Proof.
- (* WORKED IN CLASS *)
- introsbcH.
- inversionH.
- rewriteH0. rewriteH1. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 2 stars, optional (bool_prop)
-
-
-
-Theoremandb_false : ∀bc,
- andbbc = false→b = false∨c = false.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremorb_prop : ∀bc,
- orbbc = true→b = true∨c = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremorb_false_elim : ∀bc,
- orbbc = false→b = false∧c = false.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Falsehood
-
-
-
- Logical falsehood can be represented in Coq as an inductively
- defined proposition with no constructors.
-
-
-
-
-InductiveFalse : Prop := .
-
-
-
-
-
-Intuition: False is a proposition for which there is no way
- to give evidence.
-
-
- Since False has no constructors, inverting an assumption
- of type False always yields zero subgoals, allowing us to
- immediately prove any goal.
-
-How does this work? The inversion tactic breaks contra into
- each of its possible cases, and yields a subgoal for each case.
- As contra is evidence for False, it has no possible cases,
- hence, there are no possible subgoals and the proof is done.
-
-
-
- Conversely, the only way to prove False is if there is already
- something nonsensical or contradictory in the context:
-
-Actually, since the proof of False_implies_nonsense
- doesn't actually have anything to do with the specific nonsensical
- thing being proved; it can easily be generalized to work for an
- arbitrary P:
-
-
-
-
-Theoremex_falso_quodlibet : ∀(P:Prop),
- False→P.
-Proof.
- (* WORKED IN CLASS *)
- introsPcontra.
- inversioncontra. Qed.
-
-
-
-
-
-The Latin ex falso quodlibet means, literally, "from
- falsehood follows whatever you please." This theorem is also
- known as the principle of explosion.
-
-
-
-
-
-
-
-
Truth
-
-
-
- Since we have defined falsehood in Coq, one might wonder whether
- it is possible to define truth in the same way. We can.
-
-
-
Exercise: 2 stars, advanced (True)
- Define True as another inductively defined proposition. (The
- intution is that True should be a proposition for which it is
- trivial to give evidence.)
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
- However, unlike False, which we'll use extensively, True is
- used fairly rarely. By itself, it is trivial (and therefore
- uninteresting) to prove as a goal, and it carries no useful
- information as a hypothesis. But it can be useful when defining
- complex Props using conditionals, or as a parameter to
- higher-order Props.
-
-
-
-
-
-
-
-
Negation
-
-
-
- The logical complement of a proposition P is written not
- P or, for shorthand, ¬P:
-
-
-
-
-Definitionnot (P:Prop) := P→False.
-
-
-
-
-
-The intuition is that, if P is not true, then anything at
- all (even False) follows from assuming P.
-
-It takes a little practice to get used to working with
- negation in Coq. Even though you can see perfectly well why
- something is true, it can be a little hard at first to get things
- into the right configuration so that Coq can see it! Here are
- proofs of a few familiar facts about negation to get you warmed
- up.
-
-Theoremcontradiction_implies_anything : ∀PQ : Prop,
- (P∧ ¬P) →Q.
-Proof.
- (* WORKED IN CLASS *)
- introsPQH. inversionHas [HPHNA]. unfoldnotinHNA.
- applyHNAinHP. inversionHP. Qed.
-
-
-Theoremdouble_neg : ∀P : Prop,
- P→ ~~P.
-Proof.
- (* WORKED IN CLASS *)
- introsPH. unfoldnot. introsG. applyG. applyH. Qed.
-
-
-
-
-
-
Exercise: 2 stars, advanced (double_neg_inf)
- Write an informal proof of double_neg:
-
-
-
- Theorem: P implies ~~P, for any proposition P.
-
-
-
- Proof:
-(* FILL IN HERE *)
- ☐
-
-
-
-
Exercise: 2 stars (contrapositive)
-
-
-
-Theoremcontrapositive : ∀PQ : Prop,
- (P→Q) → (¬Q→ ¬P).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star (not_both_true_and_false)
-
-
-
-Theoremnot_both_true_and_false : ∀P : Prop,
- ¬ (P∧ ¬P).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, advanced (informal_not_PNP)
- Write an informal proof (in English) of the proposition ∀P
- :Prop,~(P∧¬P).
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Constructive logic
- Note that some theorems that are true in classical logic are not
- provable in Coq's (constructive) logic. E.g., let's look at how
- this proof gets stuck...
-
-
-
-
-Theoremclassic_double_neg : ∀P : Prop,
- ~~P→P.
-Proof.
- (* WORKED IN CLASS *)
- introsPH. unfoldnotinH.
- (* But now what? There is no way to "invent" evidence for ¬P
- from evidence for P. *)
- Abort.
-
-
-
- For those who like a challenge, here is an exercise
- taken from the Coq'Art book (p. 123). The following five
- statements are often considered as characterizations of
- classical logic (as opposed to constructive logic, which is
- what is "built in" to Coq). We can't prove them in Coq, but
- we can consistently add any one of them as an unproven axiom
- if we wish to work in classical logic. Prove that these five
- propositions are equivalent.
-
- This theorem implies that it is always safe to add a decidability
-axiom (i.e. an instance of excluded middle) for any particular Prop P.
-Why? Because we cannot prove the negation of such an axiom; if we could,
-we would have both ¬(P∨¬P) and ¬¬(P∨¬P), a contradiction.
-
-
-
-
-Theoremexcluded_middle_irrefutable: ∀(P:Prop), ¬ ¬ (P∨ ¬ P).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-
Inequality
-
-
-
- Saying x≠y is just the same as saying ~(x=y).
-
-Since inequality involves a negation, it again requires
- a little practice to be able to work with it fluently. Here
- is one very useful trick. If you are trying to prove a goal
- that is nonsensical (e.g., the goal state is false=true),
- apply the lemma ex_falso_quodlibet to change the goal to
- False. This makes it easier to use assumptions of the form
- ¬P that are available in the context — in particular,
- assumptions of the form x≠y.
-
-
-
-
\ No newline at end of file
diff --git a/Logic.v b/Logic.v
deleted file mode 100644
index 317ecf1..0000000
--- a/Logic.v
+++ /dev/null
@@ -1,688 +0,0 @@
-(** * Logic: Logic in Coq *)
-
-Require Export MoreCoq.
-
-
-
-(** Coq's built-in logic is very small: the only primitives are
- [Inductive] definitions, universal quantification ([forall]), and
- implication ([->]), while all the other familiar logical
- connectives -- conjunction, disjunction, negation, existential
- quantification, even equality -- can be encoded using just these.
-
- This chapter explains the encodings and shows how the tactics
- we've seen can be used to carry out standard forms of logical
- reasoning involving these connectives. *)
-
-(* ########################################################### *)
-(** * Propositions *)
-
-(** In previous chapters, we have seen many examples of factual
- claims (_propositions_) and ways of presenting evidence of their
- truth (_proofs_). In particular, we have worked extensively with
- _equality propositions_ of the form [e1 = e2], with
- implications ([P -> Q]), and with quantified propositions
- ([forall x, P]).
-*)
-
-
-(** In Coq, the type of things that can (potentially)
- be proven is [Prop]. *)
-
-(** Here is an example of a provable proposition: *)
-
-Check (3 = 3).
-(* ===> Prop *)
-
-(** Here is an example of an unprovable proposition: *)
-
-Check (forall (n:nat), n = 2).
-(* ===> Prop *)
-
-(** Recall that [Check] asks Coq to tell us the type of the indicated
- expression. *)
-
-(* ########################################################### *)
-(** * Proofs and Evidence *)
-
-(** In Coq, propositions have the same status as other types, such as
- [nat]. Just as the natural numbers [0], [1], [2], etc. inhabit
- the type [nat], a Coq proposition [P] is inhabited by its
- _proofs_. We will refer to such inhabitants as _proof term_ or
- _proof object_ or _evidence_ for the truth of [P].
-
- In Coq, when we state and then prove a lemma such as:
-
-Lemma silly : 0 * 3 = 0.
-Proof. reflexivity. Qed.
-
- the tactics we use within the [Proof]...[Qed] keywords tell Coq
- how to construct a proof term that inhabits the proposition. In
- this case, the proposition [0 * 3 = 0] is justified by a
- combination of the _definition_ of [mult], which says that [0 * 3]
- _simplifies_ to just [0], and the _reflexive_ principle of
- equality, which says that [0 = 0].
-
-
-*)
-
-(** *** *)
-
-Lemma silly : 0 * 3 = 0.
-Proof. reflexivity. Qed.
-
-(** We can see which proof term Coq constructs for a given Lemma by
-using the [Print] directive: *)
-
-Print silly.
-(* ===> silly = eq_refl : 0 * 3 = 0 *)
-
-(** Here, the [eq_refl] proof term witnesses the equality. (More on equality later!)*)
-
-(** ** Implications _are_ functions *)
-
-(** Just as we can implement natural number multiplication as a
-function:
-
-[
-mult : nat -> nat -> nat
-]
-
-The _proof term_ for an implication [P -> Q] is a _function_ that takes evidence for [P] as input and produces evidence for [Q] as its output.
-*)
-
-Lemma silly_implication : (1 + 1) = 2 -> 0 * 3 = 0.
-Proof. intros H. reflexivity. Qed.
-
-(** We can see that the proof term for the above lemma is indeed a
-function: *)
-
-Print silly_implication.
-(* ===> silly_implication = fun _ : 1 + 1 = 2 => eq_refl
- : 1 + 1 = 2 -> 0 * 3 = 0 *)
-
-(** ** Defining Propositions *)
-
-(** Just as we can create user-defined inductive types (like the
- lists, binary representations of natural numbers, etc., that we
- seen before), we can also create _user-defined_ propositions.
-
- Question: How do you define the meaning of a proposition?
-*)
-
-(** *** *)
-
-(** The meaning of a proposition is given by _rules_ and _definitions_
- that say how to construct _evidence_ for the truth of the
- proposition from other evidence.
-
- - Typically, rules are defined _inductively_, just like any other datatype.
-
- - Sometimes a proposition is declared to be true without substantiating evidence. Such propositions are called _axioms_.
-
-
- In this, and subsequence chapters, we'll see more about how these
- proof terms work in more detail.
-*)
-
-(* ########################################################### *)
-(** * Conjunction (Logical "and") *)
-
-(** The logical conjunction of propositions [P] and [Q] can be
- represented using an [Inductive] definition with one
- constructor. *)
-
-Inductive and (P Q : Prop) : Prop :=
- conj : P -> Q -> (and P Q).
-
-(** The intuition behind this definition is simple: to
- construct evidence for [and P Q], we must provide evidence
- for [P] and evidence for [Q]. More precisely:
-
- - [conj p q] can be taken as evidence for [and P Q] if [p]
- is evidence for [P] and [q] is evidence for [Q]; and
-
- - this is the _only_ way to give evidence for [and P Q] --
- that is, if someone gives us evidence for [and P Q], we
- know it must have the form [conj p q], where [p] is
- evidence for [P] and [q] is evidence for [Q].
-
- Since we'll be using conjunction a lot, let's introduce a more
- familiar-looking infix notation for it. *)
-
-Notation "P /\ Q" := (and P Q) : type_scope.
-
-(** (The [type_scope] annotation tells Coq that this notation
- will be appearing in propositions, not values.) *)
-
-(** Consider the "type" of the constructor [conj]: *)
-
-Check conj.
-(* ===> forall P Q : Prop, P -> Q -> P /\ Q *)
-
-(** Notice that it takes 4 inputs -- namely the propositions [P]
- and [Q] and evidence for [P] and [Q] -- and returns as output the
- evidence of [P /\ Q]. *)
-
-(** ** "Introducing" Conjuctions *)
-(** Besides the elegance of building everything up from a tiny
- foundation, what's nice about defining conjunction this way is
- that we can prove statements involving conjunction using the
- tactics that we already know. For example, if the goal statement
- is a conjuction, we can prove it by applying the single
- constructor [conj], which (as can be seen from the type of [conj])
- solves the current goal and leaves the two parts of the
- conjunction as subgoals to be proved separately. *)
-
-Theorem and_example :
- (0 = 0) /\ (4 = mult 2 2).
-Proof.
- apply conj.
- Case "left". reflexivity.
- Case "right". reflexivity. Qed.
-
-(** Just for convenience, we can use the tactic [split] as a shorthand for
- [apply conj]. *)
-
-Theorem and_example' :
- (0 = 0) /\ (4 = mult 2 2).
-Proof.
- split.
- Case "left". reflexivity.
- Case "right". reflexivity. Qed.
-
-(** ** "Eliminating" conjunctions *)
-(** Conversely, the [inversion] tactic can be used to take a
- conjunction hypothesis in the context, calculate what evidence
- must have been used to build it, and add variables representing
- this evidence to the proof context. *)
-
-Theorem proj1 : forall P Q : Prop,
- P /\ Q -> P.
-Proof.
- intros P Q H.
- inversion H as [HP HQ].
- apply HP. Qed.
-
-(** **** Exercise: 1 star, optional (proj2) *)
-Theorem proj2 : forall P Q : Prop,
- P /\ Q -> Q.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-Theorem and_commut : forall P Q : Prop,
- P /\ Q -> Q /\ P.
-Proof.
- (* WORKED IN CLASS *)
- intros P Q H.
- inversion H as [HP HQ].
- split.
- Case "left". apply HQ.
- Case "right". apply HP. Qed.
-
-
-(** **** Exercise: 2 stars (and_assoc) *)
-(** In the following proof, notice how the _nested pattern_ in the
- [inversion] breaks the hypothesis [H : P /\ (Q /\ R)] down into
- [HP: P], [HQ : Q], and [HR : R]. Finish the proof from there: *)
-
-Theorem and_assoc : forall P Q R : Prop,
- P /\ (Q /\ R) -> (P /\ Q) /\ R.
-Proof.
- intros P Q R H.
- inversion H as [HP [HQ HR]].
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-(* ###################################################### *)
-(** * Iff *)
-
-(** The handy "if and only if" connective is just the conjunction of
- two implications. *)
-
-Definition iff (P Q : Prop) := (P -> Q) /\ (Q -> P).
-
-Notation "P <-> Q" := (iff P Q)
- (at level 95, no associativity)
- : type_scope.
-
-Theorem iff_implies : forall P Q : Prop,
- (P <-> Q) -> P -> Q.
-Proof.
- intros P Q H.
- inversion H as [HAB HBA]. apply HAB. Qed.
-
-Theorem iff_sym : forall P Q : Prop,
- (P <-> Q) -> (Q <-> P).
-Proof.
- (* WORKED IN CLASS *)
- intros P Q H.
- inversion H as [HAB HBA].
- split.
- Case "->". apply HBA.
- Case "<-". apply HAB. Qed.
-
-(** **** Exercise: 1 star, optional (iff_properties) *)
-(** Using the above proof that [<->] is symmetric ([iff_sym]) as
- a guide, prove that it is also reflexive and transitive. *)
-
-Theorem iff_refl : forall P : Prop,
- P <-> P.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem iff_trans : forall P Q R : Prop,
- (P <-> Q) -> (Q <-> R) -> (P <-> R).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Hint: If you have an iff hypothesis in the context, you can use
- [inversion] to break it into two separate implications. (Think
- about why this works.) *)
-(** [] *)
-
-
-
-(** Some of Coq's tactics treat [iff] statements specially, thus
- avoiding the need for some low-level manipulation when reasoning
- with them. In particular, [rewrite] can be used with [iff]
- statements, not just equalities. *)
-
-(* ############################################################ *)
-(** * Disjunction (Logical "or") *)
-
-(** ** Implementing Disjunction *)
-
-(** Disjunction ("logical or") can also be defined as an
- inductive proposition. *)
-
-Inductive or (P Q : Prop) : Prop :=
- | or_introl : P -> or P Q
- | or_intror : Q -> or P Q.
-
-Notation "P \/ Q" := (or P Q) : type_scope.
-
-(** Consider the "type" of the constructor [or_introl]: *)
-
-Check or_introl.
-(* ===> forall P Q : Prop, P -> P \/ Q *)
-
-(** It takes 3 inputs, namely the propositions [P], [Q] and
- evidence of [P], and returns, as output, the evidence of [P \/ Q].
- Next, look at the type of [or_intror]: *)
-
-Check or_intror.
-(* ===> forall P Q : Prop, Q -> P \/ Q *)
-
-(** It is like [or_introl] but it requires evidence of [Q]
- instead of evidence of [P]. *)
-
-(** Intuitively, there are two ways of giving evidence for [P \/ Q]:
-
- - give evidence for [P] (and say that it is [P] you are giving
- evidence for -- this is the function of the [or_introl]
- constructor), or
-
- - give evidence for [Q], tagged with the [or_intror]
- constructor. *)
-
-(** *** *)
-(** Since [P \/ Q] has two constructors, doing [inversion] on a
- hypothesis of type [P \/ Q] yields two subgoals. *)
-
-Theorem or_commut : forall P Q : Prop,
- P \/ Q -> Q \/ P.
-Proof.
- intros P Q H.
- inversion H as [HP | HQ].
- Case "left". apply or_intror. apply HP.
- Case "right". apply or_introl. apply HQ. Qed.
-
-(** From here on, we'll use the shorthand tactics [left] and [right]
- in place of [apply or_introl] and [apply or_intror]. *)
-
-Theorem or_commut' : forall P Q : Prop,
- P \/ Q -> Q \/ P.
-Proof.
- intros P Q H.
- inversion H as [HP | HQ].
- Case "left". right. apply HP.
- Case "right". left. apply HQ. Qed.
-
-
-
-
-
-Theorem or_distributes_over_and_1 : forall P Q R : Prop,
- P \/ (Q /\ R) -> (P \/ Q) /\ (P \/ R).
-Proof.
- intros P Q R. intros H. inversion H as [HP | [HQ HR]].
- Case "left". split.
- SCase "left". left. apply HP.
- SCase "right". left. apply HP.
- Case "right". split.
- SCase "left". right. apply HQ.
- SCase "right". right. apply HR. Qed.
-
-(** **** Exercise: 2 stars (or_distributes_over_and_2) *)
-Theorem or_distributes_over_and_2 : forall P Q R : Prop,
- (P \/ Q) /\ (P \/ R) -> P \/ (Q /\ R).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (or_distributes_over_and) *)
-Theorem or_distributes_over_and : forall P Q R : Prop,
- P \/ (Q /\ R) <-> (P \/ Q) /\ (P \/ R).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ################################################### *)
-(** ** Relating [/\] and [\/] with [andb] and [orb] (advanced) *)
-
-(** We've already seen several places where analogous structures
- can be found in Coq's computational ([Type]) and logical ([Prop])
- worlds. Here is one more: the boolean operators [andb] and [orb]
- are clearly analogs of the logical connectives [/\] and [\/].
- This analogy can be made more precise by the following theorems,
- which show how to translate knowledge about [andb] and [orb]'s
- behaviors on certain inputs into propositional facts about those
- inputs. *)
-
-Theorem andb_prop : forall b c,
- andb b c = true -> b = true /\ c = true.
-Proof.
- (* WORKED IN CLASS *)
- intros b c H.
- destruct b.
- Case "b = true". destruct c.
- SCase "c = true". apply conj. reflexivity. reflexivity.
- SCase "c = false". inversion H.
- Case "b = false". inversion H. Qed.
-
-Theorem andb_true_intro : forall b c,
- b = true /\ c = true -> andb b c = true.
-Proof.
- (* WORKED IN CLASS *)
- intros b c H.
- inversion H.
- rewrite H0. rewrite H1. reflexivity. Qed.
-
-(** **** Exercise: 2 stars, optional (bool_prop) *)
-Theorem andb_false : forall b c,
- andb b c = false -> b = false \/ c = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem orb_prop : forall b c,
- orb b c = true -> b = true \/ c = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem orb_false_elim : forall b c,
- orb b c = false -> b = false /\ c = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-(* ################################################### *)
-(** * Falsehood *)
-
-(** Logical falsehood can be represented in Coq as an inductively
- defined proposition with no constructors. *)
-
-Inductive False : Prop := .
-
-(** Intuition: [False] is a proposition for which there is no way
- to give evidence. *)
-
-
-(** Since [False] has no constructors, inverting an assumption
- of type [False] always yields zero subgoals, allowing us to
- immediately prove any goal. *)
-
-Theorem False_implies_nonsense :
- False -> 2 + 2 = 5.
-Proof.
- intros contra.
- inversion contra. Qed.
-
-(** How does this work? The [inversion] tactic breaks [contra] into
- each of its possible cases, and yields a subgoal for each case.
- As [contra] is evidence for [False], it has _no_ possible cases,
- hence, there are no possible subgoals and the proof is done. *)
-
-(** *** *)
-(** Conversely, the only way to prove [False] is if there is already
- something nonsensical or contradictory in the context: *)
-
-Theorem nonsense_implies_False :
- 2 + 2 = 5 -> False.
-Proof.
- intros contra.
- inversion contra. Qed.
-
-(** Actually, since the proof of [False_implies_nonsense]
- doesn't actually have anything to do with the specific nonsensical
- thing being proved; it can easily be generalized to work for an
- arbitrary [P]: *)
-
-Theorem ex_falso_quodlibet : forall (P:Prop),
- False -> P.
-Proof.
- (* WORKED IN CLASS *)
- intros P contra.
- inversion contra. Qed.
-
-(** The Latin _ex falso quodlibet_ means, literally, "from
- falsehood follows whatever you please." This theorem is also
- known as the _principle of explosion_. *)
-
-
-(* #################################################### *)
-(** ** Truth *)
-
-(** Since we have defined falsehood in Coq, one might wonder whether
- it is possible to define truth in the same way. We can. *)
-
-(** **** Exercise: 2 stars, advanced (True) *)
-(** Define [True] as another inductively defined proposition. (The
- intution is that [True] should be a proposition for which it is
- trivial to give evidence.) *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** However, unlike [False], which we'll use extensively, [True] is
- used fairly rarely. By itself, it is trivial (and therefore
- uninteresting) to prove as a goal, and it carries no useful
- information as a hypothesis. But it can be useful when defining
- complex [Prop]s using conditionals, or as a parameter to
- higher-order [Prop]s. *)
-
-(* #################################################### *)
-(** * Negation *)
-
-(** The logical complement of a proposition [P] is written [not
- P] or, for shorthand, [~P]: *)
-
-Definition not (P:Prop) := P -> False.
-
-(** The intuition is that, if [P] is not true, then anything at
- all (even [False]) follows from assuming [P]. *)
-
-Notation "~ x" := (not x) : type_scope.
-
-Check not.
-(* ===> Prop -> Prop *)
-
-(** It takes a little practice to get used to working with
- negation in Coq. Even though you can see perfectly well why
- something is true, it can be a little hard at first to get things
- into the right configuration so that Coq can see it! Here are
- proofs of a few familiar facts about negation to get you warmed
- up. *)
-
-Theorem not_False :
- ~ False.
-Proof.
- unfold not. intros H. inversion H. Qed.
-
-(** *** *)
-Theorem contradiction_implies_anything : forall P Q : Prop,
- (P /\ ~P) -> Q.
-Proof.
- (* WORKED IN CLASS *)
- intros P Q H. inversion H as [HP HNA]. unfold not in HNA.
- apply HNA in HP. inversion HP. Qed.
-
-Theorem double_neg : forall P : Prop,
- P -> ~~P.
-Proof.
- (* WORKED IN CLASS *)
- intros P H. unfold not. intros G. apply G. apply H. Qed.
-
-(** **** Exercise: 2 stars, advanced (double_neg_inf) *)
-(** Write an informal proof of [double_neg]:
-
- _Theorem_: [P] implies [~~P], for any proposition [P].
-
- _Proof_:
-(* FILL IN HERE *)
- []
-*)
-
-(** **** Exercise: 2 stars (contrapositive) *)
-Theorem contrapositive : forall P Q : Prop,
- (P -> Q) -> (~Q -> ~P).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star (not_both_true_and_false) *)
-Theorem not_both_true_and_false : forall P : Prop,
- ~ (P /\ ~P).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, advanced (informal_not_PNP) *)
-(** Write an informal proof (in English) of the proposition [forall P
- : Prop, ~(P /\ ~P)]. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** *** Constructive logic *)
-(** Note that some theorems that are true in classical logic are _not_
- provable in Coq's (constructive) logic. E.g., let's look at how
- this proof gets stuck... *)
-
-Theorem classic_double_neg : forall P : Prop,
- ~~P -> P.
-Proof.
- (* WORKED IN CLASS *)
- intros P H. unfold not in H.
- (* But now what? There is no way to "invent" evidence for [~P]
- from evidence for [P]. *)
- Abort.
-
-(** **** Exercise: 5 stars, advanced, optional (classical_axioms) *)
-(** For those who like a challenge, here is an exercise
- taken from the Coq'Art book (p. 123). The following five
- statements are often considered as characterizations of
- classical logic (as opposed to constructive logic, which is
- what is "built in" to Coq). We can't prove them in Coq, but
- we can consistently add any one of them as an unproven axiom
- if we wish to work in classical logic. Prove that these five
- propositions are equivalent. *)
-
-Definition peirce := forall P Q: Prop,
- ((P->Q)->P)->P.
-Definition classic := forall P:Prop,
- ~~P -> P.
-Definition excluded_middle := forall P:Prop,
- P \/ ~P.
-Definition de_morgan_not_and_not := forall P Q:Prop,
- ~(~P /\ ~Q) -> P\/Q.
-Definition implies_to_or := forall P Q:Prop,
- (P->Q) -> (~P\/Q).
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars (excluded_middle_irrefutable) *)
-(** This theorem implies that it is always safe to add a decidability
-axiom (i.e. an instance of excluded middle) for any _particular_ Prop [P].
-Why? Because we cannot prove the negation of such an axiom; if we could,
-we would have both [~ (P \/ ~P)] and [~ ~ (P \/ ~P)], a contradiction. *)
-
-Theorem excluded_middle_irrefutable: forall (P:Prop), ~ ~ (P \/ ~ P).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-(* ########################################################## *)
-(** ** Inequality *)
-
-(** Saying [x <> y] is just the same as saying [~(x = y)]. *)
-
-Notation "x <> y" := (~ (x = y)) : type_scope.
-
-(** Since inequality involves a negation, it again requires
- a little practice to be able to work with it fluently. Here
- is one very useful trick. If you are trying to prove a goal
- that is nonsensical (e.g., the goal state is [false = true]),
- apply the lemma [ex_falso_quodlibet] to change the goal to
- [False]. This makes it easier to use assumptions of the form
- [~P] that are available in the context -- in particular,
- assumptions of the form [x<>y]. *)
-
-Theorem not_false_then_true : forall b : bool,
- b <> false -> b = true.
-Proof.
- intros b H. destruct b.
- Case "b = true". reflexivity.
- Case "b = false".
- unfold not in H.
- apply ex_falso_quodlibet.
- apply H. reflexivity. Qed.
-
-
-(** *** *)
-
-(** *** *)
-
-(** *** *)
-
-(** *** *)
-
-(** *** *)
-
-(** **** Exercise: 2 stars (false_beq_nat) *)
-Theorem false_beq_nat : forall n m : nat,
- n <> m ->
- beq_nat n m = false.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (beq_nat_false) *)
-Theorem beq_nat_false : forall n m,
- beq_nat n m = false -> n <> m.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-
-
-(* $Date: 2014-06-05 07:22:21 -0400 (Thu, 05 Jun 2014) $ *)
-
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 82033db..0000000
--- a/Makefile
+++ /dev/null
@@ -1,220 +0,0 @@
-#############################################################################
-## v # The Coq Proof Assistant ##
-## "$@" || ( RV=$$?; rm -f "$@"; exit $${RV} )
-
-%.v.beautified:
- $(COQC) $(COQDEBUG) $(COQFLAGS) -beautify $*
-
-# WARNING
-#
-# This Makefile has been automagically generated
-# Edit at your own risks !
-#
-# END OF WARNING
-
-include .depend
diff --git a/MoreCoq.html b/MoreCoq.html
deleted file mode 100644
index b256a98..0000000
--- a/MoreCoq.html
+++ /dev/null
@@ -1,1772 +0,0 @@
-
-
-
-
-
-MoreCoq: More About Coq
-
-
-
-
-
-
-
-
-
-
-
-
-
-
MoreCoqMore About Coq
-
-
-
-
-
-
-
-
-
-
-RequireExportPoly.
-
-
-
-
-
-This chapter introduces several more Coq tactics that,
- together, allow us to prove many more theorems about the
- functional programs we are writing.
-
-
-
-
-
-
-
-
The apply Tactic
-
-
-
- We often encounter situations where the goal to be proved is
- exactly the same as some hypothesis in the context or some
- previously proved lemma.
-
-
-
-
-Theoremsilly1 : ∀(nmop : nat),
- n = m→
- [n;o] = [n;p] →
- [n;o] = [m;p].
-Proof.
- introsnmopeq1eq2.
- rewrite←eq1.
- (* At this point, we could finish with
- "rewrite→eq2.reflexivity." as we have
- done several times above. But we can achieve the
- same effect in a single step by using the
- apply tactic instead: *)
- applyeq2. Qed.
-
-
-
-
-
-The apply tactic also works with conditional hypotheses
- and lemmas: if the statement being applied is an implication, then
- the premises of this implication will be added to the list of
- subgoals needing to be proved.
-
-You may find it instructive to experiment with this proof
- and see if there is a way to complete it using just rewrite
- instead of apply.
-
-
- Typically, when we use applyH, the statement H will
- begin with a ∀ binding some universal variables. When
- Coq matches the current goal against the conclusion of H, it
- will try to find appropriate values for these variables. For
- example, when we do applyeq2 in the following proof, the
- universal variable q in eq2 gets instantiated with n and r
- gets instantiated with m.
-
-
- To use the apply tactic, the (conclusion of the) fact
- being applied must match the goal exactly — for example, apply
- will not work if the left and right sides of the equality are
- swapped.
-
-
-
-
-Theoremsilly3_firsttry : ∀(n : nat),
- true = beq_natn 5 →
- beq_nat (S (Sn)) 7 = true.
-Proof.
- introsnH.
- simpl.
- (* Here we cannot use apply directly *)
-Abort.
-
-
-
-
-
-In this case we can use the symmetry tactic, which switches the
- left and right sides of an equality in the goal.
-
-
-
-
-Theoremsilly3 : ∀(n : nat),
- true = beq_natn 5 →
- beq_nat (S (Sn)) 7 = true.
-Proof.
- introsnH.
- symmetry.
- simpl. (* Actually, this simpl is unnecessary, since
- apply will perform simplification first. *)
- applyH. Qed.
-
-
-
-
-
-
Exercise: 3 stars (apply_exercise1)
- Hint: you can use apply with previously defined lemmas, not
- just hypotheses in the context. Remember that SearchAbout is
- your friend.
-
-
-
-
-Theoremrev_exercise1 : ∀(ll' : listnat),
- l = revl'→
- l' = revl.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (apply_rewrite)
- Briefly explain the difference between the tactics apply and
- rewrite. Are there situations where both can usefully be
- applied?
- (* FILL IN HERE *)
- ☐
-
-
-
-
-
-
-
-
The apply...with... Tactic
-
-
-
- The following silly example uses two rewrites in a row to
- get from [a,b] to [e,f].
-
-Now, we should be able to use trans_eq to
- prove the above example. However, to do this we need
- a slight refinement of the apply tactic.
-
-
-
-
-Exampletrans_eq_example' : ∀(abcdef : nat),
- [a;b] = [c;d] →
- [c;d] = [e;f] →
- [a;b] = [e;f].
-Proof.
- introsabcdefeq1eq2.
- (* If we simply tell Coq applytrans_eq at this point,
- it can tell (by matching the goal against the
- conclusion of the lemma) that it should instantiate X
- with [nat], n with [a,b], and o with [e,f].
- However, the matching process doesn't determine an
- instantiation for m: we have to supply one explicitly
- by adding with(m:=[c,d]) to the invocation of
- apply. *)
- applytrans_eqwith (m:=[c;d]). applyeq1. applyeq2. Qed.
-
-
-
-
-
- Actually, we usually don't have to include the name m
- in the with clause; Coq is often smart enough to
- figure out which instantiation we're giving. We could
- instead write: applytrans_eqwith[c,d].
-
-
-
Exercise: 3 stars, optional (apply_with_exercise)
-
-
-
-Exampletrans_eq_exercise : ∀(nmop : nat),
- m = (minustwoo) →
- (n + p) = m→
- (n + p) = (minustwoo).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
The inversion tactic
-
-
-
- Recall the definition of natural numbers:
-
-
-
-
- Inductivenat : Type :=
- | O : nat
- | S : nat→nat.
-
-
-
- It is clear from this definition that every number has one of two
- forms: either it is the constructor O or it is built by applying
- the constructor S to another number. But there is more here than
- meets the eye: implicit in the definition (and in our informal
- understanding of how datatype declarations work in other
- programming languages) are two other facts:
-
-
-
-
-
The constructor S is injective. That is, the only way we can
- have Sn=Sm is if n=m.
-
-
-
-
-
-
The constructors O and S are disjoint. That is, O is not
- equal to Sn for any n.
-
-
-
-
-
- Similar principles apply to all inductively defined types: all
- constructors are injective, and the values built from distinct
- constructors are never equal. For lists, the cons constructor is
- injective and nil is different from every non-empty list. For
- booleans, true and false are unequal. (Since neither true
- nor false take any arguments, their injectivity is not an issue.)
-
-
- Coq provides a tactic called inversion that allows us to exploit
- these principles in proofs.
-
-
-
- The inversion tactic is used like this. Suppose H is a
- hypothesis in the context (or a previously proven lemma) of the
- form
-
-
-
-
- ca1a2 ... an = db1b2 ... bm
-
-
-
- for some constructors c and d and arguments a1...an and
- b1...bm. Then inversionH instructs Coq to "invert" this
- equality to extract the information it contains about these terms:
-
-
-
-
-
If c and d are the same constructor, then we know, by the
- injectivity of this constructor, that a1=b1, a2=b2,
- etc.; inversionH adds these facts to the context, and tries
- to use them to rewrite the goal.
-
-
-
-
-
-
If c and d are different constructors, then the hypothesis
- H is contradictory. That is, a false assumption has crept
- into the context, and this means that any goal whatsoever is
- provable! In this case, inversionH marks the current goal as
- completed and pops it off the goal stack.
-
-
-
-
-
- The inversion tactic is probably easier to understand by
- seeing it in action than from general descriptions like the above.
- Below you will find example theorems that demonstrate the use of
- inversion and exercises to test your understanding.
-
-
-
-
-Theoremeq_add_S : ∀(nm : nat),
- Sn = Sm→
- n = m.
-Proof.
- introsnmeq. inversioneq. reflexivity. Qed.
-
-
-Theoremsilly4 : ∀(nm : nat),
- [n] = [m] →
- n = m.
-Proof.
- introsnoeq. inversioneq. reflexivity. Qed.
-
-
-
-
-
-As a convenience, the inversion tactic can also
- destruct equalities between complex values, binding
- multiple variables as it goes.
-
-Examplesillyex2 : ∀(X : Type) (xyz : X) (lj : listX),
- x :: y :: l = [] →
- y :: l = z :: j→
- x = z.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- While the injectivity of constructors allows us to reason
- ∀(nm:nat),Sn=Sm→n=m, the reverse direction of
- the implication is an instance of a more general fact about
- constructors and functions, which we will often find useful:
-
- A couple more nontrivial but not-too-complicated proofs to work
- together in class, or for you to work as exercises.
-
-
-
-
-Theorembeq_nat_0_l : ∀n,
- beq_nat 0 n = true→n = 0.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theorembeq_nat_0_r : ∀n,
- beq_natn 0 = true→n = 0.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Using Tactics on Hypotheses
-
-
-
- By default, most tactics work on the goal formula and leave
- the context unchanged. However, most tactics also have a variant
- that performs a similar operation on a statement in the context.
-
-
-
- For example, the tactic simplinH performs simplification in
- the hypothesis named H in the context.
-
-Similarly, the tactic applyLinH matches some
- conditional statement L (of the form L1→L2, say) against a
- hypothesis H in the context. However, unlike ordinary
- apply (which rewrites a goal matching L2 into a subgoal L1),
- applyLinH matches H against L1 and, if successful,
- replaces it with L2.
-
-
-
- In other words, applyLinH gives us a form of "forward
- reasoning" — from L1→L2 and a hypothesis matching L1, it
- gives us a hypothesis matching L2. By contrast, applyL is
- "backward reasoning" — it says that if we know L1→L2 and we
- are trying to prove L2, it suffices to prove L1.
-
-
-
- Here is a variant of a proof from above, using forward reasoning
- throughout instead of backward reasoning.
-
-Forward reasoning starts from what is given (premises,
- previously proven theorems) and iteratively draws conclusions from
- them until the goal is reached. Backward reasoning starts from
- the goal, and iteratively reasons about what would imply the
- goal, until premises or previously proven theorems are reached.
- If you've seen informal proofs before (for example, in a math or
- computer science class), they probably used forward reasoning. In
- general, Coq tends to favor backward reasoning, but in some
- situations the forward style can be easier to use or to think
- about.
-
-
-
Exercise: 3 stars (plus_n_n_injective)
- Practice using "in" variants in this exercise.
-
-
-
-
-Theoremplus_n_n_injective : ∀nm,
- n + n = m + m→
- n = m.
-Proof.
- introsn. inductionnas [| n'].
- (* Hint: use the plus_n_Sm lemma *)
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Varying the Induction Hypothesis
-
-
-
- Sometimes it is important to control the exact form of the
- induction hypothesis when carrying out inductive proofs in Coq.
- In particular, we need to be careful about which of the
- assumptions we move (using intros) from the goal to the context
- before invoking the induction tactic. For example, suppose
- we want to show that the double function is injective — i.e.,
- that it always maps different arguments to different results:
-
-
-
-
- Theoremdouble_injective: ∀nm, doublen = doublem→n = m.
-
-
-
- The way we start this proof is a little bit delicate: if we
- begin it with
-
-
-
-
- introsn. inductionn.
-
-
-
- all is well. But if we begin it with
-
-
-
-
- introsnm. inductionn.
-
-
-
- we get stuck in the middle of the inductive case...
-
-
-
-
-Theoremdouble_injective_FAILED : ∀nm,
- doublen = doublem→
- n = m.
-Proof.
- introsnm. inductionnas [| n'].
- Case "n = O". simpl. introseq. destructmas [| m'].
- SCase "m = O". reflexivity.
- SCase "m = S m'". inversioneq.
- Case "n = S n'". introseq. destructmas [| m'].
- SCase "m = O". inversioneq.
- SCase "m = S m'". applyf_equal.
- (* Here we are stuck. The induction hypothesis, IHn', does
- not give us n'=m' -- there is an extra S in the
- way -- so the goal is not provable. *)
- Abort.
-
-
-
-
-
-What went wrong?
-
-
- The problem is that, at the point we invoke the induction
- hypothesis, we have already introduced m into the context —
- intuitively, we have told Coq, "Let's consider some particular
- n and m..." and we now have to prove that, if doublen=
- doublem for this particularn and m, then n=m.
-
-
-
- The next tactic, inductionn says to Coq: We are going to show
- the goal by induction on n. That is, we are going to prove that
- the proposition
-
-
-
-
-
Pn = "if doublen=doublem, then n=m"
-
-
-
-
-
-
- holds for all n by showing
-
-
-
-
-
PO
-
-
-
- (i.e., "if doubleO=doublem then O=m")
-
-
-
-
-
-
Pn→P(Sn)
-
-
-
- (i.e., "if doublen=doublem then n=m" implies "if
- double(Sn)=doublem then Sn=m").
-
-
-
-
-
-
- If we look closely at the second statement, it is saying something
- rather strange: it says that, for a particularm, if we know
-
-
-
-
-
"if doublen=doublem then n=m"
-
-
-
-
-
-
- then we can prove
-
-
-
-
-
"if double(Sn)=doublem then Sn=m".
-
-
-
-
-
-
- To see why this is strange, let's think of a particular m —
- say, 5. The statement is then saying that, if we know
-
-
-
-
-
Q = "if doublen=10 then n=5"
-
-
-
-
-
-
- then we can prove
-
-
-
-
-
R = "if double(Sn)=10 then Sn=5".
-
-
-
-
-
-
- But knowing Q doesn't give us any help with proving R! (If we
- tried to prove R from Q, we would say something like "Suppose
- double(Sn)=10..." but then we'd be stuck: knowing that
- double(Sn) is 10 tells us nothing about whether doublen
- is 10, so Q is useless at this point.)
-
-
- To summarize: Trying to carry out this proof by induction on n
- when m is already in the context doesn't work because we are
- trying to prove a relation involving everyn but just a
- singlem.
-
-
- The good proof of double_injective leaves m in the goal
- statement at the point where the induction tactic is invoked on
- n:
-
-
-
-
-Theoremdouble_injective : ∀nm,
- doublen = doublem→
- n = m.
-Proof.
- introsn. inductionnas [| n'].
- Case "n = O". simpl. introsmeq. destructmas [| m'].
- SCase "m = O". reflexivity.
- SCase "m = S m'". inversioneq.
- Case "n = S n'".
- (* Notice that both the goal and the induction
- hypothesis have changed: the goal asks us to prove
- something more general (i.e., to prove the
- statement for _every_ m), but the IH is
- correspondingly more flexible, allowing us to
- choose any m we like when we apply the IH. *)
- introsmeq.
- (* Now we choose a particular m and introduce the
- assumption that doublen=doublem. Since we
- are doing a case analysis on n, we need a case
- analysis on m to keep the two "in sync." *)
- destructmas [| m'].
- SCase "m = O".
- (* The 0 case is trivial *)
- inversioneq.
- SCase "m = S m'".
- applyf_equal.
- (* At this point, since we are in the second
- branch of the destructm, the m' mentioned
- in the context at this point is actually the
- predecessor of the one we started out talking
- about. Since we are also in the S branch of
- the induction, this is perfect: if we
- instantiate the generic m in the IH with the
- m' that we are talking about right now (this
- instantiation is performed automatically by
- apply), then IHn' gives us exactly what we
- need to finish the proof. *)
- applyIHn'. inversioneq. reflexivity. Qed.
-
-
-
-
-
-What this teaches us is that we need to be careful about using
- induction to try to prove something too specific: If we're proving
- a property of n and m by induction on n, we may need to
- leave m generic.
-
-
- The proof of this theorem (left as an exercise) has to be treated similarly:
-
-
-
Exercise: 2 stars (beq_nat_true)
-
-
-
-Theorembeq_nat_true : ∀nm,
- beq_natnm = true→n = m.
-Proof.
- (* FILL IN HERE *)Admitted.
-
- Give a careful informal proof of beq_nat_true, being as explicit
- as possible about quantifiers.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
- The strategy of doing fewer intros before an induction doesn't
- always work directly; sometimes a little rearrangement of
- quantified variables is needed. Suppose, for example, that we
- wanted to prove double_injective by induction on m instead of
- n.
-
-
-
-
-Theoremdouble_injective_take2_FAILED : ∀nm,
- doublen = doublem→
- n = m.
-Proof.
- introsnm. inductionmas [| m'].
- Case "m = O". simpl. introseq. destructnas [| n'].
- SCase "n = O". reflexivity.
- SCase "n = S n'". inversioneq.
- Case "m = S m'". introseq. destructnas [| n'].
- SCase "n = O". inversioneq.
- SCase "n = S n'". applyf_equal.
- (* Stuck again here, just like before. *)
-Abort.
-
-
-
-
-
-The problem is that, to do induction on m, we must first
- introduce n. (If we simply say inductionm without
- introducing anything first, Coq will automatically introduce
- n for us!)
-
-
- What can we do about this? One possibility is to rewrite the
- statement of the lemma so that m is quantified before n. This
- will work, but it's not nice: We don't want to have to mangle the
- statements of lemmas to fit the needs of a particular strategy for
- proving them — we want to state them in the most clear and
- natural way.
-
-
- What we can do instead is to first introduce all the
- quantified variables and then re-generalize one or more of
- them, taking them out of the context and putting them back at
- the beginning of the goal. The generalizedependent tactic
- does this.
-
-
-
-
-Theoremdouble_injective_take2 : ∀nm,
- doublen = doublem→
- n = m.
-Proof.
- introsnm.
- (* n and m are both in the context *)
- generalizedependentn.
- (* Now n is back in the goal and we can do induction on
- m and get a sufficiently general IH. *)
- inductionmas [| m'].
- Case "m = O". simpl. introsneq. destructnas [| n'].
- SCase "n = O". reflexivity.
- SCase "n = S n'". inversioneq.
- Case "m = S m'". introsneq. destructnas [| n'].
- SCase "n = O". inversioneq.
- SCase "n = S n'". applyf_equal.
- applyIHm'. inversioneq. reflexivity. Qed.
-
-
-
-
-
-Let's look at an informal proof of this theorem. Note that
- the proposition we prove by induction leaves n quantified,
- corresponding to the use of generalize dependent in our formal
- proof.
-
-
-
-Theorem: For any nats n and m, if doublen=doublem, then
- n=m.
-
-
-
-Proof: Let m be a nat. We prove by induction on m that, for
- any n, if doublen=doublem then n=m.
-
-
-
-
-
First, suppose m=0, and suppose n is a number such
- that doublen=doublem. We must show that n=0.
-
-
-
- Since m=0, by the definition of double we have doublen=
- 0. There are two cases to consider for n. If n=0 we are
- done, since this is what we wanted to show. Otherwise, if n=S
- n' for some n', we derive a contradiction: by the definition of
- double we would have doublen=S(S(doublen')), but this
- contradicts the assumption that doublen=0.
-
-
-
-
-
-
Otherwise, suppose m=Sm' and that n is again a number such
- that doublen=doublem. We must show that n=Sm', with
- the induction hypothesis that for every number s, if doubles=
- doublem' then s=m'.
-
-
-
- By the fact that m=Sm' and the definition of double, we
- have doublen=S(S(doublem')). There are two cases to
- consider for n.
-
-
-
- If n=0, then by definition doublen=0, a contradiction.
- Thus, we may assume that n=Sn' for some n', and again by
- the definition of double we have S(S(doublen'))=S(S
- (doublem')), which implies by inversion that doublen'=double
- m'.
-
-
-
- Instantiating the induction hypothesis with n' thus allows us to
- conclude that n'=m', and it follows immediately that Sn'=S
- m'. Since Sn'=n and Sm'=m, this is just what we wanted
- to show. ☐
-
-
-
-
-
- Here's another illustration of inversion and using an
- appropriately general induction hypothesis. This is a slightly
- roundabout way of stating a fact that we have already proved
- above. The extra equalities force us to do a little more
- equational reasoning and exercise some of the tactics we've seen
- recently.
-
-It might be tempting to start proving the above theorem
- by introducing n and eq at the outset. However, this leads
- to an induction hypothesis that is not strong enough. Compare
- the above to the following (aborted) attempt:
-
-As in the double examples, the problem is that by
- introducing n before doing induction on l, the induction
- hypothesis is specialized to one particular natural number, namely
- n. In the induction case, however, we need to be able to use
- the induction hypothesis on some other natural number n'.
- Retaining the more general form of the induction hypothesis thus
- gives us more flexibility.
-
-
-
- In general, a good rule of thumb is to make the induction hypothesis
- as general as possible.
-
-
-
Exercise: 3 stars (gen_dep_practice)
-
-
-
- Prove this by induction on l.
-
-
-
-
-Theoremindex_after_last: ∀(n : nat) (X : Type) (l : listX),
- lengthl = n→
- indexnl = None.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-Theoremlength_snoc''' : ∀(n : nat) (X : Type)
- (v : X) (l : listX),
- lengthl = n→
- length (snoclv) = Sn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (app_length_cons)
- Prove this by induction on l1, without using app_length.
-
-
-
-
-Theoremapp_length_cons : ∀(X : Type) (l1l2 : listX)
- (x : X) (n : nat),
- length (l1 ++ (x :: l2)) = n→
- S (length (l1 ++ l2)) = n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 4 stars, optional (app_length_twice)
- Prove this by induction on l, without using app_length.
-
-
-
-
-Theoremapp_length_twice : ∀(X:Type) (n:nat) (l:listX),
- lengthl = n→
- length (l ++ l) = n + n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (double_induction)
- Prove the following principle of induction over two naturals.
-
-
-
-
-Theoremdouble_induction: ∀(P : nat→nat→Prop),
- P 0 0 →
- (∀m, Pm 0 →P (Sm) 0) →
- (∀n, P 0 n→P 0 (Sn)) →
- (∀mn, Pmn→P (Sm) (Sn)) →
- ∀mn, Pmn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Using destruct on Compound Expressions
-
-
-
- We have seen many examples where the destruct tactic is
- used to perform case analysis of the value of some variable. But
- sometimes we need to reason by cases on the result of some
- expression. We can also do this with destruct.
-
-
-After unfolding sillyfun in the above proof, we find that
- we are stuck on if(beq_natn3)then...else.... Well,
- either n is equal to 3 or it isn't, so we use destruct
- (beq_natn3) to let us reason about the two cases.
-
-
-
- In general, the destruct tactic can be used to perform case
- analysis of the results of arbitrary computations. If e is an
- expression whose type is some inductively defined type T, then,
- for each constructor c of T, destructe generates a subgoal
- in which all occurrences of e (in the goal and in the context)
- are replaced by c.
-
-
-
-
-
-
-
Exercise: 1 star (override_shadow)
-
-
-
-Theoremoverride_shadow : ∀(X:Type) x1x2k1k2 (f : nat→X),
- (override (overridefk1x2) k1x1) k2 = (overridefk1x1) k2.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (combine_split)
- Complete the proof below
-
-
-
-
-Theoremcombine_split : ∀XY (l : list (X × Y)) l1l2,
- splitl = (l1, l2) →
- combinel1l2 = l.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- Sometimes, doing a destruct on a compound expression (a
- non-variable) will erase information we need to complete a proof. For example, suppose
- we define a function sillyfun1 like this:
-
-And suppose that we want to convince Coq of the rather
- obvious observation that sillyfun1n yields true only when n
- is odd. By analogy with the proofs we did with sillyfun above,
- it is natural to start the proof like this:
-
-We get stuck at this point because the context does not
- contain enough information to prove the goal! The problem is that
- the substitution peformed by destruct is too brutal — it threw
- away every occurrence of beq_natn3, but we need to keep some
- memory of this expression and how it was destructed, because we
- need to be able to reason that since, in this branch of the case
- analysis, beq_natn3=true, it must be that n=3, from
- which it follows that n is odd.
-
-
-
- What we would really like is to substitute away all existing
- occurences of beq_natn3, but at the same time add an equation
- to the context that records which case we are in. The eqn:
- qualifier allows us to introduce such an equation (with whatever
- name we choose).
-
-
-
-
-Theoremsillyfun1_odd : ∀(n : nat),
- sillyfun1n = true→
- oddbn = true.
-Proof.
- introsneq. unfoldsillyfun1ineq.
- destruct (beq_natn 3) eqn:Heqe3.
- (* Now we have the same state as at the point where we got stuck
- above, except that the context contains an extra equality
- assumption, which is exactly what we need to make progress. *)
- Case "e3 = true". applybeq_nat_trueinHeqe3.
- rewrite→Heqe3. reflexivity.
- Case "e3 = false".
- (* When we come to the second equality test in the body of the
- function we are reasoning about, we can use eqn: again in the
- same way, allow us to finish the proof. *)
- destruct (beq_natn 5) eqn:Heqe5.
- SCase "e5 = true".
- applybeq_nat_trueinHeqe5.
- rewrite→Heqe5. reflexivity.
- SCase "e5 = false". inversioneq. Qed.
-
-
-
-
-
-
Exercise: 2 stars (destruct_eqn_practice)
-
-
-
-Theorembool_fn_applied_thrice :
- ∀(f : bool→bool) (b : bool),
- f (f (fb)) = fb.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars (override_same)
-
-
-
-Theoremoverride_same : ∀(X:Type) x1k1k2 (f : nat→X),
- fk1 = x1→
- (overridefk1x1) k2 = fk2.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Review
-
-
-
- We've now seen a bunch of Coq's fundamental tactics. We'll
- introduce a few more as we go along through the coming lectures,
- and later in the course we'll introduce some more powerful
- automation tactics that make Coq do more of the low-level work
- in many cases. But basically we've got what we need to get work
- done.
-
-
-
- Here are the ones we've seen:
-
-
-
-
-
intros:
- move hypotheses/variables from goal to context
-
-
-
-
-
-
reflexivity:
- finish the proof (when the goal looks like e=e)
-
-
-
-
-
-
apply:
- prove goal using a hypothesis, lemma, or constructor
-
-
-
-
-
-
apply...inH:
- apply a hypothesis, lemma, or constructor to a hypothesis in
- the context (forward reasoning)
-
-
-
-
-
-
apply...with...:
- explicitly specify values for variables that cannot be
- determined by pattern matching
-
-
-
-
-
-
simpl:
- simplify computations in the goal
-
-
-
-
-
-
simplinH:
- ... or a hypothesis
-
-
-
-
-
-
rewrite:
- use an equality hypothesis (or lemma) to rewrite the goal
-
-
-
-
-
-
rewrite...inH:
- ... or a hypothesis
-
-
-
-
-
-
symmetry:
- changes a goal of the form t=u into u=t
-
-
-
-
-
-
symmetryinH:
- changes a hypothesis of the form t=u into u=t
-
-
-
-
-
-
unfold:
- replace a defined constant by its right-hand side in the goal
-
-
-
-
-
-
unfold...inH:
- ... or a hypothesis
-
-
-
-
-
-
destruct...as...:
- case analysis on values of inductively defined types
-
-
-
-
-
-
destruct...eqn:...:
- specify the name of an equation to be added to the context,
- recording the result of the case analysis
-
-
-
-
-
-
induction...as...:
- induction on values of inductively defined types
-
-
-
-
-
-
inversion:
- reason by injectivity and distinctness of constructors
-
-
-
-
-
-
assert(e)asH:
- introduce a "local lemma" e and call it H
-
-
-
-
-
-
generalizedependentx:
- move the variable x (and anything else that depends on it)
- from the context back to an explicit hypothesis in the goal
- formula
-
-
-
-
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 3 stars (beq_nat_sym)
-
-
-
-Theorembeq_nat_sym : ∀(nm : nat),
- beq_natnm = beq_natmn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
- Give an informal proof of this lemma that corresponds to your
- formal proof above:
-
-
-
- Theorem: For any nats nm, beq_natnm=beq_natmn.
-
-
-
- Proof:
- (* FILL IN HERE *)
-☐
-
-
-
-
Exercise: 3 stars, optional (beq_nat_trans)
-
-
-
-Theorembeq_nat_trans : ∀nmp,
- beq_natnm = true→
- beq_natmp = true→
- beq_natnp = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (split_combine)
- We have just proven that for all lists of pairs, combine is the
- inverse of split. How would you formalize the statement that
- split is the inverse of combine?
-
-
-
- Complete the definition of split_combine_statement below with a
- property that states that split is the inverse of
- combine. Then, prove that the property holds. (Be sure to leave
- your induction hypothesis general by not doing intros on more
- things than necessary. Hint: what property do you need of l1
- and l2 for splitcombinel1l2=(l1,l2) to be true?)
-
-
-
-
-Definitionsplit_combine_statement : Prop :=
-(* FILL IN HERE *)admit.
-
-
-Theoremsplit_combine : split_combine_statement.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-
-
-
-☐
-
-
-
Exercise: 3 stars (override_permute)
-
-
-
-Theoremoverride_permute : ∀(X:Type) x1x2k1k2k3 (f : nat→X),
- beq_natk2k1 = false→
- (override (overridefk2x2) k1x1) k3 = (override (overridefk1x1) k2x2) k3.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (filter_exercise)
- This one is a bit challenging. Pay attention to the form of your IH.
-
-
-
-
-Theoremfilter_exercise : ∀(X : Type) (test : X→bool)
- (x : X) (llf : listX),
- filtertestl = x :: lf→
- testx = true.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
\ No newline at end of file
diff --git a/MoreCoq.v b/MoreCoq.v
deleted file mode 100644
index 3aaf8de..0000000
--- a/MoreCoq.v
+++ /dev/null
@@ -1,1070 +0,0 @@
-(** * MoreCoq: More About Coq *)
-
-Require Export Poly.
-
-(** This chapter introduces several more Coq tactics that,
- together, allow us to prove many more theorems about the
- functional programs we are writing. *)
-
-(* ###################################################### *)
-(** * The [apply] Tactic *)
-
-(** We often encounter situations where the goal to be proved is
- exactly the same as some hypothesis in the context or some
- previously proved lemma. *)
-
-Theorem silly1 : forall (n m o p : nat),
- n = m ->
- [n;o] = [n;p] ->
- [n;o] = [m;p].
-Proof.
- intros n m o p eq1 eq2.
- rewrite <- eq1.
- (* At this point, we could finish with
- "[rewrite -> eq2. reflexivity.]" as we have
- done several times above. But we can achieve the
- same effect in a single step by using the
- [apply] tactic instead: *)
- apply eq2. Qed.
-
-(** The [apply] tactic also works with _conditional_ hypotheses
- and lemmas: if the statement being applied is an implication, then
- the premises of this implication will be added to the list of
- subgoals needing to be proved. *)
-
-Theorem silly2 : forall (n m o p : nat),
- n = m ->
- (forall (q r : nat), q = r -> [q;o] = [r;p]) ->
- [n;o] = [m;p].
-Proof.
- intros n m o p eq1 eq2.
- apply eq2. apply eq1. Qed.
-
-(** You may find it instructive to experiment with this proof
- and see if there is a way to complete it using just [rewrite]
- instead of [apply]. *)
-
-(** Typically, when we use [apply H], the statement [H] will
- begin with a [forall] binding some _universal variables_. When
- Coq matches the current goal against the conclusion of [H], it
- will try to find appropriate values for these variables. For
- example, when we do [apply eq2] in the following proof, the
- universal variable [q] in [eq2] gets instantiated with [n] and [r]
- gets instantiated with [m]. *)
-
-Theorem silly2a : forall (n m : nat),
- (n,n) = (m,m) ->
- (forall (q r : nat), (q,q) = (r,r) -> [q] = [r]) ->
- [n] = [m].
-Proof.
- intros n m eq1 eq2.
- apply eq2. apply eq1. Qed.
-
-(** **** Exercise: 2 stars, optional (silly_ex) *)
-(** Complete the following proof without using [simpl]. *)
-
-Theorem silly_ex :
- (forall n, evenb n = true -> oddb (S n) = true) ->
- evenb 3 = true ->
- oddb 4 = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** To use the [apply] tactic, the (conclusion of the) fact
- being applied must match the goal _exactly_ -- for example, [apply]
- will not work if the left and right sides of the equality are
- swapped. *)
-
-Theorem silly3_firsttry : forall (n : nat),
- true = beq_nat n 5 ->
- beq_nat (S (S n)) 7 = true.
-Proof.
- intros n H.
- simpl.
- (* Here we cannot use [apply] directly *)
-Abort.
-
-(** In this case we can use the [symmetry] tactic, which switches the
- left and right sides of an equality in the goal. *)
-
-Theorem silly3 : forall (n : nat),
- true = beq_nat n 5 ->
- beq_nat (S (S n)) 7 = true.
-Proof.
- intros n H.
- symmetry.
- simpl. (* Actually, this [simpl] is unnecessary, since
- [apply] will perform simplification first. *)
- apply H. Qed.
-
-(** **** Exercise: 3 stars (apply_exercise1) *)
-(** Hint: you can use [apply] with previously defined lemmas, not
- just hypotheses in the context. Remember that [SearchAbout] is
- your friend. *)
-
-Theorem rev_exercise1 : forall (l l' : list nat),
- l = rev l' ->
- l' = rev l.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (apply_rewrite) *)
-(** Briefly explain the difference between the tactics [apply] and
- [rewrite]. Are there situations where both can usefully be
- applied?
- (* FILL IN HERE *)
-*)
-(** [] *)
-
-
-(* ###################################################### *)
-(** * The [apply ... with ...] Tactic *)
-
-(** The following silly example uses two rewrites in a row to
- get from [[a,b]] to [[e,f]]. *)
-
-Example trans_eq_example : forall (a b c d e f : nat),
- [a;b] = [c;d] ->
- [c;d] = [e;f] ->
- [a;b] = [e;f].
-Proof.
- intros a b c d e f eq1 eq2.
- rewrite -> eq1. rewrite -> eq2. reflexivity. Qed.
-
-(** Since this is a common pattern, we might
- abstract it out as a lemma recording once and for all
- the fact that equality is transitive. *)
-
-Theorem trans_eq : forall (X:Type) (n m o : X),
- n = m -> m = o -> n = o.
-Proof.
- intros X n m o eq1 eq2. rewrite -> eq1. rewrite -> eq2.
- reflexivity. Qed.
-
-(** Now, we should be able to use [trans_eq] to
- prove the above example. However, to do this we need
- a slight refinement of the [apply] tactic. *)
-
-Example trans_eq_example' : forall (a b c d e f : nat),
- [a;b] = [c;d] ->
- [c;d] = [e;f] ->
- [a;b] = [e;f].
-Proof.
- intros a b c d e f eq1 eq2.
- (* If we simply tell Coq [apply trans_eq] at this point,
- it can tell (by matching the goal against the
- conclusion of the lemma) that it should instantiate [X]
- with [[nat]], [n] with [[a,b]], and [o] with [[e,f]].
- However, the matching process doesn't determine an
- instantiation for [m]: we have to supply one explicitly
- by adding [with (m:=[c,d])] to the invocation of
- [apply]. *)
- apply trans_eq with (m:=[c;d]). apply eq1. apply eq2. Qed.
-
-(** Actually, we usually don't have to include the name [m]
- in the [with] clause; Coq is often smart enough to
- figure out which instantiation we're giving. We could
- instead write: [apply trans_eq with [c,d]]. *)
-
-(** **** Exercise: 3 stars, optional (apply_with_exercise) *)
-Example trans_eq_exercise : forall (n m o p : nat),
- m = (minustwo o) ->
- (n + p) = m ->
- (n + p) = (minustwo o).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ###################################################### *)
-(** * The [inversion] tactic *)
-
-(** Recall the definition of natural numbers:
- Inductive nat : Type :=
- | O : nat
- | S : nat -> nat.
- It is clear from this definition that every number has one of two
- forms: either it is the constructor [O] or it is built by applying
- the constructor [S] to another number. But there is more here than
- meets the eye: implicit in the definition (and in our informal
- understanding of how datatype declarations work in other
- programming languages) are two other facts:
-
- - The constructor [S] is _injective_. That is, the only way we can
- have [S n = S m] is if [n = m].
-
- - The constructors [O] and [S] are _disjoint_. That is, [O] is not
- equal to [S n] for any [n]. *)
-
-(** Similar principles apply to all inductively defined types: all
- constructors are injective, and the values built from distinct
- constructors are never equal. For lists, the [cons] constructor is
- injective and [nil] is different from every non-empty list. For
- booleans, [true] and [false] are unequal. (Since neither [true]
- nor [false] take any arguments, their injectivity is not an issue.) *)
-
-(** Coq provides a tactic called [inversion] that allows us to exploit
- these principles in proofs.
-
- The [inversion] tactic is used like this. Suppose [H] is a
- hypothesis in the context (or a previously proven lemma) of the
- form
- c a1 a2 ... an = d b1 b2 ... bm
- for some constructors [c] and [d] and arguments [a1 ... an] and
- [b1 ... bm]. Then [inversion H] instructs Coq to "invert" this
- equality to extract the information it contains about these terms:
-
- - If [c] and [d] are the same constructor, then we know, by the
- injectivity of this constructor, that [a1 = b1], [a2 = b2],
- etc.; [inversion H] adds these facts to the context, and tries
- to use them to rewrite the goal.
-
- - If [c] and [d] are different constructors, then the hypothesis
- [H] is contradictory. That is, a false assumption has crept
- into the context, and this means that any goal whatsoever is
- provable! In this case, [inversion H] marks the current goal as
- completed and pops it off the goal stack. *)
-
-(** The [inversion] tactic is probably easier to understand by
- seeing it in action than from general descriptions like the above.
- Below you will find example theorems that demonstrate the use of
- [inversion] and exercises to test your understanding. *)
-
-Theorem eq_add_S : forall (n m : nat),
- S n = S m ->
- n = m.
-Proof.
- intros n m eq. inversion eq. reflexivity. Qed.
-
-Theorem silly4 : forall (n m : nat),
- [n] = [m] ->
- n = m.
-Proof.
- intros n o eq. inversion eq. reflexivity. Qed.
-
-(** As a convenience, the [inversion] tactic can also
- destruct equalities between complex values, binding
- multiple variables as it goes. *)
-
-Theorem silly5 : forall (n m o : nat),
- [n;m] = [o;o] ->
- [n] = [m].
-Proof.
- intros n m o eq. inversion eq. reflexivity. Qed.
-
-(** **** Exercise: 1 star (sillyex1) *)
-Example sillyex1 : forall (X : Type) (x y z : X) (l j : list X),
- x :: y :: l = z :: j ->
- y :: l = x :: j ->
- x = y.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-Theorem silly6 : forall (n : nat),
- S n = O ->
- 2 + 2 = 5.
-Proof.
- intros n contra. inversion contra. Qed.
-
-Theorem silly7 : forall (n m : nat),
- false = true ->
- [n] = [m].
-Proof.
- intros n m contra. inversion contra. Qed.
-
-(** **** Exercise: 1 star (sillyex2) *)
-Example sillyex2 : forall (X : Type) (x y z : X) (l j : list X),
- x :: y :: l = [] ->
- y :: l = z :: j ->
- x = z.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** While the injectivity of constructors allows us to reason
- [forall (n m : nat), S n = S m -> n = m], the reverse direction of
- the implication is an instance of a more general fact about
- constructors and functions, which we will often find useful: *)
-
-Theorem f_equal : forall (A B : Type) (f: A -> B) (x y: A),
- x = y -> f x = f y.
-Proof. intros A B f x y eq. rewrite eq. reflexivity. Qed.
-
-
-
-
-(** **** Exercise: 2 stars, optional (practice) *)
-(** A couple more nontrivial but not-too-complicated proofs to work
- together in class, or for you to work as exercises. *)
-
-
-Theorem beq_nat_0_l : forall n,
- beq_nat 0 n = true -> n = 0.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem beq_nat_0_r : forall n,
- beq_nat n 0 = true -> n = 0.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ###################################################### *)
-(** * Using Tactics on Hypotheses *)
-
-(** By default, most tactics work on the goal formula and leave
- the context unchanged. However, most tactics also have a variant
- that performs a similar operation on a statement in the context.
-
- For example, the tactic [simpl in H] performs simplification in
- the hypothesis named [H] in the context. *)
-
-Theorem S_inj : forall (n m : nat) (b : bool),
- beq_nat (S n) (S m) = b ->
- beq_nat n m = b.
-Proof.
- intros n m b H. simpl in H. apply H. Qed.
-
-(** Similarly, the tactic [apply L in H] matches some
- conditional statement [L] (of the form [L1 -> L2], say) against a
- hypothesis [H] in the context. However, unlike ordinary
- [apply] (which rewrites a goal matching [L2] into a subgoal [L1]),
- [apply L in H] matches [H] against [L1] and, if successful,
- replaces it with [L2].
-
- In other words, [apply L in H] gives us a form of "forward
- reasoning" -- from [L1 -> L2] and a hypothesis matching [L1], it
- gives us a hypothesis matching [L2]. By contrast, [apply L] is
- "backward reasoning" -- it says that if we know [L1->L2] and we
- are trying to prove [L2], it suffices to prove [L1].
-
- Here is a variant of a proof from above, using forward reasoning
- throughout instead of backward reasoning. *)
-
-Theorem silly3' : forall (n : nat),
- (beq_nat n 5 = true -> beq_nat (S (S n)) 7 = true) ->
- true = beq_nat n 5 ->
- true = beq_nat (S (S n)) 7.
-Proof.
- intros n eq H.
- symmetry in H. apply eq in H. symmetry in H.
- apply H. Qed.
-
-(** Forward reasoning starts from what is _given_ (premises,
- previously proven theorems) and iteratively draws conclusions from
- them until the goal is reached. Backward reasoning starts from
- the _goal_, and iteratively reasons about what would imply the
- goal, until premises or previously proven theorems are reached.
- If you've seen informal proofs before (for example, in a math or
- computer science class), they probably used forward reasoning. In
- general, Coq tends to favor backward reasoning, but in some
- situations the forward style can be easier to use or to think
- about. *)
-
-(** **** Exercise: 3 stars (plus_n_n_injective) *)
-(** Practice using "in" variants in this exercise. *)
-
-Theorem plus_n_n_injective : forall n m,
- n + n = m + m ->
- n = m.
-Proof.
- intros n. induction n as [| n'].
- (* Hint: use the plus_n_Sm lemma *)
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** * Varying the Induction Hypothesis *)
-
-(** Sometimes it is important to control the exact form of the
- induction hypothesis when carrying out inductive proofs in Coq.
- In particular, we need to be careful about which of the
- assumptions we move (using [intros]) from the goal to the context
- before invoking the [induction] tactic. For example, suppose
- we want to show that the [double] function is injective -- i.e.,
- that it always maps different arguments to different results:
- Theorem double_injective: forall n m, double n = double m -> n = m.
- The way we _start_ this proof is a little bit delicate: if we
- begin it with
- intros n. induction n.
-]]
- all is well. But if we begin it with
- intros n m. induction n.
- we get stuck in the middle of the inductive case... *)
-
-Theorem double_injective_FAILED : forall n m,
- double n = double m ->
- n = m.
-Proof.
- intros n m. induction n as [| n'].
- Case "n = O". simpl. intros eq. destruct m as [| m'].
- SCase "m = O". reflexivity.
- SCase "m = S m'". inversion eq.
- Case "n = S n'". intros eq. destruct m as [| m'].
- SCase "m = O". inversion eq.
- SCase "m = S m'". apply f_equal.
- (* Here we are stuck. The induction hypothesis, [IHn'], does
- not give us [n' = m'] -- there is an extra [S] in the
- way -- so the goal is not provable. *)
- Abort.
-
-(** What went wrong? *)
-
-(** The problem is that, at the point we invoke the induction
- hypothesis, we have already introduced [m] into the context --
- intuitively, we have told Coq, "Let's consider some particular
- [n] and [m]..." and we now have to prove that, if [double n =
- double m] for _this particular_ [n] and [m], then [n = m].
-
- The next tactic, [induction n] says to Coq: We are going to show
- the goal by induction on [n]. That is, we are going to prove that
- the proposition
-
- - [P n] = "if [double n = double m], then [n = m]"
-
- holds for all [n] by showing
-
- - [P O]
-
- (i.e., "if [double O = double m] then [O = m]")
-
- - [P n -> P (S n)]
-
- (i.e., "if [double n = double m] then [n = m]" implies "if
- [double (S n) = double m] then [S n = m]").
-
- If we look closely at the second statement, it is saying something
- rather strange: it says that, for a _particular_ [m], if we know
-
- - "if [double n = double m] then [n = m]"
-
- then we can prove
-
- - "if [double (S n) = double m] then [S n = m]".
-
- To see why this is strange, let's think of a particular [m] --
- say, [5]. The statement is then saying that, if we know
-
- - [Q] = "if [double n = 10] then [n = 5]"
-
- then we can prove
-
- - [R] = "if [double (S n) = 10] then [S n = 5]".
-
- But knowing [Q] doesn't give us any help with proving [R]! (If we
- tried to prove [R] from [Q], we would say something like "Suppose
- [double (S n) = 10]..." but then we'd be stuck: knowing that
- [double (S n)] is [10] tells us nothing about whether [double n]
- is [10], so [Q] is useless at this point.) *)
-
-(** To summarize: Trying to carry out this proof by induction on [n]
- when [m] is already in the context doesn't work because we are
- trying to prove a relation involving _every_ [n] but just a
- _single_ [m]. *)
-
-(** The good proof of [double_injective] leaves [m] in the goal
- statement at the point where the [induction] tactic is invoked on
- [n]: *)
-
-Theorem double_injective : forall n m,
- double n = double m ->
- n = m.
-Proof.
- intros n. induction n as [| n'].
- Case "n = O". simpl. intros m eq. destruct m as [| m'].
- SCase "m = O". reflexivity.
- SCase "m = S m'". inversion eq.
- Case "n = S n'".
- (* Notice that both the goal and the induction
- hypothesis have changed: the goal asks us to prove
- something more general (i.e., to prove the
- statement for _every_ [m]), but the IH is
- correspondingly more flexible, allowing us to
- choose any [m] we like when we apply the IH. *)
- intros m eq.
- (* Now we choose a particular [m] and introduce the
- assumption that [double n = double m]. Since we
- are doing a case analysis on [n], we need a case
- analysis on [m] to keep the two "in sync." *)
- destruct m as [| m'].
- SCase "m = O".
- (* The 0 case is trivial *)
- inversion eq.
- SCase "m = S m'".
- apply f_equal.
- (* At this point, since we are in the second
- branch of the [destruct m], the [m'] mentioned
- in the context at this point is actually the
- predecessor of the one we started out talking
- about. Since we are also in the [S] branch of
- the induction, this is perfect: if we
- instantiate the generic [m] in the IH with the
- [m'] that we are talking about right now (this
- instantiation is performed automatically by
- [apply]), then [IHn'] gives us exactly what we
- need to finish the proof. *)
- apply IHn'. inversion eq. reflexivity. Qed.
-
-(** What this teaches us is that we need to be careful about using
- induction to try to prove something too specific: If we're proving
- a property of [n] and [m] by induction on [n], we may need to
- leave [m] generic. *)
-
-(** The proof of this theorem (left as an exercise) has to be treated similarly: *)
-
-(** **** Exercise: 2 stars (beq_nat_true) *)
-Theorem beq_nat_true : forall n m,
- beq_nat n m = true -> n = m.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, advanced (beq_nat_true_informal) *)
-(** Give a careful informal proof of [beq_nat_true], being as explicit
- as possible about quantifiers. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-(** The strategy of doing fewer [intros] before an [induction] doesn't
- always work directly; sometimes a little _rearrangement_ of
- quantified variables is needed. Suppose, for example, that we
- wanted to prove [double_injective] by induction on [m] instead of
- [n]. *)
-
-Theorem double_injective_take2_FAILED : forall n m,
- double n = double m ->
- n = m.
-Proof.
- intros n m. induction m as [| m'].
- Case "m = O". simpl. intros eq. destruct n as [| n'].
- SCase "n = O". reflexivity.
- SCase "n = S n'". inversion eq.
- Case "m = S m'". intros eq. destruct n as [| n'].
- SCase "n = O". inversion eq.
- SCase "n = S n'". apply f_equal.
- (* Stuck again here, just like before. *)
-Abort.
-
-(** The problem is that, to do induction on [m], we must first
- introduce [n]. (If we simply say [induction m] without
- introducing anything first, Coq will automatically introduce
- [n] for us!) *)
-
-(** What can we do about this? One possibility is to rewrite the
- statement of the lemma so that [m] is quantified before [n]. This
- will work, but it's not nice: We don't want to have to mangle the
- statements of lemmas to fit the needs of a particular strategy for
- proving them -- we want to state them in the most clear and
- natural way. *)
-
-(** What we can do instead is to first introduce all the
- quantified variables and then _re-generalize_ one or more of
- them, taking them out of the context and putting them back at
- the beginning of the goal. The [generalize dependent] tactic
- does this. *)
-
-Theorem double_injective_take2 : forall n m,
- double n = double m ->
- n = m.
-Proof.
- intros n m.
- (* [n] and [m] are both in the context *)
- generalize dependent n.
- (* Now [n] is back in the goal and we can do induction on
- [m] and get a sufficiently general IH. *)
- induction m as [| m'].
- Case "m = O". simpl. intros n eq. destruct n as [| n'].
- SCase "n = O". reflexivity.
- SCase "n = S n'". inversion eq.
- Case "m = S m'". intros n eq. destruct n as [| n'].
- SCase "n = O". inversion eq.
- SCase "n = S n'". apply f_equal.
- apply IHm'. inversion eq. reflexivity. Qed.
-
-(** Let's look at an informal proof of this theorem. Note that
- the proposition we prove by induction leaves [n] quantified,
- corresponding to the use of generalize dependent in our formal
- proof.
-
-_Theorem_: For any nats [n] and [m], if [double n = double m], then
- [n = m].
-
-_Proof_: Let [m] be a [nat]. We prove by induction on [m] that, for
- any [n], if [double n = double m] then [n = m].
-
- - First, suppose [m = 0], and suppose [n] is a number such
- that [double n = double m]. We must show that [n = 0].
-
- Since [m = 0], by the definition of [double] we have [double n =
- 0]. There are two cases to consider for [n]. If [n = 0] we are
- done, since this is what we wanted to show. Otherwise, if [n = S
- n'] for some [n'], we derive a contradiction: by the definition of
- [double] we would have [double n = S (S (double n'))], but this
- contradicts the assumption that [double n = 0].
-
- - Otherwise, suppose [m = S m'] and that [n] is again a number such
- that [double n = double m]. We must show that [n = S m'], with
- the induction hypothesis that for every number [s], if [double s =
- double m'] then [s = m'].
-
- By the fact that [m = S m'] and the definition of [double], we
- have [double n = S (S (double m'))]. There are two cases to
- consider for [n].
-
- If [n = 0], then by definition [double n = 0], a contradiction.
- Thus, we may assume that [n = S n'] for some [n'], and again by
- the definition of [double] we have [S (S (double n')) = S (S
- (double m'))], which implies by inversion that [double n' = double
- m'].
-
- Instantiating the induction hypothesis with [n'] thus allows us to
- conclude that [n' = m'], and it follows immediately that [S n' = S
- m']. Since [S n' = n] and [S m' = m], this is just what we wanted
- to show. [] *)
-
-
-
-(** Here's another illustration of [inversion] and using an
- appropriately general induction hypothesis. This is a slightly
- roundabout way of stating a fact that we have already proved
- above. The extra equalities force us to do a little more
- equational reasoning and exercise some of the tactics we've seen
- recently. *)
-
-Theorem length_snoc' : forall (X : Type) (v : X)
- (l : list X) (n : nat),
- length l = n ->
- length (snoc l v) = S n.
-Proof.
- intros X v l. induction l as [| v' l'].
-
- Case "l = []".
- intros n eq. rewrite <- eq. reflexivity.
-
- Case "l = v' :: l'".
- intros n eq. simpl. destruct n as [| n'].
- SCase "n = 0". inversion eq.
- SCase "n = S n'".
- apply f_equal. apply IHl'. inversion eq. reflexivity. Qed.
-
-(** It might be tempting to start proving the above theorem
- by introducing [n] and [eq] at the outset. However, this leads
- to an induction hypothesis that is not strong enough. Compare
- the above to the following (aborted) attempt: *)
-
-Theorem length_snoc_bad : forall (X : Type) (v : X)
- (l : list X) (n : nat),
- length l = n ->
- length (snoc l v) = S n.
-Proof.
- intros X v l n eq. induction l as [| v' l'].
-
- Case "l = []".
- rewrite <- eq. reflexivity.
-
- Case "l = v' :: l'".
- simpl. destruct n as [| n'].
- SCase "n = 0". inversion eq.
- SCase "n = S n'".
- apply f_equal. Abort. (* apply IHl'. *) (* The IH doesn't apply! *)
-
-
-(** As in the double examples, the problem is that by
- introducing [n] before doing induction on [l], the induction
- hypothesis is specialized to one particular natural number, namely
- [n]. In the induction case, however, we need to be able to use
- the induction hypothesis on some other natural number [n'].
- Retaining the more general form of the induction hypothesis thus
- gives us more flexibility.
-
- In general, a good rule of thumb is to make the induction hypothesis
- as general as possible. *)
-
-(** **** Exercise: 3 stars (gen_dep_practice) *)
-
-(** Prove this by induction on [l]. *)
-
-Theorem index_after_last: forall (n : nat) (X : Type) (l : list X),
- length l = n ->
- index n l = None.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced, optional (index_after_last_informal) *)
-(** Write an informal proof corresponding to your Coq proof
- of [index_after_last]:
-
- _Theorem_: For all sets [X], lists [l : list X], and numbers
- [n], if [length l = n] then [index n l = None].
-
- _Proof_:
- (* FILL IN HERE *)
-[]
-*)
-
-(** **** Exercise: 3 stars, optional (gen_dep_practice_more) *)
-(** Prove this by induction on [l]. *)
-
-Theorem length_snoc''' : forall (n : nat) (X : Type)
- (v : X) (l : list X),
- length l = n ->
- length (snoc l v) = S n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (app_length_cons) *)
-(** Prove this by induction on [l1], without using [app_length]. *)
-
-Theorem app_length_cons : forall (X : Type) (l1 l2 : list X)
- (x : X) (n : nat),
- length (l1 ++ (x :: l2)) = n ->
- S (length (l1 ++ l2)) = n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 4 stars, optional (app_length_twice) *)
-(** Prove this by induction on [l], without using app_length. *)
-
-Theorem app_length_twice : forall (X:Type) (n:nat) (l:list X),
- length l = n ->
- length (l ++ l) = n + n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(** **** Exercise: 3 stars, optional (double_induction) *)
-(** Prove the following principle of induction over two naturals. *)
-
-Theorem double_induction: forall (P : nat -> nat -> Prop),
- P 0 0 ->
- (forall m, P m 0 -> P (S m) 0) ->
- (forall n, P 0 n -> P 0 (S n)) ->
- (forall m n, P m n -> P (S m) (S n)) ->
- forall m n, P m n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ###################################################### *)
-(** * Using [destruct] on Compound Expressions *)
-
-(** We have seen many examples where the [destruct] tactic is
- used to perform case analysis of the value of some variable. But
- sometimes we need to reason by cases on the result of some
- _expression_. We can also do this with [destruct].
-
- Here are some examples: *)
-
-Definition sillyfun (n : nat) : bool :=
- if beq_nat n 3 then false
- else if beq_nat n 5 then false
- else false.
-
-Theorem sillyfun_false : forall (n : nat),
- sillyfun n = false.
-Proof.
- intros n. unfold sillyfun.
- destruct (beq_nat n 3).
- Case "beq_nat n 3 = true". reflexivity.
- Case "beq_nat n 3 = false". destruct (beq_nat n 5).
- SCase "beq_nat n 5 = true". reflexivity.
- SCase "beq_nat n 5 = false". reflexivity. Qed.
-
-(** After unfolding [sillyfun] in the above proof, we find that
- we are stuck on [if (beq_nat n 3) then ... else ...]. Well,
- either [n] is equal to [3] or it isn't, so we use [destruct
- (beq_nat n 3)] to let us reason about the two cases.
-
- In general, the [destruct] tactic can be used to perform case
- analysis of the results of arbitrary computations. If [e] is an
- expression whose type is some inductively defined type [T], then,
- for each constructor [c] of [T], [destruct e] generates a subgoal
- in which all occurrences of [e] (in the goal and in the context)
- are replaced by [c].
-
-*)
-
-(** **** Exercise: 1 star (override_shadow) *)
-Theorem override_shadow : forall (X:Type) x1 x2 k1 k2 (f : nat->X),
- (override (override f k1 x2) k1 x1) k2 = (override f k1 x1) k2.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (combine_split) *)
-(** Complete the proof below *)
-
-Theorem combine_split : forall X Y (l : list (X * Y)) l1 l2,
- split l = (l1, l2) ->
- combine l1 l2 = l.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Sometimes, doing a [destruct] on a compound expression (a
- non-variable) will erase information we need to complete a proof. *)
-(** For example, suppose
- we define a function [sillyfun1] like this: *)
-
-Definition sillyfun1 (n : nat) : bool :=
- if beq_nat n 3 then true
- else if beq_nat n 5 then true
- else false.
-
-(** And suppose that we want to convince Coq of the rather
- obvious observation that [sillyfun1 n] yields [true] only when [n]
- is odd. By analogy with the proofs we did with [sillyfun] above,
- it is natural to start the proof like this: *)
-
-Theorem sillyfun1_odd_FAILED : forall (n : nat),
- sillyfun1 n = true ->
- oddb n = true.
-Proof.
- intros n eq. unfold sillyfun1 in eq.
- destruct (beq_nat n 3).
- (* stuck... *)
-Abort.
-
-(** We get stuck at this point because the context does not
- contain enough information to prove the goal! The problem is that
- the substitution peformed by [destruct] is too brutal -- it threw
- away every occurrence of [beq_nat n 3], but we need to keep some
- memory of this expression and how it was destructed, because we
- need to be able to reason that since, in this branch of the case
- analysis, [beq_nat n 3 = true], it must be that [n = 3], from
- which it follows that [n] is odd.
-
- What we would really like is to substitute away all existing
- occurences of [beq_nat n 3], but at the same time add an equation
- to the context that records which case we are in. The [eqn:]
- qualifier allows us to introduce such an equation (with whatever
- name we choose). *)
-
-Theorem sillyfun1_odd : forall (n : nat),
- sillyfun1 n = true ->
- oddb n = true.
-Proof.
- intros n eq. unfold sillyfun1 in eq.
- destruct (beq_nat n 3) eqn:Heqe3.
- (* Now we have the same state as at the point where we got stuck
- above, except that the context contains an extra equality
- assumption, which is exactly what we need to make progress. *)
- Case "e3 = true". apply beq_nat_true in Heqe3.
- rewrite -> Heqe3. reflexivity.
- Case "e3 = false".
- (* When we come to the second equality test in the body of the
- function we are reasoning about, we can use [eqn:] again in the
- same way, allow us to finish the proof. *)
- destruct (beq_nat n 5) eqn:Heqe5.
- SCase "e5 = true".
- apply beq_nat_true in Heqe5.
- rewrite -> Heqe5. reflexivity.
- SCase "e5 = false". inversion eq. Qed.
-
-
-(** **** Exercise: 2 stars (destruct_eqn_practice) *)
-Theorem bool_fn_applied_thrice :
- forall (f : bool -> bool) (b : bool),
- f (f (f b)) = f b.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (override_same) *)
-Theorem override_same : forall (X:Type) x1 k1 k2 (f : nat->X),
- f k1 = x1 ->
- (override f k1 x1) k2 = f k2.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ################################################################## *)
-(** * Review *)
-
-(** We've now seen a bunch of Coq's fundamental tactics. We'll
- introduce a few more as we go along through the coming lectures,
- and later in the course we'll introduce some more powerful
- _automation_ tactics that make Coq do more of the low-level work
- in many cases. But basically we've got what we need to get work
- done.
-
- Here are the ones we've seen:
-
- - [intros]:
- move hypotheses/variables from goal to context
-
- - [reflexivity]:
- finish the proof (when the goal looks like [e = e])
-
- - [apply]:
- prove goal using a hypothesis, lemma, or constructor
-
- - [apply... in H]:
- apply a hypothesis, lemma, or constructor to a hypothesis in
- the context (forward reasoning)
-
- - [apply... with...]:
- explicitly specify values for variables that cannot be
- determined by pattern matching
-
- - [simpl]:
- simplify computations in the goal
-
- - [simpl in H]:
- ... or a hypothesis
-
- - [rewrite]:
- use an equality hypothesis (or lemma) to rewrite the goal
-
- - [rewrite ... in H]:
- ... or a hypothesis
-
- - [symmetry]:
- changes a goal of the form [t=u] into [u=t]
-
- - [symmetry in H]:
- changes a hypothesis of the form [t=u] into [u=t]
-
- - [unfold]:
- replace a defined constant by its right-hand side in the goal
-
- - [unfold... in H]:
- ... or a hypothesis
-
- - [destruct... as...]:
- case analysis on values of inductively defined types
-
- - [destruct... eqn:...]:
- specify the name of an equation to be added to the context,
- recording the result of the case analysis
-
- - [induction... as...]:
- induction on values of inductively defined types
-
- - [inversion]:
- reason by injectivity and distinctness of constructors
-
- - [assert (e) as H]:
- introduce a "local lemma" [e] and call it [H]
-
- - [generalize dependent x]:
- move the variable [x] (and anything else that depends on it)
- from the context back to an explicit hypothesis in the goal
- formula
-*)
-
-(* ###################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 3 stars (beq_nat_sym) *)
-Theorem beq_nat_sym : forall (n m : nat),
- beq_nat n m = beq_nat m n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced, optional (beq_nat_sym_informal) *)
-(** Give an informal proof of this lemma that corresponds to your
- formal proof above:
-
- Theorem: For any [nat]s [n] [m], [beq_nat n m = beq_nat m n].
-
- Proof:
- (* FILL IN HERE *)
-[]
- *)
-
-(** **** Exercise: 3 stars, optional (beq_nat_trans) *)
-Theorem beq_nat_trans : forall n m p,
- beq_nat n m = true ->
- beq_nat m p = true ->
- beq_nat n p = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (split_combine) *)
-(** We have just proven that for all lists of pairs, [combine] is the
- inverse of [split]. How would you formalize the statement that
- [split] is the inverse of [combine]?
-
- Complete the definition of [split_combine_statement] below with a
- property that states that [split] is the inverse of
- [combine]. Then, prove that the property holds. (Be sure to leave
- your induction hypothesis general by not doing [intros] on more
- things than necessary. Hint: what property do you need of [l1]
- and [l2] for [split] [combine l1 l2 = (l1,l2)] to be true?) *)
-
-Definition split_combine_statement : Prop :=
-(* FILL IN HERE *) admit.
-
-Theorem split_combine : split_combine_statement.
-Proof.
-(* FILL IN HERE *) Admitted.
-
-
-(** [] *)
-
-(** **** Exercise: 3 stars (override_permute) *)
-Theorem override_permute : forall (X:Type) x1 x2 k1 k2 k3 (f : nat->X),
- beq_nat k2 k1 = false ->
- (override (override f k2 x2) k1 x1) k3 = (override (override f k1 x1) k2 x2) k3.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (filter_exercise) *)
-(** This one is a bit challenging. Pay attention to the form of your IH. *)
-
-Theorem filter_exercise : forall (X : Type) (test : X -> bool)
- (x : X) (l lf : list X),
- filter test l = x :: lf ->
- test x = true.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (forall_exists_challenge) *)
-(** Define two recursive [Fixpoints], [forallb] and [existsb]. The
- first checks whether every element in a list satisfies a given
- predicate:
- forallb oddb [1;3;5;7;9] = true
-
- forallb negb [false;false] = true
-
- forallb evenb [0;2;4;5] = false
-
- forallb (beq_nat 5) [] = true
- The second checks whether there exists an element in the list that
- satisfies a given predicate:
- existsb (beq_nat 5) [0;2;3;6] = false
-
- existsb (andb true) [true;true;false] = true
-
- existsb oddb [1;0;0;0;0;3] = true
-
- existsb evenb [] = false
- Next, define a _nonrecursive_ version of [existsb] -- call it
- [existsb'] -- using [forallb] and [negb].
-
- Prove that [existsb'] and [existsb] have the same behavior.
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* $Date: 2014-02-04 07:15:43 -0500 (Tue, 04 Feb 2014) $ *)
-
-
-
diff --git a/MoreInd.html b/MoreInd.html
deleted file mode 100644
index 7c39080..0000000
--- a/MoreInd.html
+++ /dev/null
@@ -1,1978 +0,0 @@
-
-
-
-
-
-MoreInd: More on Induction
-
-
-
-
-
-
-
-
-
-
-
-
-
-
MoreIndMore on Induction
-
-
-
-
-
-
-
-
-
-
-RequireExport "ProofObjects".
-
-
-
-
-
-
Induction Principles
-
-
-
- This is a good point to pause and take a deeper look at induction
- principles.
-
-
-
- Every time we declare a new Inductive datatype, Coq
- automatically generates and proves an induction principle
- for this type.
-
-
-
- The induction principle for a type t is called t_ind. Here is
- the one for natural numbers:
-
-
-
-
-Checknat_ind.
-(* ===> nat_ind :
- forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, P n -> P (S n)) ->
- forall n : nat, P n *)
-
-
-
-
-
-
- The induction tactic is a straightforward wrapper that, at
- its core, simply performs applyt_ind. To see this more
- clearly, let's experiment a little with using applynat_ind
- directly, instead of the induction tactic, to carry out some
- proofs. Here, for example, is an alternate proof of a theorem
- that we saw in the Basics chapter.
-
-
-
-
-Theoremmult_0_r' : ∀n:nat,
- n × 0 = 0.
-Proof.
- applynat_ind.
- Case "O". reflexivity.
- Case "S". simpl. introsnIHn. rewrite→IHn.
- reflexivity. Qed.
-
-
-
-
-
-This proof is basically the same as the earlier one, but a
- few minor differences are worth noting. First, in the induction
- step of the proof (the "S" case), we have to do a little
- bookkeeping manually (the intros) that induction does
- automatically.
-
-
-
- Second, we do not introduce n into the context before applying
- nat_ind — the conclusion of nat_ind is a quantified formula,
- and apply needs this conclusion to exactly match the shape of
- the goal state, including the quantifier. The induction tactic
- works either with a variable in the context or a quantified
- variable in the goal.
-
-
-
- Third, the apply tactic automatically chooses variable names for
- us (in the second subgoal, here), whereas induction lets us
- specify (with the as... clause) what names should be used. The
- automatic choice is actually a little unfortunate, since it
- re-uses the name n for a variable that is different from the n
- in the original theorem. This is why the Case annotation is
- just S — if we tried to write it out in the more explicit form
- that we've been using for most proofs, we'd have to write n=S
- n, which doesn't make a lot of sense! All of these conveniences
- make induction nicer to use in practice than applying induction
- principles like nat_ind directly. But it is important to
- realize that, modulo this little bit of bookkeeping, applying
- nat_ind is what we are really doing.
-
-
-
Exercise: 2 stars, optional (plus_one_r')
- Complete this proof as we did mult_0_r' above, without using
- the induction tactic.
-
-
-
-
-Theoremplus_one_r' : ∀n:nat,
- n + 1 = Sn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- Coq generates induction principles for every datatype defined with
- Inductive, including those that aren't recursive. (Although
- we don't need induction to prove properties of non-recursive
- datatypes, the idea of an induction principle still makes sense
- for them: it gives a way to prove that a property holds for all
- values of the type.)
-
-
-
- These generated principles follow a similar pattern. If we define a
- type t with constructors c1 ... cn, Coq generates a theorem
- with this shape:
-
-
- The specific shape of each case depends on the arguments to the
- corresponding constructor. Before trying to write down a general
- rule, let's look at some more examples. First, an example where
- the constructors take no arguments:
-
-
-
-
-Inductiveyesno : Type :=
- | yes : yesno
- | no : yesno.
-
-
-Checkyesno_ind.
-(* ===> yesno_ind : forall P : yesno -> Prop,
- P yes ->
- P no ->
- forall y : yesno, P y *)
-
-
-
-
-
-
Exercise: 1 star, optional (rgb)
- Write out the induction principle that Coq will generate for the
- following datatype. Write down your answer on paper or type it
- into a comment, and then compare it with what Coq prints.
-
-
-
-
-Inductivergb : Type :=
- | red : rgb
- | green : rgb
- | blue : rgb.
-Checkrgb_ind.
-
-
-
-☐
-
-
- Here's another example, this time with one of the constructors
- taking some arguments.
-
-
-
-
-Inductivenatlist : Type :=
- | nnil : natlist
- | ncons : nat→natlist→natlist.
-
-
-Checknatlist_ind.
-(* ===> (modulo a little variable renaming for clarity)
- natlist_ind :
- forall P : natlist -> Prop,
- P nnil ->
- (forall (n : nat) (l : natlist), P l -> P (ncons n l)) ->
- forall n : natlist, P n *)
-
-
-
-
-
-
Exercise: 1 star, optional (natlist1)
- Suppose we had written the above definition a little
- differently:
-
-Now what will the induction principle look like? ☐
-
-
- From these examples, we can extract this general rule:
-
-
-
-
-
The type declaration gives several constructors; each
- corresponds to one clause of the induction principle.
-
-
-
Each constructor c takes argument types a1...an.
-
-
-
Each ai can be either t (the datatype we are defining) or
- some other type s.
-
-
-
The corresponding case of the induction principle
- says (in English):
-
-
"for all values x1...xn of types a1...an, if P
- holds for each of the inductive arguments (each xi of
- type t), then P holds for cx1...xn".
-
-
-
-
-
-
-
-
-
-
-
-
-
Exercise: 1 star, optional (byntree_ind)
- Write out the induction principle that Coq will generate for the
- following datatype. Write down your answer on paper or type it
- into a comment, and then compare it with what Coq prints.
-
- is very similar to that of natlist. The main difference is
- that, here, the whole definition is parameterized on a set X:
- that is, we are defining a family of inductive types listX,
- one for each X. (Note that, wherever list appears in the body
- of the declaration, it is always applied to the parameter X.)
- The induction principle is likewise parameterized on X:
-
-
- Note the wording here (and, accordingly, the form of list_ind):
- The whole induction principle is parameterized on X. That is,
- list_ind can be thought of as a polymorphic function that, when
- applied to a type X, gives us back an induction principle
- specialized to the type listX.
-
-
-
Exercise: 1 star, optional (tree)
- Write out the induction principle that Coq will generate for
- the following datatype. Compare your answer with what Coq
- prints.
-
- is a generic statement that holds for all propositions
- P (strictly speaking, for all families of propositions P
- indexed by a number n). Each time we use this principle, we
- are choosing P to be a particular expression of type
- nat→Prop.
-
-
-
- We can make the proof more explicit by giving this expression a
- name. For example, instead of stating the theorem mult_0_r as
- "∀n,n×0=0," we can write it as "∀n,P_m0r
- n", where P_m0r is defined as...
-
-Now when we do the proof it is easier to see where P_m0r
- appears.
-
-
-
-
-Theoremmult_0_r'' : ∀n:nat,
- P_m0rn.
-Proof.
- applynat_ind.
- Case "n = O". reflexivity.
- Case "n = S n'".
- (* Note the proof state at this point! *)
- introsnIHn.
- unfoldP_m0rinIHn. unfoldP_m0r. simpl. applyIHn. Qed.
-
-
-
-
-
-This extra naming step isn't something that we'll do in
- normal proofs, but it is useful to do it explicitly for an example
- or two, because it allows us to see exactly what the induction
- hypothesis is. If we prove ∀n,P_m0rn by induction on
- n (using either induction or applynat_ind), we see that the
- first subgoal requires us to prove P_m0r0 ("P holds for
- zero"), while the second subgoal requires us to prove ∀n',
- P_m0rn'→P_m0rn'(Sn') (that is "P holds of Sn' if it
- holds of n'" or, more elegantly, "P is preserved by S").
- The induction hypothesis is the premise of this latter
- implication — the assumption that P holds of n', which we are
- allowed to use in proving that P holds for Sn'.
-
-
-
-
-
-
-
-
More on the induction Tactic
-
-
-
- The induction tactic actually does even more low-level
- bookkeeping for us than we discussed above.
-
-
-
- Recall the informal statement of the induction principle for
- natural numbers:
-
-
-
-
-
If Pn is some proposition involving a natural number n, and
- we want to show that P holds for all numbers n, we can
- reason like this:
-
-
show that PO holds
-
-
-
show that, if Pn' holds, then so does P(Sn')
-
-
-
conclude that Pn holds for all n.
-
-
-
-
-
-
- So, when we begin a proof with introsn and then inductionn,
- we are first telling Coq to consider a particularn (by
- introducing it into the context) and then telling it to prove
- something about all numbers (by using induction).
-
-
-
- What Coq actually does in this situation, internally, is to
- "re-generalize" the variable we perform induction on. For
- example, in our original proof that plus is associative...
-
-
-
-
-
-Theoremplus_assoc' : ∀nmp : nat,
- n + (m + p) = (n + m) + p.
-Proof.
- (* ...we first introduce all 3 variables into the context,
- which amounts to saying "Consider an arbitrary n, m, and
- p..." *)
- introsnmp.
- (* ...We now use the induction tactic to prove Pn (that
- is, n+(m+p)=(n+m)+p) for _all_ n,
- and hence also for the particular n that is in the context
- at the moment. *)
- inductionnas [| n'].
- Case "n = O". reflexivity.
- Case "n = S n'".
- (* In the second subgoal generated by induction -- the
- "inductive step" -- we must prove that Pn' implies
- P(Sn') for all n'. The induction tactic
- automatically introduces n' and Pn' into the context
- for us, leaving just P(Sn') as the goal. *)
- simpl. rewrite→IHn'. reflexivity. Qed.
-
-
-
-
-
-It also works to apply induction to a variable that is
- quantified in the goal.
-
-
-
-
-Theoremplus_comm' : ∀nm : nat,
- n + m = m + n.
-Proof.
- inductionnas [| n'].
- Case "n = O". introsm. rewrite→plus_0_r. reflexivity.
- Case "n = S n'". introsm. simpl. rewrite→IHn'.
- rewrite←plus_n_Sm. reflexivity. Qed.
-
-
-
-
-
-Note that inductionn leaves m still bound in the goal —
- i.e., what we are proving inductively is a statement beginning
- with ∀m.
-
-
-
- If we do induction on a variable that is quantified in the goal
- after some other quantifiers, the induction tactic will
- automatically introduce the variables bound by these quantifiers
- into the context.
-
-
-
-
-Theoremplus_comm'' : ∀nm : nat,
- n + m = m + n.
-Proof.
- (* Let's do induction on m this time, instead of n... *)
- inductionmas [| m'].
- Case "m = O". simpl. rewrite→plus_0_r. reflexivity.
- Case "m = S m'". simpl. rewrite←IHm'.
- rewrite←plus_n_Sm. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 1 star, optional (plus_explicit_prop)
- Rewrite both plus_assoc' and plus_comm' and their proofs in
- the same style as mult_0_r'' above — that is, for each theorem,
- give an explicit Definition of the proposition being proved by
- induction, and state the theorem and proof in terms of this
- defined proposition.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Generalizing Inductions.
-
-
-
- One potentially confusing feature of the induction tactic is
-that it happily lets you try to set up an induction over a term
-that isn't sufficiently general. The net effect of this will be
-to lose information (much as destruct can do), and leave
-you unable to complete the proof. Here's an example:
-
-
-
-
-Lemmaone_not_beautiful_FAILED: ¬ beautiful 1.
-Proof.
- introH.
- (* Just doing an inversion on H won't get us very far in the b_sum
- case. (Try it!). So we'll need induction. A naive first attempt: *)
- inductionH.
- (* But now, although we get four cases, as we would expect from
- the definition of beautiful, we lose all information about H ! *)
-Abort.
-
-
-
-
-
-The problem is that induction over a Prop only works properly over
- completely general instances of the Prop, i.e. one in which all
- the arguments are free (unconstrained) variables.
- In this respect it behaves more
- like destruct than like inversion.
-
-
-
- When you're tempted to do use induction like this, it is generally
- an indication that you need to be proving something more general.
- But in some cases, it suffices to pull out any concrete arguments
- into separate equations, like this:
-
-
-
-
-Lemmaone_not_beautiful: ∀n, n = 1 → ¬ beautifuln.
-Proof.
- introsnEH.
- inductionHas [| | | pqHpIHpHqIHq].
- Case "b_0".
- inversionE.
- Case "b_3".
- inversionE.
- Case "b_5".
- inversionE.
- Case "b_sum".
- (* the rest is a tedious case analysis *)
- destructpas [|p'].
- SCase "p = 0".
- destructqas [|q'].
- SSCase "q = 0".
- inversionE.
- SSCase "q = S q'".
- applyIHq. applyE.
- SCase "p = S p'".
- destructqas [|q'].
- SSCase "q = 0".
- applyIHp. rewriteplus_0_rinE. applyE.
- SSCase "q = S q'".
- simplinE. inversionE. destructp'. inversionH0. inversionH0.
-Qed.
-
-
-
-
-
-There's a handy remember tactic that can generate the second
-proof state out of the original one.
-
-
-
-
-Lemmaone_not_beautiful': ¬ beautiful 1.
-Proof.
- introsH.
- remember 1 asneqn:E.
- (* now carry on as above *)
- inductionH.
-Admitted.
-
-
-
-
-
-
Informal Proofs (Advanced)
-
-
-
- Q: What is the relation between a formal proof of a proposition
- P and an informal proof of the same proposition P?
-
-
-
- A: The latter should teach the reader how to produce the
- former.
-
-
-
- Q: How much detail is needed??
-
-
-
- Unfortunately, There is no single right answer; rather, there is a
- range of choices.
-
-
-
- At one end of the spectrum, we can essentially give the reader the
- whole formal proof (i.e., the informal proof amounts to just
- transcribing the formal one into words). This gives the reader
- the ability to reproduce the formal one for themselves, but it
- doesn't teach them anything.
-
-
-
- At the other end of the spectrum, we can say "The theorem is true
- and you can figure out why for yourself if you think about it hard
- enough." This is also not a good teaching strategy, because
- usually writing the proof requires some deep insights into the
- thing we're proving, and most readers will give up before they
- rediscover all the same insights as we did.
-
-
-
- In the middle is the golden mean — a proof that includes all of
- the essential insights (saving the reader the hard part of work
- that we went through to find the proof in the first place) and
- clear high-level suggestions for the more routine parts to save the
- reader from spending too much time reconstructing these
- parts (e.g., what the IH says and what must be shown in each case
- of an inductive proof), but not so much detail that the main ideas
- are obscured.
-
-
-
- Another key point: if we're comparing a formal proof of a
- proposition P and an informal proof of P, the proposition P
- doesn't change. That is, formal and informal proofs are talking
- about the same world and they must play by the same rules.
Informal Proofs by Induction
-
-
-
- Since we've spent much of this chapter looking "under the hood" at
- formal proofs by induction, now is a good moment to talk a little
- about informal proofs by induction.
-
-
-
- In the real world of mathematical communication, written proofs
- range from extremely longwinded and pedantic to extremely brief
- and telegraphic. The ideal is somewhere in between, of course,
- but while you are getting used to the style it is better to start
- out at the pedantic end. Also, during the learning phase, it is
- probably helpful to have a clear standard to compare against.
- With this in mind, we offer two templates below — one for proofs
- by induction over data (i.e., where the thing we're doing
- induction on lives in Type) and one for proofs by induction over
- evidence (i.e., where the inductively defined thing lives in
- Prop). In the rest of this course, please follow one of the two
- for all of your inductive proofs.
-
-
-
Induction Over an Inductively Defined Set
-
-
-
- Template:
-
-
-
-
-
Theorem: <Universally quantified proposition of the form
- "For all n:S, P(n)," where S is some inductively defined
- set.>
-
-
-
- Proof: By induction on n.
-
-
-
- <one case for each constructor c of S...>
-
-
-
-
-
Suppose n=ca1...ak, where <...and here we state
- the IH for each of the a's that has type S, if any>.
- We must show <...and here we restate P(ca1...ak)>.
-
-
-
- <go on and prove P(n) to finish the case...>
-
-
-
-
-
-
<other cases similarly...> ☐
-
-
-
-
-
-
-
-
-
- Example:
-
-
-
-
-
Theorem: For all sets X, lists l:listX, and numbers
- n, if lengthl=n then index(Sn)l=None.
-
-
-
- Proof: By induction on l.
-
-
-
-
-
Suppose l=[]. We must show, for all numbers n,
- that, if length []=n, then index(Sn)[]=
- None.
-
-
-
- This follows immediately from the definition of index.
-
-
-
-
-
-
Suppose l=x::l' for some x and l', where
- lengthl'=n' implies index(Sn')l'=None, for
- any number n'. We must show, for all n, that, if
- length(x::l')=n then index(Sn)(x::l')=
- None.
-
-
-
- Let n be a number with lengthl=n. Since
-
-
-
-
- lengthl = length (x::l') = S (lengthl'),
-
-
-
- it suffices to show that
-
-
-
-
- index (S (lengthl')) l' = None.
-
-
-
- But this follows directly from the induction hypothesis,
- picking n' to be length l'. ☐
-
-
-
-
-
-
-
-
-
Induction Over an Inductively Defined Proposition
-
-
-
- Since inductively defined proof objects are often called
- "derivation trees," this form of proof is also known as induction
- on derivations.
-
-
-
- Template:
-
-
-
-
-
Theorem: <Proposition of the form "Q→P," where Q is
- some inductively defined proposition (more generally,
- "For all xyz, Qxyz→Pxyz")>
-
-
-
- Proof: By induction on a derivation of Q. <Or, more
- generally, "Suppose we are given x, y, and z. We
- show that Qxyz implies Pxyz, by induction on a
- derivation of Qxyz"...>
-
-
-
- <one case for each constructor c of Q...>
-
-
-
-
-
Suppose the final rule used to show Q is c. Then
- <...and here we state the types of all of the a's
- together with any equalities that follow from the
- definition of the constructor and the IH for each of
- the a's that has type Q, if there are any>. We must
- show <...and here we restate P>.
-
-
-
- <go on and prove P to finish the case...>
-
-
-
-
-
-
<other cases similarly...> ☐
-
-
-
-
-
-
-
-
-
- Example
-
-
-
-
-
-
Theorem: The ≤ relation is transitive — i.e., for all
- numbers n, m, and o, if n≤m and m≤o, then
- n≤o.
-
-
-
- Proof: By induction on a derivation of m≤o.
-
-
-
-
-
Suppose the final rule used to show m≤o is
- le_n. Then m=o and we must show that n≤m,
- which is immediate by hypothesis.
-
-
-
-
-
-
Suppose the final rule used to show m≤o is
- le_S. Then o=So' for some o' with m≤o'.
- We must show that n≤So'.
- By induction hypothesis, n≤o'.
-
-
-
- But then, by le_S, n≤So'. ☐
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Optional Material
-
-
-
- The remainder of this chapter offers some additional details on
- how induction works in Coq, the process of building proof
- trees, and the "trusted computing base" that underlies
- Coq proofs. It can safely be skimmed on a first reading. (We
- recommend skimming rather than skipping over it outright: it
- answers some questions that occur to many Coq users at some point,
- so it is useful to have a rough idea of what's here.)
-
-
-
-
-
-
-
-
Induction Principles in Prop
-
-
-
- Earlier, we looked in detail at the induction principles that Coq
- generates for inductively defined sets. The induction
- principles for inductively defined propositions like gorgeous
- are a tiny bit more complicated. As with all induction
- principles, we want to use the induction principle on gorgeous
- to prove things by inductively considering the possible shapes
- that something in gorgeous can have — either it is evidence
- that 0 is gorgeous, or it is evidence that, for some n, 3+n
- is gorgeous, or it is evidence that, for some n, 5+n is
- gorgeous and it includes evidence that n itself is. Intuitively
- speaking, however, what we want to prove are not statements about
- evidence but statements about numbers. So we want an
- induction principle that lets us prove properties of numbers by
- induction on evidence.
-
-
-
- For example, from what we've said so far, you might expect the
- inductive definition of gorgeous...
-
-
Since gorgeous is indexed by a number n (every gorgeous
- object e is a piece of evidence that some particular number
- n is gorgeous), the proposition P is parameterized by both
- n and e — that is, the induction principle can be used to
- prove assertions involving both a gorgeous number and the
- evidence that it is gorgeous.
-
-
-
-
-
-
Since there are three ways of giving evidence of gorgeousness
- (gorgeous has three constructors), applying the induction
- principle generates three subgoals:
-
-
-
-
-
We must prove that P holds for O and b_0.
-
-
-
-
-
-
We must prove that, whenever n is a gorgeous
- number and e is an evidence of its gorgeousness,
- if P holds of n and e,
- then it also holds of 3+m and g_plus3ne.
-
-
-
-
-
-
We must prove that, whenever n is a gorgeous
- number and e is an evidence of its gorgeousness,
- if P holds of n and e,
- then it also holds of 5+m and g_plus5ne.
-
-
-
-
-
-
-
-
-
If these subgoals can be proved, then the induction principle
- tells us that P is true for all gorgeous numbers n and
- evidence e of their gorgeousness.
-
-
-
-
-
-
- But this is a little more flexibility than we actually need or
- want: it is giving us a way to prove logical assertions where the
- assertion involves properties of some piece of evidence of
- gorgeousness, while all we really care about is proving
- properties of numbers that are gorgeous — we are interested in
- assertions about numbers, not about evidence. It would therefore
- be more convenient to have an induction principle for proving
- propositions P that are parameterized just by n and whose
- conclusion establishes P for all gorgeous numbers n:
-
-
- For this reason, Coq actually generates the following simplified
- induction principle for gorgeous:
-
-
-
-
-Checkgorgeous_ind.
-(* ===> gorgeous_ind
- : forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, gorgeous n -> P n -> P (3 + n)) ->
- (forall n : nat, gorgeous n -> P n -> P (5 + n)) ->
- forall n : nat, gorgeous n -> P n *)
-
-
-
-
-
-In particular, Coq has dropped the evidence term e as a
- parameter of the the proposition P, and consequently has
- rewritten the assumption ∀(n:nat)(e:gorgeousn),...
- to be ∀(n:nat),gorgeousn→...; i.e., we no longer
- require explicit evidence of the provability of gorgeousn.
-
-
- In English, gorgeous_ind says:
-
-
-
-
-
Suppose, P is a property of natural numbers (that is, Pn is
- a Prop for every n). To show that Pn holds whenever n
- is gorgeous, it suffices to show:
-
-
-
-
-
P holds for 0,
-
-
-
-
-
-
for any n, if n is gorgeous and P holds for
- n, then P holds for 3+n,
-
-
-
-
-
-
for any n, if n is gorgeous and P holds for
- n, then P holds for 5+n.
-
-
-
-
-
-
-
-
- As expected, we can apply gorgeous_ind directly instead of using induction.
-
-The precise form of an Inductive definition can affect the
- induction principle Coq generates.
-
-
-
-For example, in Logic, we have defined ≤ as:
-
-
-
-
-(* Inductive le : nat -> nat -> Prop :=
- | le_n : forall n, le n n
- | le_S : forall n m, (le n m) -> (le n (S m)). *)
-
-
-
-
-
-This definition can be streamlined a little by observing that the
- left-hand argument n is the same everywhere in the definition,
- so we can actually make it a "general parameter" to the whole
- definition, rather than an argument to each constructor.
-
-The second one is better, even though it looks less symmetric.
- Why? Because it gives us a simpler induction principle.
-
-
-
-
-Checkle_ind.
-(* ===> forall (n : nat) (P : nat -> Prop),
- P n ->
- (forall m : nat, n <= m -> P m -> P (S m)) ->
- forall n0 : nat, n <= n0 -> P n0 *)
-
-
-
-
-
-By contrast, the induction principle that Coq calculates for the
- first definition has a lot of extra quantifiers, which makes it
- messier to work with when proving things by induction. Here is
- the induction principle for the first le:
-
-
-
-
-(* le_ind :
- forall P : nat -> nat -> Prop,
- (forall n : nat, P n n) ->
- (forall n m : nat, le n m -> P n m -> P n (S m)) ->
- forall n n0 : nat, le n n0 -> P n n0 *)
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 2 stars, optional (foo_ind_principle)
- Suppose we make the following inductive definition:
-
-
-The advantage of this definition is that the induction
- principle that Coq derives for it is precisely the familiar
- principle of Leibniz equality: what we mean when we say "x and
- y are equal" is that every property on P that is true of x
- is also true of y.
-
-
-
-
-Checkeq'_ind.
-(* ===>
- forall (X : Type) (x : X) (P : X -> Prop),
- P x -> forall y : X, x =' y -> P y
-
- ===> (i.e., after a little reorganization)
- forall (X : Type) (x : X) forall y : X,
- x =' y ->
- forall P : X -> Prop, P x -> P y *)
-
-
-
-
-
-The induction principles for conjunction and disjunction are a
- good illustration of Coq's way of generating simplified induction
- principles for Inductively defined propositions, which we
- discussed above. You try first:
-
-
-
Exercise: 1 star, optional (and_ind_principle)
- See if you can predict the induction principle for conjunction.
-
-
-
-
-(* Check and_ind. *)
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (or_ind_principle)
- See if you can predict the induction principle for disjunction.
-
-
-
-
-(* Check or_ind. *)
-
-
-
-☐
-
-
-
-
-Checkand_ind.
-
-
-
-
-
-From the inductive definition of the proposition andPQ
-
-
- Can you predict the induction principle for falsehood?
-
-
-
-
-(* Check False_ind. *)
-
-
-
-☐
-
-
- Here's the induction principle that Coq generates for existentials:
-
-
-
-
-Checkex_ind.
-(* ===> forall (X:Type) (P: X->Prop) (Q: Prop),
- (forall witness:X, P witness -> Q) ->
- ex X P ->
- Q *)
-
-
-
-
-
-This induction principle can be understood as follows: If we have
- a function f that can construct evidence for Q given any
- witness of type X together with evidence that this witness has
- property P, then from a proof of exXP we can extract the
- witness and evidence that must have been supplied to the
- constructor, give these to f, and thus obtain a proof of Q.
-
-
-
-
-
-
-
-
Explicit Proof Objects for Induction
-
-
-
- Although tactic-based proofs are normally much easier to
- work with, the ability to write a proof term directly is sometimes
- very handy, particularly when we want Coq to do something slightly
- non-standard.
-
-
- Recall the induction principle on naturals that Coq generates for
- us automatically from the Inductive declation for nat.
-
-
-
-
-Checknat_ind.
-(* ===>
- nat_ind : forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, P n -> P (S n)) ->
- forall n : nat, P n *)
-
-
-
-
-
-There's nothing magic about this induction lemma: it's just
- another Coq lemma that requires a proof. Coq generates the proof
- automatically too...
-
-
-
-
-Printnat_ind.
-Printnat_rect.
-(* ===> (after some manual inlining and tidying)
- nat_ind =
- fun (P : nat -> Prop)
- (f : P 0)
- (f0 : forall n : nat, P n -> P (S n)) =>
- fix F (n : nat) : P n :=
- match n with
- | 0 => f
- | S n0 => f0 n0 (F n0)
- end.
-*)
-
-
-
-
-
-We can read this as follows:
- Suppose we have evidence f that P holds on 0, and
- evidence f0 that ∀n:nat,Pn→P(Sn).
- Then we can prove that P holds of an arbitrary nat n via
- a recursive function F (here defined using the expression
- form Fix rather than by a top-level Fixpoint
- declaration). F pattern matches on n:
-
-
-
-
-
If it finds 0, F uses f to show that Pn holds.
-
-
-
If it finds Sn0, F applies itself recursively on n0
- to obtain evidence that Pn0 holds; then it applies f0
- on that evidence to show that P(Sn) holds.
-
-
-
- F is just an ordinary recursive function that happens to
- operate on evidence in Prop rather than on terms in Set.
-
-
-
-
-
-
- We can adapt this approach to proving nat_ind to help prove
- non-standard induction principles too. Recall our desire to
- prove that
-
-
-
- ∀n:nat,evenn→evn.
-
-
-
- Attempts to do this by standard induction on n fail, because the
- induction principle only lets us proceed when we can prove that
- evenn→even(Sn) — which is of course never provable. What
- we did in Logic was a bit of a hack:
-
-
-Once you get the hang of it, it is entirely straightforward to
- give an explicit proof term for induction principles like this.
- Proving this as a lemma using tactics is much less intuitive (try
- it!).
-
-
-
- The induction...using tactic variant gives a convenient way to
- specify a non-standard induction principle like this.
-
-
- One issue that arises with any automated proof assistant is "why
- trust it?": what if there is a bug in the implementation that
- renders all its reasoning suspect?
-
-
-
- While it is impossible to allay such concerns completely, the fact
- that Coq is based on the Curry-Howard correspondence gives it a
- strong foundation. Because propositions are just types and proofs
- are just terms, checking that an alleged proof of a proposition is
- valid just amounts to type-checking the term. Type checkers are
- relatively small and straightforward programs, so the "trusted
- computing base" for Coq — the part of the code that we have to
- believe is operating correctly — is small too.
-
-
-
- What must a typechecker do? Its primary job is to make sure that
- in each function application the expected and actual argument
- types match, that the arms of a match expression are constructor
- patterns belonging to the inductive type being matched over and
- all arms of the match return the same type, and so on.
-
-
-
- There are a few additional wrinkles:
-
-
-
-
-
Since Coq types can themselves be expressions, the checker must
- normalize these (by using the computation rules) before
- comparing them.
-
-
-
-
-
-
The checker must make sure that match expressions are
- exhaustive. That is, there must be an arm for every possible
- constructor. To see why, consider the following alleged proof
- object:
-
-
-
-
-Definitionor_bogus : ∀PQ, P∨Q→P :=
- fun (PQ : Prop) (A : P∨Q) ⇒
- matchAwith
- | or_introlH ⇒ H
- end.
-
-
-
- All the types here match correctly, but the match only
- considers one of the possible constructors for or. Coq's
- exhaustiveness check will reject this definition.
-
-
-
-
-
-
The checker must make sure that each fix expression
- terminates. It does this using a syntactic check to make sure
- that each recursive call is on a subexpression of the original
- argument. To see why this is essential, consider this alleged
- proof:
-
-
- Again, this is perfectly well-typed, but (fortunately) Coq will
- reject it.
-
-
-
-
-
- Note that the soundness of Coq depends only on the correctness of
- this typechecking engine, not on the tactic machinery. If there
- is a bug in a tactic implementation (and this certainly does
- happen!), that tactic might construct an invalid proof term. But
- when you type Qed, Coq checks the term for validity from
- scratch. Only lemmas whose proofs pass the type-checker can be
- used in further proof developments.
-
-
-
-
\ No newline at end of file
diff --git a/MoreInd.v b/MoreInd.v
deleted file mode 100644
index 7d68b78..0000000
--- a/MoreInd.v
+++ /dev/null
@@ -1,1158 +0,0 @@
-(** * MoreInd: More on Induction *)
-
-Require Export "ProofObjects".
-
-(* ##################################################### *)
-(** * Induction Principles *)
-
-(** This is a good point to pause and take a deeper look at induction
- principles.
-
- Every time we declare a new [Inductive] datatype, Coq
- automatically generates and proves an _induction principle_
- for this type.
-
- The induction principle for a type [t] is called [t_ind]. Here is
- the one for natural numbers: *)
-
-Check nat_ind.
-(* ===> nat_ind :
- forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, P n -> P (S n)) ->
- forall n : nat, P n *)
-
-(** *** *)
-(** The [induction] tactic is a straightforward wrapper that, at
- its core, simply performs [apply t_ind]. To see this more
- clearly, let's experiment a little with using [apply nat_ind]
- directly, instead of the [induction] tactic, to carry out some
- proofs. Here, for example, is an alternate proof of a theorem
- that we saw in the [Basics] chapter. *)
-
-Theorem mult_0_r' : forall n:nat,
- n * 0 = 0.
-Proof.
- apply nat_ind.
- Case "O". reflexivity.
- Case "S". simpl. intros n IHn. rewrite -> IHn.
- reflexivity. Qed.
-
-
-(** This proof is basically the same as the earlier one, but a
- few minor differences are worth noting. First, in the induction
- step of the proof (the ["S"] case), we have to do a little
- bookkeeping manually (the [intros]) that [induction] does
- automatically.
-
- Second, we do not introduce [n] into the context before applying
- [nat_ind] -- the conclusion of [nat_ind] is a quantified formula,
- and [apply] needs this conclusion to exactly match the shape of
- the goal state, including the quantifier. The [induction] tactic
- works either with a variable in the context or a quantified
- variable in the goal.
-
- Third, the [apply] tactic automatically chooses variable names for
- us (in the second subgoal, here), whereas [induction] lets us
- specify (with the [as...] clause) what names should be used. The
- automatic choice is actually a little unfortunate, since it
- re-uses the name [n] for a variable that is different from the [n]
- in the original theorem. This is why the [Case] annotation is
- just [S] -- if we tried to write it out in the more explicit form
- that we've been using for most proofs, we'd have to write [n = S
- n], which doesn't make a lot of sense! All of these conveniences
- make [induction] nicer to use in practice than applying induction
- principles like [nat_ind] directly. But it is important to
- realize that, modulo this little bit of bookkeeping, applying
- [nat_ind] is what we are really doing. *)
-
-(** **** Exercise: 2 stars, optional (plus_one_r') *)
-(** Complete this proof as we did [mult_0_r'] above, without using
- the [induction] tactic. *)
-
-Theorem plus_one_r' : forall n:nat,
- n + 1 = S n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Coq generates induction principles for every datatype defined with
- [Inductive], including those that aren't recursive. (Although
- we don't need induction to prove properties of non-recursive
- datatypes, the idea of an induction principle still makes sense
- for them: it gives a way to prove that a property holds for all
- values of the type.)
-
- These generated principles follow a similar pattern. If we define a
- type [t] with constructors [c1] ... [cn], Coq generates a theorem
- with this shape:
- t_ind :
- forall P : t -> Prop,
- ... case for c1 ... ->
- ... case for c2 ... ->
- ...
- ... case for cn ... ->
- forall n : t, P n
- The specific shape of each case depends on the arguments to the
- corresponding constructor. Before trying to write down a general
- rule, let's look at some more examples. First, an example where
- the constructors take no arguments: *)
-
-Inductive yesno : Type :=
- | yes : yesno
- | no : yesno.
-
-Check yesno_ind.
-(* ===> yesno_ind : forall P : yesno -> Prop,
- P yes ->
- P no ->
- forall y : yesno, P y *)
-
-(** **** Exercise: 1 star, optional (rgb) *)
-(** Write out the induction principle that Coq will generate for the
- following datatype. Write down your answer on paper or type it
- into a comment, and then compare it with what Coq prints. *)
-
-Inductive rgb : Type :=
- | red : rgb
- | green : rgb
- | blue : rgb.
-Check rgb_ind.
-(** [] *)
-
-(** Here's another example, this time with one of the constructors
- taking some arguments. *)
-
-Inductive natlist : Type :=
- | nnil : natlist
- | ncons : nat -> natlist -> natlist.
-
-Check natlist_ind.
-(* ===> (modulo a little variable renaming for clarity)
- natlist_ind :
- forall P : natlist -> Prop,
- P nnil ->
- (forall (n : nat) (l : natlist), P l -> P (ncons n l)) ->
- forall n : natlist, P n *)
-
-(** **** Exercise: 1 star, optional (natlist1) *)
-(** Suppose we had written the above definition a little
- differently: *)
-
-Inductive natlist1 : Type :=
- | nnil1 : natlist1
- | nsnoc1 : natlist1 -> nat -> natlist1.
-
-(** Now what will the induction principle look like? *)
-(** [] *)
-
-(** From these examples, we can extract this general rule:
-
- - The type declaration gives several constructors; each
- corresponds to one clause of the induction principle.
- - Each constructor [c] takes argument types [a1]...[an].
- - Each [ai] can be either [t] (the datatype we are defining) or
- some other type [s].
- - The corresponding case of the induction principle
- says (in English):
- - "for all values [x1]...[xn] of types [a1]...[an], if [P]
- holds for each of the inductive arguments (each [xi] of
- type [t]), then [P] holds for [c x1 ... xn]".
-
-*)
-
-
-
-(** **** Exercise: 1 star, optional (byntree_ind) *)
-(** Write out the induction principle that Coq will generate for the
- following datatype. Write down your answer on paper or type it
- into a comment, and then compare it with what Coq prints. *)
-
-Inductive byntree : Type :=
- | bempty : byntree
- | bleaf : yesno -> byntree
- | nbranch : yesno -> byntree -> byntree -> byntree.
-(** [] *)
-
-
-(** **** Exercise: 1 star, optional (ex_set) *)
-(** Here is an induction principle for an inductively defined
- set.
- ExSet_ind :
- forall P : ExSet -> Prop,
- (forall b : bool, P (con1 b)) ->
- (forall (n : nat) (e : ExSet), P e -> P (con2 n e)) ->
- forall e : ExSet, P e
- Give an [Inductive] definition of [ExSet]: *)
-
-Inductive ExSet : Type :=
- (* FILL IN HERE *)
-.
-(** [] *)
-
-(** What about polymorphic datatypes?
-
- The inductive definition of polymorphic lists
- Inductive list (X:Type) : Type :=
- | nil : list X
- | cons : X -> list X -> list X.
- is very similar to that of [natlist]. The main difference is
- that, here, the whole definition is _parameterized_ on a set [X]:
- that is, we are defining a _family_ of inductive types [list X],
- one for each [X]. (Note that, wherever [list] appears in the body
- of the declaration, it is always applied to the parameter [X].)
- The induction principle is likewise parameterized on [X]:
- list_ind :
- forall (X : Type) (P : list X -> Prop),
- P [] ->
- (forall (x : X) (l : list X), P l -> P (x :: l)) ->
- forall l : list X, P l
- Note the wording here (and, accordingly, the form of [list_ind]):
- The _whole_ induction principle is parameterized on [X]. That is,
- [list_ind] can be thought of as a polymorphic function that, when
- applied to a type [X], gives us back an induction principle
- specialized to the type [list X]. *)
-
-(** **** Exercise: 1 star, optional (tree) *)
-(** Write out the induction principle that Coq will generate for
- the following datatype. Compare your answer with what Coq
- prints. *)
-
-Inductive tree (X:Type) : Type :=
- | leaf : X -> tree X
- | node : tree X -> tree X -> tree X.
-Check tree_ind.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (mytype) *)
-(** Find an inductive definition that gives rise to the
- following induction principle:
- mytype_ind :
- forall (X : Type) (P : mytype X -> Prop),
- (forall x : X, P (constr1 X x)) ->
- (forall n : nat, P (constr2 X n)) ->
- (forall m : mytype X, P m ->
- forall n : nat, P (constr3 X m n)) ->
- forall m : mytype X, P m
-*)
-(** [] *)
-
-(** **** Exercise: 1 star, optional (foo) *)
-(** Find an inductive definition that gives rise to the
- following induction principle:
- foo_ind :
- forall (X Y : Type) (P : foo X Y -> Prop),
- (forall x : X, P (bar X Y x)) ->
- (forall y : Y, P (baz X Y y)) ->
- (forall f1 : nat -> foo X Y,
- (forall n : nat, P (f1 n)) -> P (quux X Y f1)) ->
- forall f2 : foo X Y, P f2
-*)
-(** [] *)
-
-(** **** Exercise: 1 star, optional (foo') *)
-(** Consider the following inductive definition: *)
-
-Inductive foo' (X:Type) : Type :=
- | C1 : list X -> foo' X -> foo' X
- | C2 : foo' X.
-
-(** What induction principle will Coq generate for [foo']? Fill
- in the blanks, then check your answer with Coq.)
- foo'_ind :
- forall (X : Type) (P : foo' X -> Prop),
- (forall (l : list X) (f : foo' X),
- _______________________ ->
- _______________________ ) ->
- ___________________________________________ ->
- forall f : foo' X, ________________________
-*)
-
-(** [] *)
-
-(* ##################################################### *)
-(** ** Induction Hypotheses *)
-
-(** Where does the phrase "induction hypothesis" fit into this story?
-
- The induction principle for numbers
- forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, P n -> P (S n)) ->
- forall n : nat, P n
- is a generic statement that holds for all propositions
- [P] (strictly speaking, for all families of propositions [P]
- indexed by a number [n]). Each time we use this principle, we
- are choosing [P] to be a particular expression of type
- [nat->Prop].
-
- We can make the proof more explicit by giving this expression a
- name. For example, instead of stating the theorem [mult_0_r] as
- "[forall n, n * 0 = 0]," we can write it as "[forall n, P_m0r
- n]", where [P_m0r] is defined as... *)
-
-Definition P_m0r (n:nat) : Prop :=
- n * 0 = 0.
-
-(** ... or equivalently... *)
-
-Definition P_m0r' : nat->Prop :=
- fun n => n * 0 = 0.
-
-(** Now when we do the proof it is easier to see where [P_m0r]
- appears. *)
-
-Theorem mult_0_r'' : forall n:nat,
- P_m0r n.
-Proof.
- apply nat_ind.
- Case "n = O". reflexivity.
- Case "n = S n'".
- (* Note the proof state at this point! *)
- intros n IHn.
- unfold P_m0r in IHn. unfold P_m0r. simpl. apply IHn. Qed.
-
-(** This extra naming step isn't something that we'll do in
- normal proofs, but it is useful to do it explicitly for an example
- or two, because it allows us to see exactly what the induction
- hypothesis is. If we prove [forall n, P_m0r n] by induction on
- [n] (using either [induction] or [apply nat_ind]), we see that the
- first subgoal requires us to prove [P_m0r 0] ("[P] holds for
- zero"), while the second subgoal requires us to prove [forall n',
- P_m0r n' -> P_m0r n' (S n')] (that is "[P] holds of [S n'] if it
- holds of [n']" or, more elegantly, "[P] is preserved by [S]").
- The _induction hypothesis_ is the premise of this latter
- implication -- the assumption that [P] holds of [n'], which we are
- allowed to use in proving that [P] holds for [S n']. *)
-
-(* ##################################################### *)
-(** ** More on the [induction] Tactic *)
-
-(** The [induction] tactic actually does even more low-level
- bookkeeping for us than we discussed above.
-
- Recall the informal statement of the induction principle for
- natural numbers:
- - If [P n] is some proposition involving a natural number n, and
- we want to show that P holds for _all_ numbers n, we can
- reason like this:
- - show that [P O] holds
- - show that, if [P n'] holds, then so does [P (S n')]
- - conclude that [P n] holds for all n.
- So, when we begin a proof with [intros n] and then [induction n],
- we are first telling Coq to consider a _particular_ [n] (by
- introducing it into the context) and then telling it to prove
- something about _all_ numbers (by using induction).
-
- What Coq actually does in this situation, internally, is to
- "re-generalize" the variable we perform induction on. For
- example, in our original proof that [plus] is associative...
-*)
-
-Theorem plus_assoc' : forall n m p : nat,
- n + (m + p) = (n + m) + p.
-Proof.
- (* ...we first introduce all 3 variables into the context,
- which amounts to saying "Consider an arbitrary [n], [m], and
- [p]..." *)
- intros n m p.
- (* ...We now use the [induction] tactic to prove [P n] (that
- is, [n + (m + p) = (n + m) + p]) for _all_ [n],
- and hence also for the particular [n] that is in the context
- at the moment. *)
- induction n as [| n'].
- Case "n = O". reflexivity.
- Case "n = S n'".
- (* In the second subgoal generated by [induction] -- the
- "inductive step" -- we must prove that [P n'] implies
- [P (S n')] for all [n']. The [induction] tactic
- automatically introduces [n'] and [P n'] into the context
- for us, leaving just [P (S n')] as the goal. *)
- simpl. rewrite -> IHn'. reflexivity. Qed.
-
-
-(** It also works to apply [induction] to a variable that is
- quantified in the goal. *)
-
-Theorem plus_comm' : forall n m : nat,
- n + m = m + n.
-Proof.
- induction n as [| n'].
- Case "n = O". intros m. rewrite -> plus_0_r. reflexivity.
- Case "n = S n'". intros m. simpl. rewrite -> IHn'.
- rewrite <- plus_n_Sm. reflexivity. Qed.
-
-(** Note that [induction n] leaves [m] still bound in the goal --
- i.e., what we are proving inductively is a statement beginning
- with [forall m].
-
- If we do [induction] on a variable that is quantified in the goal
- _after_ some other quantifiers, the [induction] tactic will
- automatically introduce the variables bound by these quantifiers
- into the context. *)
-
-Theorem plus_comm'' : forall n m : nat,
- n + m = m + n.
-Proof.
- (* Let's do induction on [m] this time, instead of [n]... *)
- induction m as [| m'].
- Case "m = O". simpl. rewrite -> plus_0_r. reflexivity.
- Case "m = S m'". simpl. rewrite <- IHm'.
- rewrite <- plus_n_Sm. reflexivity. Qed.
-
-(** **** Exercise: 1 star, optional (plus_explicit_prop) *)
-(** Rewrite both [plus_assoc'] and [plus_comm'] and their proofs in
- the same style as [mult_0_r''] above -- that is, for each theorem,
- give an explicit [Definition] of the proposition being proved by
- induction, and state the theorem and proof in terms of this
- defined proposition. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-(** ** Generalizing Inductions. *)
-
-(** One potentially confusing feature of the [induction] tactic is
-that it happily lets you try to set up an induction over a term
-that isn't sufficiently general. The net effect of this will be
-to lose information (much as [destruct] can do), and leave
-you unable to complete the proof. Here's an example: *)
-
-Lemma one_not_beautiful_FAILED: ~ beautiful 1.
-Proof.
- intro H.
- (* Just doing an [inversion] on [H] won't get us very far in the [b_sum]
- case. (Try it!). So we'll need induction. A naive first attempt: *)
- induction H.
- (* But now, although we get four cases, as we would expect from
- the definition of [beautiful], we lose all information about [H] ! *)
-Abort.
-
-(** The problem is that [induction] over a Prop only works properly over
- completely general instances of the Prop, i.e. one in which all
- the arguments are free (unconstrained) variables.
- In this respect it behaves more
- like [destruct] than like [inversion].
-
- When you're tempted to do use [induction] like this, it is generally
- an indication that you need to be proving something more general.
- But in some cases, it suffices to pull out any concrete arguments
- into separate equations, like this: *)
-
-Lemma one_not_beautiful: forall n, n = 1 -> ~ beautiful n.
-Proof.
- intros n E H.
- induction H as [| | | p q Hp IHp Hq IHq].
- Case "b_0".
- inversion E.
- Case "b_3".
- inversion E.
- Case "b_5".
- inversion E.
- Case "b_sum".
- (* the rest is a tedious case analysis *)
- destruct p as [|p'].
- SCase "p = 0".
- destruct q as [|q'].
- SSCase "q = 0".
- inversion E.
- SSCase "q = S q'".
- apply IHq. apply E.
- SCase "p = S p'".
- destruct q as [|q'].
- SSCase "q = 0".
- apply IHp. rewrite plus_0_r in E. apply E.
- SSCase "q = S q'".
- simpl in E. inversion E. destruct p'. inversion H0. inversion H0.
-Qed.
-
-(** There's a handy [remember] tactic that can generate the second
-proof state out of the original one. *)
-
-Lemma one_not_beautiful': ~ beautiful 1.
-Proof.
- intros H.
- remember 1 as n eqn:E.
- (* now carry on as above *)
- induction H.
-Admitted.
-
-
-(* ####################################################### *)
-(** * Informal Proofs (Advanced) *)
-
-(** Q: What is the relation between a formal proof of a proposition
- [P] and an informal proof of the same proposition [P]?
-
- A: The latter should _teach_ the reader how to produce the
- former.
-
- Q: How much detail is needed??
-
- Unfortunately, There is no single right answer; rather, there is a
- range of choices.
-
- At one end of the spectrum, we can essentially give the reader the
- whole formal proof (i.e., the informal proof amounts to just
- transcribing the formal one into words). This gives the reader
- the _ability_ to reproduce the formal one for themselves, but it
- doesn't _teach_ them anything.
-
- At the other end of the spectrum, we can say "The theorem is true
- and you can figure out why for yourself if you think about it hard
- enough." This is also not a good teaching strategy, because
- usually writing the proof requires some deep insights into the
- thing we're proving, and most readers will give up before they
- rediscover all the same insights as we did.
-
- In the middle is the golden mean -- a proof that includes all of
- the essential insights (saving the reader the hard part of work
- that we went through to find the proof in the first place) and
- clear high-level suggestions for the more routine parts to save the
- reader from spending too much time reconstructing these
- parts (e.g., what the IH says and what must be shown in each case
- of an inductive proof), but not so much detail that the main ideas
- are obscured.
-
- Another key point: if we're comparing a formal proof of a
- proposition [P] and an informal proof of [P], the proposition [P]
- doesn't change. That is, formal and informal proofs are _talking
- about the same world_ and they _must play by the same rules_. *)
-(** ** Informal Proofs by Induction *)
-
-(** Since we've spent much of this chapter looking "under the hood" at
- formal proofs by induction, now is a good moment to talk a little
- about _informal_ proofs by induction.
-
- In the real world of mathematical communication, written proofs
- range from extremely longwinded and pedantic to extremely brief
- and telegraphic. The ideal is somewhere in between, of course,
- but while you are getting used to the style it is better to start
- out at the pedantic end. Also, during the learning phase, it is
- probably helpful to have a clear standard to compare against.
- With this in mind, we offer two templates below -- one for proofs
- by induction over _data_ (i.e., where the thing we're doing
- induction on lives in [Type]) and one for proofs by induction over
- _evidence_ (i.e., where the inductively defined thing lives in
- [Prop]). In the rest of this course, please follow one of the two
- for _all_ of your inductive proofs. *)
-
-(** *** Induction Over an Inductively Defined Set *)
-
-(** _Template_:
-
- - _Theorem_:
-
- _Proof_: By induction on [n].
-
-
-
- - Suppose [n = c a1 ... ak], where <...and here we state
- the IH for each of the [a]'s that has type [S], if any>.
- We must show <...and here we restate [P(c a1 ... ak)]>.
-
-
-
- - []
-
- _Example_:
-
- - _Theorem_: For all sets [X], lists [l : list X], and numbers
- [n], if [length l = n] then [index (S n) l = None].
-
- _Proof_: By induction on [l].
-
- - Suppose [l = []]. We must show, for all numbers [n],
- that, if length [[] = n], then [index (S n) [] =
- None].
-
- This follows immediately from the definition of index.
-
- - Suppose [l = x :: l'] for some [x] and [l'], where
- [length l' = n'] implies [index (S n') l' = None], for
- any number [n']. We must show, for all [n], that, if
- [length (x::l') = n] then [index (S n) (x::l') =
- None].
-
- Let [n] be a number with [length l = n]. Since
- length l = length (x::l') = S (length l'),
- it suffices to show that
- index (S (length l')) l' = None.
-]]
- But this follows directly from the induction hypothesis,
- picking [n'] to be length [l']. [] *)
-
-(** *** Induction Over an Inductively Defined Proposition *)
-
-(** Since inductively defined proof objects are often called
- "derivation trees," this form of proof is also known as _induction
- on derivations_.
-
- _Template_:
-
- - _Theorem_: P]," where [Q] is
- some inductively defined proposition (more generally,
- "For all [x] [y] [z], [Q x y z -> P x y z]")>
-
- _Proof_: By induction on a derivation of [Q].
-
-
-
- - Suppose the final rule used to show [Q] is [c]. Then
- <...and here we state the types of all of the [a]'s
- together with any equalities that follow from the
- definition of the constructor and the IH for each of
- the [a]'s that has type [Q], if there are any>. We must
- show <...and here we restate [P]>.
-
-
-
- - []
-
- _Example_
-
- - _Theorem_: The [<=] relation is transitive -- i.e., for all
- numbers [n], [m], and [o], if [n <= m] and [m <= o], then
- [n <= o].
-
- _Proof_: By induction on a derivation of [m <= o].
-
- - Suppose the final rule used to show [m <= o] is
- [le_n]. Then [m = o] and we must show that [n <= m],
- which is immediate by hypothesis.
-
- - Suppose the final rule used to show [m <= o] is
- [le_S]. Then [o = S o'] for some [o'] with [m <= o'].
- We must show that [n <= S o'].
- By induction hypothesis, [n <= o'].
-
- But then, by [le_S], [n <= S o']. [] *)
-
-
-
-(* ##################################################### *)
-(** * Optional Material *)
-
-(** The remainder of this chapter offers some additional details on
- how induction works in Coq, the process of building proof
- trees, and the "trusted computing base" that underlies
- Coq proofs. It can safely be skimmed on a first reading. (We
- recommend skimming rather than skipping over it outright: it
- answers some questions that occur to many Coq users at some point,
- so it is useful to have a rough idea of what's here.) *)
-
-
-(* ##################################################### *)
-(** ** Induction Principles in [Prop] *)
-
-
-(** Earlier, we looked in detail at the induction principles that Coq
- generates for inductively defined _sets_. The induction
- principles for inductively defined _propositions_ like [gorgeous]
- are a tiny bit more complicated. As with all induction
- principles, we want to use the induction principle on [gorgeous]
- to prove things by inductively considering the possible shapes
- that something in [gorgeous] can have -- either it is evidence
- that [0] is gorgeous, or it is evidence that, for some [n], [3+n]
- is gorgeous, or it is evidence that, for some [n], [5+n] is
- gorgeous and it includes evidence that [n] itself is. Intuitively
- speaking, however, what we want to prove are not statements about
- _evidence_ but statements about _numbers_. So we want an
- induction principle that lets us prove properties of numbers by
- induction on evidence.
-
- For example, from what we've said so far, you might expect the
- inductive definition of [gorgeous]...
- Inductive gorgeous : nat -> Prop :=
- g_0 : gorgeous 0
- | g_plus3 : forall n, gorgeous n -> gorgeous (3+m)
- | g_plus5 : forall n, gorgeous n -> gorgeous (5+m).
- ...to give rise to an induction principle that looks like this...
- gorgeous_ind_max :
- forall P : (forall n : nat, gorgeous n -> Prop),
- P O g_0 ->
- (forall (m : nat) (e : gorgeous m),
- P m e -> P (3+m) (g_plus3 m e) ->
- (forall (m : nat) (e : gorgeous m),
- P m e -> P (5+m) (g_plus5 m e) ->
- forall (n : nat) (e : gorgeous n), P n e
- ... because:
-
- - Since [gorgeous] is indexed by a number [n] (every [gorgeous]
- object [e] is a piece of evidence that some particular number
- [n] is gorgeous), the proposition [P] is parameterized by both
- [n] and [e] -- that is, the induction principle can be used to
- prove assertions involving both a gorgeous number and the
- evidence that it is gorgeous.
-
- - Since there are three ways of giving evidence of gorgeousness
- ([gorgeous] has three constructors), applying the induction
- principle generates three subgoals:
-
- - We must prove that [P] holds for [O] and [b_0].
-
- - We must prove that, whenever [n] is a gorgeous
- number and [e] is an evidence of its gorgeousness,
- if [P] holds of [n] and [e],
- then it also holds of [3+m] and [g_plus3 n e].
-
- - We must prove that, whenever [n] is a gorgeous
- number and [e] is an evidence of its gorgeousness,
- if [P] holds of [n] and [e],
- then it also holds of [5+m] and [g_plus5 n e].
-
- - If these subgoals can be proved, then the induction principle
- tells us that [P] is true for _all_ gorgeous numbers [n] and
- evidence [e] of their gorgeousness.
-
- But this is a little more flexibility than we actually need or
- want: it is giving us a way to prove logical assertions where the
- assertion involves properties of some piece of _evidence_ of
- gorgeousness, while all we really care about is proving
- properties of _numbers_ that are gorgeous -- we are interested in
- assertions about numbers, not about evidence. It would therefore
- be more convenient to have an induction principle for proving
- propositions [P] that are parameterized just by [n] and whose
- conclusion establishes [P] for all gorgeous numbers [n]:
- forall P : nat -> Prop,
- ... ->
- forall n : nat, gorgeous n -> P n
- For this reason, Coq actually generates the following simplified
- induction principle for [gorgeous]: *)
-
-
-
-Check gorgeous_ind.
-(* ===> gorgeous_ind
- : forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, gorgeous n -> P n -> P (3 + n)) ->
- (forall n : nat, gorgeous n -> P n -> P (5 + n)) ->
- forall n : nat, gorgeous n -> P n *)
-
-(** In particular, Coq has dropped the evidence term [e] as a
- parameter of the the proposition [P], and consequently has
- rewritten the assumption [forall (n : nat) (e: gorgeous n), ...]
- to be [forall (n : nat), gorgeous n -> ...]; i.e., we no longer
- require explicit evidence of the provability of [gorgeous n]. *)
-
-(** In English, [gorgeous_ind] says:
-
- - Suppose, [P] is a property of natural numbers (that is, [P n] is
- a [Prop] for every [n]). To show that [P n] holds whenever [n]
- is gorgeous, it suffices to show:
-
- - [P] holds for [0],
-
- - for any [n], if [n] is gorgeous and [P] holds for
- [n], then [P] holds for [3+n],
-
- - for any [n], if [n] is gorgeous and [P] holds for
- [n], then [P] holds for [5+n]. *)
-
-(** As expected, we can apply [gorgeous_ind] directly instead of using [induction]. *)
-
-Theorem gorgeous__beautiful' : forall n, gorgeous n -> beautiful n.
-Proof.
- intros.
- apply gorgeous_ind.
- Case "g_0".
- apply b_0.
- Case "g_plus3".
- intros.
- apply b_sum. apply b_3.
- apply H1.
- Case "g_plus5".
- intros.
- apply b_sum. apply b_5.
- apply H1.
- apply H.
-Qed.
-
-
-
-(** The precise form of an Inductive definition can affect the
- induction principle Coq generates.
-
-For example, in [Logic], we have defined [<=] as: *)
-
-(* Inductive le : nat -> nat -> Prop :=
- | le_n : forall n, le n n
- | le_S : forall n m, (le n m) -> (le n (S m)). *)
-
-(** This definition can be streamlined a little by observing that the
- left-hand argument [n] is the same everywhere in the definition,
- so we can actually make it a "general parameter" to the whole
- definition, rather than an argument to each constructor. *)
-
-Inductive le (n:nat) : nat -> Prop :=
- | le_n : le n n
- | le_S : forall m, (le n m) -> (le n (S m)).
-
-Notation "m <= n" := (le m n).
-
-(** The second one is better, even though it looks less symmetric.
- Why? Because it gives us a simpler induction principle. *)
-
-Check le_ind.
-(* ===> forall (n : nat) (P : nat -> Prop),
- P n ->
- (forall m : nat, n <= m -> P m -> P (S m)) ->
- forall n0 : nat, n <= n0 -> P n0 *)
-
-(** By contrast, the induction principle that Coq calculates for the
- first definition has a lot of extra quantifiers, which makes it
- messier to work with when proving things by induction. Here is
- the induction principle for the first [le]: *)
-
-(* le_ind :
- forall P : nat -> nat -> Prop,
- (forall n : nat, P n n) ->
- (forall n m : nat, le n m -> P n m -> P n (S m)) ->
- forall n n0 : nat, le n n0 -> P n n0 *)
-
-
-(* ##################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 2 stars, optional (foo_ind_principle) *)
-(** Suppose we make the following inductive definition:
- Inductive foo (X : Set) (Y : Set) : Set :=
- | foo1 : X -> foo X Y
- | foo2 : Y -> foo X Y
- | foo3 : foo X Y -> foo X Y.
- Fill in the blanks to complete the induction principle that will be
- generated by Coq.
- foo_ind
- : forall (X Y : Set) (P : foo X Y -> Prop),
- (forall x : X, __________________________________) ->
- (forall y : Y, __________________________________) ->
- (________________________________________________) ->
- ________________________________________________
-
-*)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (bar_ind_principle) *)
-(** Consider the following induction principle:
- bar_ind
- : forall P : bar -> Prop,
- (forall n : nat, P (bar1 n)) ->
- (forall b : bar, P b -> P (bar2 b)) ->
- (forall (b : bool) (b0 : bar), P b0 -> P (bar3 b b0)) ->
- forall b : bar, P b
- Write out the corresponding inductive set definition.
- Inductive bar : Set :=
- | bar1 : ________________________________________
- | bar2 : ________________________________________
- | bar3 : ________________________________________.
-
-*)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (no_longer_than_ind) *)
-(** Given the following inductively defined proposition:
- Inductive no_longer_than (X : Set) : (list X) -> nat -> Prop :=
- | nlt_nil : forall n, no_longer_than X [] n
- | nlt_cons : forall x l n, no_longer_than X l n ->
- no_longer_than X (x::l) (S n)
- | nlt_succ : forall l n, no_longer_than X l n ->
- no_longer_than X l (S n).
- write the induction principle generated by Coq.
- no_longer_than_ind
- : forall (X : Set) (P : list X -> nat -> Prop),
- (forall n : nat, ____________________) ->
- (forall (x : X) (l : list X) (n : nat),
- no_longer_than X l n -> ____________________ ->
- _____________________________ ->
- (forall (l : list X) (n : nat),
- no_longer_than X l n -> ____________________ ->
- _____________________________ ->
- forall (l : list X) (n : nat), no_longer_than X l n ->
- ____________________
-
-*)
-(** [] *)
-
-
-(* ##################################################### *)
-(** ** Induction Principles for other Logical Propositions *)
-
-(** Similarly, in [Logic] we have defined [eq] as: *)
-
-(* Inductive eq (X:Type) : X -> X -> Prop :=
- refl_equal : forall x, eq X x x. *)
-
-(** In the Coq standard library, the definition of equality is
- slightly different: *)
-
-Inductive eq' (X:Type) (x:X) : X -> Prop :=
- refl_equal' : eq' X x x.
-
-(** The advantage of this definition is that the induction
- principle that Coq derives for it is precisely the familiar
- principle of _Leibniz equality_: what we mean when we say "[x] and
- [y] are equal" is that every property on [P] that is true of [x]
- is also true of [y]. *)
-
-Check eq'_ind.
-(* ===>
- forall (X : Type) (x : X) (P : X -> Prop),
- P x -> forall y : X, x =' y -> P y
-
- ===> (i.e., after a little reorganization)
- forall (X : Type) (x : X) forall y : X,
- x =' y ->
- forall P : X -> Prop, P x -> P y *)
-
-
-
-(** The induction principles for conjunction and disjunction are a
- good illustration of Coq's way of generating simplified induction
- principles for [Inductive]ly defined propositions, which we
- discussed above. You try first: *)
-
-(** **** Exercise: 1 star, optional (and_ind_principle) *)
-(** See if you can predict the induction principle for conjunction. *)
-
-(* Check and_ind. *)
-(** [] *)
-
-(** **** Exercise: 1 star, optional (or_ind_principle) *)
-(** See if you can predict the induction principle for disjunction. *)
-
-(* Check or_ind. *)
-(** [] *)
-
-Check and_ind.
-
-(** From the inductive definition of the proposition [and P Q]
- Inductive and (P Q : Prop) : Prop :=
- conj : P -> Q -> (and P Q).
- we might expect Coq to generate this induction principle
- and_ind_max :
- forall (P Q : Prop) (P0 : P /\ Q -> Prop),
- (forall (a : P) (b : Q), P0 (conj P Q a b)) ->
- forall a : P /\ Q, P0 a
- but actually it generates this simpler and more useful one:
- and_ind :
- forall P Q P0 : Prop,
- (P -> Q -> P0) ->
- P /\ Q -> P0
- In the same way, when given the inductive definition of [or P Q]
- Inductive or (P Q : Prop) : Prop :=
- | or_introl : P -> or P Q
- | or_intror : Q -> or P Q.
- instead of the "maximal induction principle"
- or_ind_max :
- forall (P Q : Prop) (P0 : P \/ Q -> Prop),
- (forall a : P, P0 (or_introl P Q a)) ->
- (forall b : Q, P0 (or_intror P Q b)) ->
- forall o : P \/ Q, P0 o
- what Coq actually generates is this:
- or_ind :
- forall P Q P0 : Prop,
- (P -> P0) ->
- (Q -> P0) ->
- P \/ Q -> P0
-]]
-*)
-
-(** **** Exercise: 1 star, optional (False_ind_principle) *)
-(** Can you predict the induction principle for falsehood? *)
-
-(* Check False_ind. *)
-(** [] *)
-
-(** Here's the induction principle that Coq generates for existentials: *)
-
-Check ex_ind.
-(* ===> forall (X:Type) (P: X->Prop) (Q: Prop),
- (forall witness:X, P witness -> Q) ->
- ex X P ->
- Q *)
-
-(** This induction principle can be understood as follows: If we have
- a function [f] that can construct evidence for [Q] given _any_
- witness of type [X] together with evidence that this witness has
- property [P], then from a proof of [ex X P] we can extract the
- witness and evidence that must have been supplied to the
- constructor, give these to [f], and thus obtain a proof of [Q]. *)
-
-
-
-(* ######################################################### *)
-(** ** Explicit Proof Objects for Induction *)
-
-
-(** Although tactic-based proofs are normally much easier to
- work with, the ability to write a proof term directly is sometimes
- very handy, particularly when we want Coq to do something slightly
- non-standard. *)
-
-(** Recall the induction principle on naturals that Coq generates for
- us automatically from the Inductive declation for [nat]. *)
-
-Check nat_ind.
-(* ===>
- nat_ind : forall P : nat -> Prop,
- P 0 ->
- (forall n : nat, P n -> P (S n)) ->
- forall n : nat, P n *)
-
-(** There's nothing magic about this induction lemma: it's just
- another Coq lemma that requires a proof. Coq generates the proof
- automatically too... *)
-
-Print nat_ind.
-Print nat_rect.
-(* ===> (after some manual inlining and tidying)
- nat_ind =
- fun (P : nat -> Prop)
- (f : P 0)
- (f0 : forall n : nat, P n -> P (S n)) =>
- fix F (n : nat) : P n :=
- match n with
- | 0 => f
- | S n0 => f0 n0 (F n0)
- end.
-*)
-
-(** We can read this as follows:
- Suppose we have evidence [f] that [P] holds on 0, and
- evidence [f0] that [forall n:nat, P n -> P (S n)].
- Then we can prove that [P] holds of an arbitrary nat [n] via
- a recursive function [F] (here defined using the expression
- form [Fix] rather than by a top-level [Fixpoint]
- declaration). [F] pattern matches on [n]:
- - If it finds 0, [F] uses [f] to show that [P n] holds.
- - If it finds [S n0], [F] applies itself recursively on [n0]
- to obtain evidence that [P n0] holds; then it applies [f0]
- on that evidence to show that [P (S n)] holds.
- [F] is just an ordinary recursive function that happens to
- operate on evidence in [Prop] rather than on terms in [Set].
-
-*)
-
-
-(** We can adapt this approach to proving [nat_ind] to help prove
- _non-standard_ induction principles too. Recall our desire to
- prove that
-
- [forall n : nat, even n -> ev n].
-
- Attempts to do this by standard induction on [n] fail, because the
- induction principle only lets us proceed when we can prove that
- [even n -> even (S n)] -- which is of course never provable. What
- we did in [Logic] was a bit of a hack:
-
- [Theorem even__ev : forall n : nat,
- (even n -> ev n) /\ (even (S n) -> ev (S n))].
-
- We can make a much better proof by defining and proving a
- non-standard induction principle that goes "by twos":
-
- *)
-
- Definition nat_ind2 :
- forall (P : nat -> Prop),
- P 0 ->
- P 1 ->
- (forall n : nat, P n -> P (S(S n))) ->
- forall n : nat , P n :=
- fun P => fun P0 => fun P1 => fun PSS =>
- fix f (n:nat) := match n with
- 0 => P0
- | 1 => P1
- | S (S n') => PSS n' (f n')
- end.
-
- (** Once you get the hang of it, it is entirely straightforward to
- give an explicit proof term for induction principles like this.
- Proving this as a lemma using tactics is much less intuitive (try
- it!).
-
- The [induction ... using] tactic variant gives a convenient way to
- specify a non-standard induction principle like this. *)
-
-Lemma even__ev' : forall n, even n -> ev n.
-Proof.
- intros.
- induction n as [ | |n'] using nat_ind2.
- Case "even 0".
- apply ev_0.
- Case "even 1".
- inversion H.
- Case "even (S(S n'))".
- apply ev_SS.
- apply IHn'. unfold even. unfold even in H. simpl in H. apply H.
-Qed.
-
-(* ######################################################### *)
-(** ** The Coq Trusted Computing Base *)
-
-(** One issue that arises with any automated proof assistant is "why
- trust it?": what if there is a bug in the implementation that
- renders all its reasoning suspect?
-
- While it is impossible to allay such concerns completely, the fact
- that Coq is based on the Curry-Howard correspondence gives it a
- strong foundation. Because propositions are just types and proofs
- are just terms, checking that an alleged proof of a proposition is
- valid just amounts to _type-checking_ the term. Type checkers are
- relatively small and straightforward programs, so the "trusted
- computing base" for Coq -- the part of the code that we have to
- believe is operating correctly -- is small too.
-
- What must a typechecker do? Its primary job is to make sure that
- in each function application the expected and actual argument
- types match, that the arms of a [match] expression are constructor
- patterns belonging to the inductive type being matched over and
- all arms of the [match] return the same type, and so on.
-
- There are a few additional wrinkles:
-
- - Since Coq types can themselves be expressions, the checker must
- normalize these (by using the computation rules) before
- comparing them.
-
- - The checker must make sure that [match] expressions are
- _exhaustive_. That is, there must be an arm for every possible
- constructor. To see why, consider the following alleged proof
- object:
- Definition or_bogus : forall P Q, P \/ Q -> P :=
- fun (P Q : Prop) (A : P \/ Q) =>
- match A with
- | or_introl H => H
- end.
- All the types here match correctly, but the [match] only
- considers one of the possible constructors for [or]. Coq's
- exhaustiveness check will reject this definition.
-
- - The checker must make sure that each [fix] expression
- terminates. It does this using a syntactic check to make sure
- that each recursive call is on a subexpression of the original
- argument. To see why this is essential, consider this alleged
- proof:
- Definition nat_false : forall (n:nat), False :=
- fix f (n:nat) : False := f n.
- Again, this is perfectly well-typed, but (fortunately) Coq will
- reject it. *)
-
-(** Note that the soundness of Coq depends only on the correctness of
- this typechecking engine, not on the tactic machinery. If there
- is a bug in a tactic implementation (and this certainly does
- happen!), that tactic might construct an invalid proof term. But
- when you type [Qed], Coq checks the term for validity from
- scratch. Only lemmas whose proofs pass the type-checker can be
- used in further proof developments. *)
-
-(* $Date: 2014-06-05 07:22:21 -0400 (Thu, 05 Jun 2014) $ *)
-
-
diff --git a/MoreLogic.html b/MoreLogic.html
deleted file mode 100644
index c0c11f2..0000000
--- a/MoreLogic.html
+++ /dev/null
@@ -1,787 +0,0 @@
-
-
-
-
-
-MoreLogic
-
-
-
-
-
-
-
-
-
-
-
-
-
-
MoreLogic
-
-
-
-
-
-
More Logic
-
-
-
-
-
-RequireExport "Prop".
-
-
-
-
-
-
Existential Quantification
-
-
-
- Another critical logical connective is existential
- quantification. We can express it with the following
- definition:
-
-That is, ex is a family of propositions indexed by a type X
- and a property P over X. In order to give evidence for the
- assertion "there exists an x for which the property P holds"
- we must actually name a witness — a specific value x — and
- then give evidence for Px, i.e., evidence that x has the
- property P.
-
-
-
-
-
-
-
- Coq's Notation facility can be used to introduce more
- familiar notation for writing existentially quantified
- propositions, exactly parallel to the built-in syntax for
- universally quantified propositions. Instead of writing exnat
- ev to express the proposition that there exists some number that
- is even, for example, we can write ∃x:nat,evx. (It is
- not necessary to understand exactly how the Notation definition
- works.)
-
- We can use the usual set of tactics for
- manipulating existentials. For example, to prove an
- existential, we can apply the constructor ex_intro. Since the
- premise of ex_intro involves a variable (witness) that does
- not appear in its conclusion, we need to explicitly give its value
- when we use apply.
-
- Conversely, if we have an existential hypothesis in the
- context, we can eliminate it with inversion. Note the use
- of the as... pattern to name the variable that Coq
- introduces to name the witness value and get evidence that
- the hypothesis holds for the witness. (If we don't
- explicitly choose one, Coq will just call it witness, which
- makes proofs confusing.)
-
-
-
-
-Theoremexists_example_2 : ∀n,
- (∃m, n = 4 + m) →
- (∃o, n = 2 + o).
-Proof.
- introsnH.
- inversionHas [mHm].
- ∃(2 + m).
- applyHm. Qed.
-
-
-
-
-
-Here is another example of how to work with existentials.
-
-
-Lemmaexists_example_3 :
- ∃(n:nat), evenn∧beautifuln.
-Proof.
-(* WORKED IN CLASS *)
- ∃8.
- split.
- unfoldeven. simpl. reflexivity.
- applyb_sumwith (n:=3) (m:=5).
- applyb_3. applyb_5.
-Qed.
-
-
-
-
-
-
Exercise: 1 star, optional (english_exists)
- In English, what does the proposition
-
-
-
-
- exnat (funn ⇒ beautiful (Sn))
-
-
-
- mean?
-
-
-
-
-(* FILL IN HERE *)
-
-
-(*
-*)
-
-
-
-
Exercise: 1 star (dist_not_exists)
- Prove that "P holds for all x" implies "there is no x for
- which P does not hold."
-
-
-
-
-Theoremdist_not_exists : ∀(X:Type) (P : X→Prop),
- (∀x, Px) → ¬ (∃x, ¬ Px).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (not_exists_dist)
- (The other direction of this theorem requires the classical "law
- of the excluded middle".)
-
-
-
-
-Theoremnot_exists_dist :
- excluded_middle→
- ∀(X:Type) (P : X→Prop),
- ¬ (∃x, ¬ Px) → (∀x, Px).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars (dist_exists_or)
- Prove that existential quantification distributes over
- disjunction.
-
-
-
-
-Theoremdist_exists_or : ∀(X:Type) (PQ : X→Prop),
- (∃x, Px∨Qx) ↔ (∃x, Px) ∨ (∃x, Qx).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Evidence-carrying booleans.
-
-
-
- So far we've seen two different forms of equality predicates:
-eq, which produces a Prop, and
-the type-specific forms, like beq_nat, that produce boolean
-values. The former are more convenient to reason about, but
-we've relied on the latter to let us use equality tests
-in computations. While it is straightforward to write lemmas
-(e.g. beq_nat_true and beq_nat_false) that connect the two forms,
-using these lemmas quickly gets tedious.
-
-
-
-
-
-
-
-It turns out that we can get the benefits of both forms at once
-by using a construct called sumbool.
-
-
-
-
-Inductivesumbool (AB : Prop) : Set :=
- | left : A→sumboolAB
- | right : B→sumboolAB.
-
-
-Notation "{ A } + { B }" := (sumboolAB) : type_scope.
-
-
-
-
-
-Think of sumbool as being like the boolean type, but instead
-of its values being just true and false, they carry evidence
-of truth or falsity. This means that when we destruct them, we
-are left with the relevant evidence as a hypothesis — just as with or.
-(In fact, the definition of sumbool is almost the same as for or.
-The only difference is that values of sumbool are declared to be in
-Set rather than in Prop; this is a technical distinction
-that allows us to compute with them.)
-
-
-
-
-
-
- Here's how we can define a sumbool for equality on nats
-
-Read as a theorem, this says that equality on nats is decidable:
-that is, given two nat values, we can always produce either
-evidence that they are equal or evidence that they are not.
-Read computationally, eq_nat_dec takes two nat values and returns
-a sumbool constructed with left if they are equal and right
-if they are not; this result can be tested with a match or, better,
-with an if-then-else, just like a regular boolean.
-(Notice that we ended this proof with Defined rather than Qed.
-The only difference this makes is that the proof becomes transparent,
-meaning that its definition is available when Coq tries to do reductions,
-which is important for the computational interpretation.)
-
-
-
-
-
-
-
-Here's a simple example illustrating the advantages of the sumbool form.
-
-Compare this to the more laborious proof (in MoreCoq.v) for the
- version of override defined using beq_nat, where we had to
- use the auxiliary lemma beq_nat_true to convert a fact about booleans
- to a Prop.
-
-
-
Exercise: 1 star (override_shadow')
-
-
-
-Theoremoverride_shadow' : ∀(X:Type) x1x2k1k2 (f : nat→X),
- (override' (override'fk1x2) k1x1) k2 = (override'fk1x1) k2.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 3 stars (all_forallb)
- Inductively define a property all of lists, parameterized by a
- type X and a property P:X→Prop, such that allXPl
- asserts that P is true for every element of the list l.
-
-
-
-
-Inductiveall (X : Type) (P : X→Prop) : listX→Prop :=
- (* FILL IN HERE *)
-.
-
-
-
-
-
-Recall the function forallb, from the exercise
- forall_exists_challenge in chapter Poly:
-
-Using the property all, write down a specification for forallb,
- and prove that it satisfies the specification. Try to make your
- specification as precise as possible.
-
-
-
- Are there any important properties of the function forallb which
- are not captured by your specification?
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 4 stars, advanced (filter_challenge)
- One of the main purposes of Coq is to prove that programs match
- their specifications. To this end, let's prove that our
- definition of filter matches a specification. Here is the
- specification, written out informally in English.
-
-
-
- Suppose we have a set X, a function test:X→bool, and a list
- l of type listX. Suppose further that l is an "in-order
- merge" of two lists, l1 and l2, such that every item in l1
- satisfies test and no item in l2 satisfies test. Then filter
- testl=l1.
-
-
-
- A list l is an "in-order merge" of l1 and l2 if it contains
- all the same elements as l1 and l2, in the same order as l1
- and l2, but possibly interleaved. For example,
-
-
-
-
- [1,4,6,2,3]
-
-
-
- is an in-order merge of
-
-
-
-
- [1,6,2]
-
-
-
- and
-
-
-
-
- [4,3].
-
-
-
- Your job is to translate this specification into a Coq theorem and
- prove it. (Hint: You'll need to begin by defining what it means
- for one list to be a merge of two others. Do this with an
- inductive relation, not a Fixpoint.)
-
- A different way to formally characterize the behavior of filter
- goes like this: Among all subsequences of l with the property
- that test evaluates to true on all their members, filtertest
- l is the longest. Express this claim formally and prove it.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 4 stars, advanced (no_repeats)
- The following inductively defined proposition...
-
-...gives us a precise way of saying that a value a appears at
- least once as a member of a list l.
-
-
-
- Here's a pair of warm-ups about appears_in.
-
-
-
-
-
-Lemmaappears_in_app : ∀(X:Type) (xsys : listX) (x:X),
- appears_inx (xs ++ ys) →appears_inxxs∨appears_inxys.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Lemmaapp_appears_in : ∀(X:Type) (xsys : listX) (x:X),
- appears_inxxs∨appears_inxys→appears_inx (xs ++ ys).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Now use appears_in to define a proposition disjointXl1l2,
- which should be provable exactly when l1 and l2 are
- lists (with elements of type X) that have no elements in common.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-Next, use appears_in to define an inductive proposition
- no_repeatsXl, which should be provable exactly when l is a
- list (with elements of type X) where every member is different
- from every other. For example, no_repeatsnat[1,2,3,4] and
- no_repeatsbool[] should be provable, while no_repeatsnat
- [1,2,1] and no_repeatsbool[true,true] should not be.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-Finally, state and prove one or more interesting theorems relating
- disjoint, no_repeats and ++ (list append).
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 3 stars (nostutter)
- Formulating inductive definitions of predicates is an important
- skill you'll need in this course. Try to solve this exercise
- without any help at all (except from your study group partner, if
- you have one).
-
-
-
- We say that a list of numbers "stutters" if it repeats the same
- number consecutively. The predicate "nostuttermylist" means
- that mylist does not stutter. Formulate an inductive definition
- for nostutter. (This is different from the no_repeats
- predicate in the exercise above; the sequence 1,4,1 repeats but
- does not stutter.)
-
-
-
-
-Inductivenostutter: listnat→Prop :=
- (* FILL IN HERE *)
-.
-
-
-
-
-
-Make sure each of these tests succeeds, but you are free
- to change the proof if the given one doesn't work for you.
- Your definition might be different from mine and still correct,
- in which case the examples might need a different proof.
-
-
-
- The suggested proofs for the examples (in comments) use a number
- of tactics we haven't talked about, to try to make them robust
- with respect to different possible ways of defining nostutter.
- You should be able to just uncomment and use them as-is, but if
- you prefer you can also prove each example with more basic
- tactics.
-
-
-
-
-Exampletest_nostutter_1: nostutter [3;1;4;1;5;6].
-(* FILL IN HERE *)Admitted.
-(*
- Proof. repeat constructor; apply beq_nat_false; auto. Qed.
-*)
-
-
-Exampletest_nostutter_2: nostutter [].
-(* FILL IN HERE *)Admitted.
-(*
- Proof. repeat constructor; apply beq_nat_false; auto. Qed.
-*)
-
-
-Exampletest_nostutter_3: nostutter [5].
-(* FILL IN HERE *)Admitted.
-(*
- Proof. repeat constructor; apply beq_nat_false; auto. Qed.
-*)
-
-
-Exampletest_nostutter_4: not (nostutter [3;1;1;4]).
-(* FILL IN HERE *)Admitted.
-(*
- Proof. intro.
- repeat match goal with
- h: nostutter _ |- _ => inversion h; clear h; subst
- end.
- contradiction H1; auto. Qed.
-*)
-
- The "pigeonhole principle" states a basic fact about counting:
- if you distribute more than n items into n pigeonholes, some
- pigeonhole must contain at least two items. As is often the case,
- this apparently trivial fact about numbers requires non-trivial
- machinery to prove, but we now have enough...
-
-
- First a pair of useful lemmas (we already proved these for lists
- of naturals, but not for arbitrary lists).
-
-
-
-
-Lemmaapp_length : ∀(X:Type) (l1l2 : listX),
- length (l1 ++ l2) = lengthl1 + lengthl2.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Lemmaappears_in_app_split : ∀(X:Type) (x:X) (l:listX),
- appears_inxl→
- ∃l1, ∃l2, l = l1 ++ (x::l2).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-Now define a predicate repeats (analogous to no_repeats in the
- exercise above), such that repeatsXl asserts that l contains
- at least one repeated element (of type X).
-
-
-
-
-Inductiverepeats {X:Type} : listX→Prop :=
- (* FILL IN HERE *)
-.
-
-
-
-
-
-Now here's a way to formalize the pigeonhole principle. List l2
- represents a list of pigeonhole labels, and list l1 represents
- the labels assigned to a list of items: if there are more items
- than labels, at least two items must have the same label. This
- proof is much easier if you use the excluded_middle hypothesis
- to show that appears_in is decidable, i.e. ∀x
- l,(appears_inxl)∨¬(appears_inxl). However, it is also
- possible to make the proof go through without assuming that
- appears_in is decidable; if you can manage to do this, you will
- not need the excluded_middle hypothesis.
-
-
-
-
-Theorempigeonhole_principle: ∀(X:Type) (l1l2:listX),
- excluded_middle→
- (∀x, appears_inxl1→appears_inxl2) →
- lengthl2 < lengthl1→
- repeatsl1.
-Proof.
- introsXl1. inductionl1as [|xl1'].
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-(* FILL IN HERE *)
-
-
-(* $Date: 2014-02-22 09:43:41 -0500 (Sat, 22 Feb 2014) $ *)
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/MoreLogic.v b/MoreLogic.v
deleted file mode 100644
index 5196f7a..0000000
--- a/MoreLogic.v
+++ /dev/null
@@ -1,473 +0,0 @@
-(** * More Logic *)
-
-Require Export "Prop".
-
-(* ############################################################ *)
-(** * Existential Quantification *)
-
-(** Another critical logical connective is _existential
- quantification_. We can express it with the following
- definition: *)
-
-Inductive ex (X:Type) (P : X->Prop) : Prop :=
- ex_intro : forall (witness:X), P witness -> ex X P.
-
-(** That is, [ex] is a family of propositions indexed by a type [X]
- and a property [P] over [X]. In order to give evidence for the
- assertion "there exists an [x] for which the property [P] holds"
- we must actually name a _witness_ -- a specific value [x] -- and
- then give evidence for [P x], i.e., evidence that [x] has the
- property [P].
-
-*)
-
-
-(** *** *)
-(** Coq's [Notation] facility can be used to introduce more
- familiar notation for writing existentially quantified
- propositions, exactly parallel to the built-in syntax for
- universally quantified propositions. Instead of writing [ex nat
- ev] to express the proposition that there exists some number that
- is even, for example, we can write [exists x:nat, ev x]. (It is
- not necessary to understand exactly how the [Notation] definition
- works.) *)
-
-Notation "'exists' x , p" := (ex _ (fun x => p))
- (at level 200, x ident, right associativity) : type_scope.
-Notation "'exists' x : X , p" := (ex _ (fun x:X => p))
- (at level 200, x ident, right associativity) : type_scope.
-
-(** *** *)
-(** We can use the usual set of tactics for
- manipulating existentials. For example, to prove an
- existential, we can [apply] the constructor [ex_intro]. Since the
- premise of [ex_intro] involves a variable ([witness]) that does
- not appear in its conclusion, we need to explicitly give its value
- when we use [apply]. *)
-
-Example exists_example_1 : exists n, n + (n * n) = 6.
-Proof.
- apply ex_intro with (witness:=2).
- reflexivity. Qed.
-
-(** Note that we have to explicitly give the witness. *)
-
-(** *** *)
-(** Or, instead of writing [apply ex_intro with (witness:=e)] all the
- time, we can use the convenient shorthand [exists e], which means
- the same thing. *)
-
-Example exists_example_1' : exists n, n + (n * n) = 6.
-Proof.
- exists 2.
- reflexivity. Qed.
-
-(** *** *)
-(** Conversely, if we have an existential hypothesis in the
- context, we can eliminate it with [inversion]. Note the use
- of the [as...] pattern to name the variable that Coq
- introduces to name the witness value and get evidence that
- the hypothesis holds for the witness. (If we don't
- explicitly choose one, Coq will just call it [witness], which
- makes proofs confusing.) *)
-
-Theorem exists_example_2 : forall n,
- (exists m, n = 4 + m) ->
- (exists o, n = 2 + o).
-Proof.
- intros n H.
- inversion H as [m Hm].
- exists (2 + m).
- apply Hm. Qed.
-
-
-(** Here is another example of how to work with existentials. *)
-Lemma exists_example_3 :
- exists (n:nat), even n /\ beautiful n.
-Proof.
-(* WORKED IN CLASS *)
- exists 8.
- split.
- unfold even. simpl. reflexivity.
- apply b_sum with (n:=3) (m:=5).
- apply b_3. apply b_5.
-Qed.
-
-(** **** Exercise: 1 star, optional (english_exists) *)
-(** In English, what does the proposition
- ex nat (fun n => beautiful (S n))
-]]
- mean? *)
-
-(* FILL IN HERE *)
-
-(*
-*)
-(** **** Exercise: 1 star (dist_not_exists) *)
-(** Prove that "[P] holds for all [x]" implies "there is no [x] for
- which [P] does not hold." *)
-
-Theorem dist_not_exists : forall (X:Type) (P : X -> Prop),
- (forall x, P x) -> ~ (exists x, ~ P x).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (not_exists_dist) *)
-(** (The other direction of this theorem requires the classical "law
- of the excluded middle".) *)
-
-Theorem not_exists_dist :
- excluded_middle ->
- forall (X:Type) (P : X -> Prop),
- ~ (exists x, ~ P x) -> (forall x, P x).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (dist_exists_or) *)
-(** Prove that existential quantification distributes over
- disjunction. *)
-
-Theorem dist_exists_or : forall (X:Type) (P Q : X -> Prop),
- (exists x, P x \/ Q x) <-> (exists x, P x) \/ (exists x, Q x).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** * Evidence-carrying booleans. *)
-
-(** So far we've seen two different forms of equality predicates:
-[eq], which produces a [Prop], and
-the type-specific forms, like [beq_nat], that produce [boolean]
-values. The former are more convenient to reason about, but
-we've relied on the latter to let us use equality tests
-in _computations_. While it is straightforward to write lemmas
-(e.g. [beq_nat_true] and [beq_nat_false]) that connect the two forms,
-using these lemmas quickly gets tedious.
-*)
-
-(** *** *)
-(**
-It turns out that we can get the benefits of both forms at once
-by using a construct called [sumbool]. *)
-
-Inductive sumbool (A B : Prop) : Set :=
- | left : A -> sumbool A B
- | right : B -> sumbool A B.
-
-Notation "{ A } + { B }" := (sumbool A B) : type_scope.
-
-(** Think of [sumbool] as being like the [boolean] type, but instead
-of its values being just [true] and [false], they carry _evidence_
-of truth or falsity. This means that when we [destruct] them, we
-are left with the relevant evidence as a hypothesis -- just as with [or].
-(In fact, the definition of [sumbool] is almost the same as for [or].
-The only difference is that values of [sumbool] are declared to be in
-[Set] rather than in [Prop]; this is a technical distinction
-that allows us to compute with them.) *)
-
-(** *** *)
-
-(** Here's how we can define a [sumbool] for equality on [nat]s *)
-
-Theorem eq_nat_dec : forall n m : nat, {n = m} + {n <> m}.
-Proof.
- (* WORKED IN CLASS *)
- intros n.
- induction n as [|n'].
- Case "n = 0".
- intros m.
- destruct m as [|m'].
- SCase "m = 0".
- left. reflexivity.
- SCase "m = S m'".
- right. intros contra. inversion contra.
- Case "n = S n'".
- intros m.
- destruct m as [|m'].
- SCase "m = 0".
- right. intros contra. inversion contra.
- SCase "m = S m'".
- destruct IHn' with (m := m') as [eq | neq].
- left. apply f_equal. apply eq.
- right. intros Heq. inversion Heq as [Heq']. apply neq. apply Heq'.
-Defined.
-
-(** Read as a theorem, this says that equality on [nat]s is decidable:
-that is, given two [nat] values, we can always produce either
-evidence that they are equal or evidence that they are not.
-Read computationally, [eq_nat_dec] takes two [nat] values and returns
-a [sumbool] constructed with [left] if they are equal and [right]
-if they are not; this result can be tested with a [match] or, better,
-with an [if-then-else], just like a regular [boolean].
-(Notice that we ended this proof with [Defined] rather than [Qed].
-The only difference this makes is that the proof becomes _transparent_,
-meaning that its definition is available when Coq tries to do reductions,
-which is important for the computational interpretation.)
-*)
-
-(** *** *)
-(**
-Here's a simple example illustrating the advantages of the [sumbool] form. *)
-
-Definition override' {X: Type} (f: nat->X) (k:nat) (x:X) : nat->X:=
- fun (k':nat) => if eq_nat_dec k k' then x else f k'.
-
-Theorem override_same' : forall (X:Type) x1 k1 k2 (f : nat->X),
- f k1 = x1 ->
- (override' f k1 x1) k2 = f k2.
-Proof.
- intros X x1 k1 k2 f. intros Hx1.
- unfold override'.
- destruct (eq_nat_dec k1 k2). (* observe what appears as a hypothesis *)
- Case "k1 = k2".
- rewrite <- e.
- symmetry. apply Hx1.
- Case "k1 <> k2".
- reflexivity. Qed.
-
-(** Compare this to the more laborious proof (in MoreCoq.v) for the
- version of [override] defined using [beq_nat], where we had to
- use the auxiliary lemma [beq_nat_true] to convert a fact about booleans
- to a Prop. *)
-
-
-(** **** Exercise: 1 star (override_shadow') *)
-Theorem override_shadow' : forall (X:Type) x1 x2 k1 k2 (f : nat->X),
- (override' (override' f k1 x2) k1 x1) k2 = (override' f k1 x1) k2.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-
-
-
-(* ####################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 3 stars (all_forallb) *)
-(** Inductively define a property [all] of lists, parameterized by a
- type [X] and a property [P : X -> Prop], such that [all X P l]
- asserts that [P] is true for every element of the list [l]. *)
-
-Inductive all (X : Type) (P : X -> Prop) : list X -> Prop :=
- (* FILL IN HERE *)
-.
-
-(** Recall the function [forallb], from the exercise
- [forall_exists_challenge] in chapter [Poly]: *)
-
-Fixpoint forallb {X : Type} (test : X -> bool) (l : list X) : bool :=
- match l with
- | [] => true
- | x :: l' => andb (test x) (forallb test l')
- end.
-
-(** Using the property [all], write down a specification for [forallb],
- and prove that it satisfies the specification. Try to make your
- specification as precise as possible.
-
- Are there any important properties of the function [forallb] which
- are not captured by your specification? *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (filter_challenge) *)
-(** One of the main purposes of Coq is to prove that programs match
- their specifications. To this end, let's prove that our
- definition of [filter] matches a specification. Here is the
- specification, written out informally in English.
-
- Suppose we have a set [X], a function [test: X->bool], and a list
- [l] of type [list X]. Suppose further that [l] is an "in-order
- merge" of two lists, [l1] and [l2], such that every item in [l1]
- satisfies [test] and no item in [l2] satisfies test. Then [filter
- test l = l1].
-
- A list [l] is an "in-order merge" of [l1] and [l2] if it contains
- all the same elements as [l1] and [l2], in the same order as [l1]
- and [l2], but possibly interleaved. For example,
- [1,4,6,2,3]
- is an in-order merge of
- [1,6,2]
- and
- [4,3].
- Your job is to translate this specification into a Coq theorem and
- prove it. (Hint: You'll need to begin by defining what it means
- for one list to be a merge of two others. Do this with an
- inductive relation, not a [Fixpoint].) *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 5 stars, advanced, optional (filter_challenge_2) *)
-(** A different way to formally characterize the behavior of [filter]
- goes like this: Among all subsequences of [l] with the property
- that [test] evaluates to [true] on all their members, [filter test
- l] is the longest. Express this claim formally and prove it. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (no_repeats) *)
-(** The following inductively defined proposition... *)
-
-Inductive appears_in {X:Type} (a:X) : list X -> Prop :=
- | ai_here : forall l, appears_in a (a::l)
- | ai_later : forall b l, appears_in a l -> appears_in a (b::l).
-
-(** ...gives us a precise way of saying that a value [a] appears at
- least once as a member of a list [l].
-
- Here's a pair of warm-ups about [appears_in].
-*)
-
-Lemma appears_in_app : forall (X:Type) (xs ys : list X) (x:X),
- appears_in x (xs ++ ys) -> appears_in x xs \/ appears_in x ys.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Lemma app_appears_in : forall (X:Type) (xs ys : list X) (x:X),
- appears_in x xs \/ appears_in x ys -> appears_in x (xs ++ ys).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Now use [appears_in] to define a proposition [disjoint X l1 l2],
- which should be provable exactly when [l1] and [l2] are
- lists (with elements of type X) that have no elements in common. *)
-
-(* FILL IN HERE *)
-
-(** Next, use [appears_in] to define an inductive proposition
- [no_repeats X l], which should be provable exactly when [l] is a
- list (with elements of type [X]) where every member is different
- from every other. For example, [no_repeats nat [1,2,3,4]] and
- [no_repeats bool []] should be provable, while [no_repeats nat
- [1,2,1]] and [no_repeats bool [true,true]] should not be. *)
-
-(* FILL IN HERE *)
-
-(** Finally, state and prove one or more interesting theorems relating
- [disjoint], [no_repeats] and [++] (list append). *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-
-(** **** Exercise: 3 stars (nostutter) *)
-(** Formulating inductive definitions of predicates is an important
- skill you'll need in this course. Try to solve this exercise
- without any help at all (except from your study group partner, if
- you have one).
-
- We say that a list of numbers "stutters" if it repeats the same
- number consecutively. The predicate "[nostutter mylist]" means
- that [mylist] does not stutter. Formulate an inductive definition
- for [nostutter]. (This is different from the [no_repeats]
- predicate in the exercise above; the sequence [1,4,1] repeats but
- does not stutter.) *)
-
-Inductive nostutter: list nat -> Prop :=
- (* FILL IN HERE *)
-.
-
-(** Make sure each of these tests succeeds, but you are free
- to change the proof if the given one doesn't work for you.
- Your definition might be different from mine and still correct,
- in which case the examples might need a different proof.
-
- The suggested proofs for the examples (in comments) use a number
- of tactics we haven't talked about, to try to make them robust
- with respect to different possible ways of defining [nostutter].
- You should be able to just uncomment and use them as-is, but if
- you prefer you can also prove each example with more basic
- tactics. *)
-
-Example test_nostutter_1: nostutter [3;1;4;1;5;6].
-(* FILL IN HERE *) Admitted.
-(*
- Proof. repeat constructor; apply beq_nat_false; auto. Qed.
-*)
-
-Example test_nostutter_2: nostutter [].
-(* FILL IN HERE *) Admitted.
-(*
- Proof. repeat constructor; apply beq_nat_false; auto. Qed.
-*)
-
-Example test_nostutter_3: nostutter [5].
-(* FILL IN HERE *) Admitted.
-(*
- Proof. repeat constructor; apply beq_nat_false; auto. Qed.
-*)
-
-Example test_nostutter_4: not (nostutter [3;1;1;4]).
-(* FILL IN HERE *) Admitted.
-(*
- Proof. intro.
- repeat match goal with
- h: nostutter _ |- _ => inversion h; clear h; subst
- end.
- contradiction H1; auto. Qed.
-*)
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (pigeonhole principle) *)
-(** The "pigeonhole principle" states a basic fact about counting:
- if you distribute more than [n] items into [n] pigeonholes, some
- pigeonhole must contain at least two items. As is often the case,
- this apparently trivial fact about numbers requires non-trivial
- machinery to prove, but we now have enough... *)
-
-(** First a pair of useful lemmas (we already proved these for lists
- of naturals, but not for arbitrary lists). *)
-
-Lemma app_length : forall (X:Type) (l1 l2 : list X),
- length (l1 ++ l2) = length l1 + length l2.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Lemma appears_in_app_split : forall (X:Type) (x:X) (l:list X),
- appears_in x l ->
- exists l1, exists l2, l = l1 ++ (x::l2).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Now define a predicate [repeats] (analogous to [no_repeats] in the
- exercise above), such that [repeats X l] asserts that [l] contains
- at least one repeated element (of type [X]). *)
-
-Inductive repeats {X:Type} : list X -> Prop :=
- (* FILL IN HERE *)
-.
-
-(** Now here's a way to formalize the pigeonhole principle. List [l2]
- represents a list of pigeonhole labels, and list [l1] represents
- the labels assigned to a list of items: if there are more items
- than labels, at least two items must have the same label. This
- proof is much easier if you use the [excluded_middle] hypothesis
- to show that [appears_in] is decidable, i.e. [forall x
- l, (appears_in x l) \/ ~ (appears_in x l)]. However, it is also
- possible to make the proof go through _without_ assuming that
- [appears_in] is decidable; if you can manage to do this, you will
- not need the [excluded_middle] hypothesis. *)
-
-Theorem pigeonhole_principle: forall (X:Type) (l1 l2:list X),
- excluded_middle ->
- (forall x, appears_in x l1 -> appears_in x l2) ->
- length l2 < length l1 ->
- repeats l1.
-Proof.
- intros X l1. induction l1 as [|x l1'].
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* FILL IN HERE *)
-
-
-(* $Date: 2014-02-22 09:43:41 -0500 (Sat, 22 Feb 2014) $ *)
diff --git a/MoreStlc.html b/MoreStlc.html
deleted file mode 100644
index a7350de..0000000
--- a/MoreStlc.html
+++ /dev/null
@@ -1,3084 +0,0 @@
-
-
-
-
-
-MoreStlc: More on the Simply Typed Lambda-Calculus
-
-
-
-
-
-
-
-
-
-
-
-
-
-
MoreStlcMore on the Simply Typed Lambda-Calculus
-
-
-
-
-
-
-
-
-
-
-RequireExportStlc.
-
-
-
-
-
-
Simple Extensions to STLC
-
-
-
- The simply typed lambda-calculus has enough structure to make its
- theoretical properties interesting, but it is not much of a
- programming language. In this chapter, we begin to close the gap
- with real-world languages by introducing a number of familiar
- features that have straightforward treatments at the level of
- typing.
-
-
-
Numbers
-
-
-
- Adding types, constants, and primitive operations for numbers is
- easy — just a matter of combining the Types and Stlc
- chapters.
-
-
-
let-bindings
-
-
-
- When writing a complex expression, it is often useful to give
- names to some of its subexpressions: this avoids repetition and
- often increases readability. Most languages provide one or more
- ways of doing this. In OCaml (and Coq), for example, we can write
- letx=t1int2 to mean ``evaluate the expression t1 and bind
- the name x to the resulting value while evaluating t2.''
-
-
-
- Our let-binder follows OCaml's in choosing a call-by-value
- evaluation order, where the let-bound term must be fully
- evaluated before evaluation of the let-body can begin. The
- typing rule T_Let tells us that the type of a let can be
- calculated by calculating the type of the let-bound term,
- extending the context with a binding with this type, and in this
- enriched context calculating the type of the body, which is then
- the type of the whole let expression.
-
-
-
- At this point in the course, it's probably easier simply to look
- at the rules defining this new feature as to wade through a lot of
- english text conveying the same information. Here they are:
-
-
- Syntax:
-
- t ::= Terms
- | ... (other terms same as before)
- | let x=t in t let-binding
-
-
-
-
-
-
-
- Reduction:
-
-
-
t1⇒ t1'
-
- (ST_Let1)
-
-
-
-
-
-
let x=t1 in t2⇒ let x=t1' in t2
-
-
-
-
-
-
- (ST_LetValue)
-
-
-
-
-
-
let x=v1 in t2⇒ [x:=v1]t2
-
-
-
Typing:
-
-
-
Γ⊢ t1 : T1Γ , x:T1⊢ t2 : T2
-
- (T_Let)
-
-
-
-
-
-
Γ⊢ let x=t1 in t2 : T2
-
-
-
-
-
-
Pairs
-
-
-
- Our functional programming examples in Coq have made
- frequent use of pairs of values. The type of such pairs is
- called a product type.
-
-
-
- The formalization of pairs is almost too simple to be worth
- discussing. However, let's look briefly at the various parts of
- the definition to emphasize the common pattern.
-
-
- In Coq, the primitive way of extracting the components of a pair
- is pattern matching. An alternative style is to take fst and
- snd — the first- and second-projection operators — as
- primitives. Just for fun, let's do our products this way. For
- example, here's how we'd write a function that takes a pair of
- numbers and returns the pair of their sum and difference:
-
- λx:Nat*Nat.
- let sum = x.fst + x.snd in
- let diff = x.fst - x.snd in
- (sum,diff)
-
-
-
-
- Adding pairs to the simply typed lambda-calculus, then, involves
- adding two new forms of term — pairing, written (t1,t2), and
- projection, written t.fst for the first projection from t and
- t.snd for the second projection — plus one new type constructor,
- T1×T2, called the product of T1 and T2.
-
-
- Syntax:
-
- t ::= Terms
- | ...
- | (t,t) pair
- | t.fst first projection
- | t.snd second projection
-
- v ::= Values
- | ...
- | (v,v) pair value
-
- T ::= Types
- | ...
- | T * T product type
-
-
-
-
- For evaluation, we need several new rules specifying how pairs and
- projection behave.
-
-
-
t1⇒ t1'
-
- (ST_Pair1)
-
-
-
-
-
-
(t1,t2) ⇒ (t1',t2)
-
-
-
-
-
t2⇒ t2'
-
- (ST_Pair2)
-
-
-
-
-
-
(v1,t2) ⇒ (v1,t2')
-
-
-
-
-
t1⇒ t1'
-
- (ST_Fst1)
-
-
-
-
-
-
t1.fst ⇒ t1'.fst
-
-
-
-
-
-
- (ST_FstPair)
-
-
-
-
-
-
(v1,v2).fst ⇒ v1
-
-
-
-
-
t1⇒ t1'
-
- (ST_Snd1)
-
-
-
-
-
-
t1.snd ⇒ t1'.snd
-
-
-
-
-
-
- (ST_SndPair)
-
-
-
-
-
-
(v1,v2).snd ⇒ v2
-
-
-
-
-
-
-
-
- Rules ST_FstPair and ST_SndPair specify that, when a fully
- evaluated pair meets a first or second projection, the result is
- the appropriate component. The congruence rules ST_Fst1 and
- ST_Snd1 allow reduction to proceed under projections, when the
- term being projected from has not yet been fully evaluated.
- ST_Pair1 and ST_Pair2 evaluate the parts of pairs: first the
- left part, and then — when a value appears on the left — the right
- part. The ordering arising from the use of the metavariables v
- and t in these rules enforces a left-to-right evaluation
- strategy for pairs. (Note the implicit convention that
- metavariables like v and v1 can only denote values.) We've
- also added a clause to the definition of values, above, specifying
- that (v1,v2) is a value. The fact that the components of a pair
- value must themselves be values ensures that a pair passed as an
- argument to a function will be fully evaluated before the function
- body starts executing.
-
-
- The typing rules for pairs and projections are straightforward.
-
-
-
Γ⊢ t1 : T1Γ⊢ t2 : T2
-
- (T_Pair)
-
-
-
-
-
-
Γ⊢ (t1,t2) : T1*T2
-
-
-
-
-
Γ⊢ t1 : T11*T12
-
- (T_Fst)
-
-
-
-
-
-
Γ⊢ t1.fst : T11
-
-
-
-
-
Γ⊢ t1 : T11*T12
-
- (T_Snd)
-
-
-
-
-
-
Γ⊢ t1.snd : T12
-
-
-
-
-
- The rule T_Pair says that (t1,t2) has type T1×T2 if t1 has
- type T1 and t2 has type T2. Conversely, the rules T_Fst
- and T_Snd tell us that, if t1 has a product type
- T11×T12 (i.e., if it will evaluate to a pair), then the types of
- the projections from this pair are T11 and T12.
-
-
-
Unit
-
-
-
- Another handy base type, found especially in languages in
- the ML family, is the singleton type Unit. It has a single element — the term constant unit (with a small
- u) — and a typing rule making unit an element of Unit. We
- also add unit to the set of possible result values of
- computations — indeed, unit is the only possible result of
- evaluating an expression of type Unit.
-
-
- Syntax:
-
- t ::= Terms
- | ...
- | unit unit value
-
- v ::= Values
- | ...
- | unit unit
-
- T ::= Types
- | ...
- | Unit Unit type
-
- Typing:
-
-
-
-
- (T_Unit)
-
-
-
-
-
-
Γ⊢ unit : Unit
-
-
-
-
-
- It may seem a little strange to bother defining a type that
- has just one element — after all, wouldn't every computation
- living in such a type be trivial?
-
-
-
- This is a fair question, and indeed in the STLC the Unit type is
- not especially critical (though we'll see two uses for it below).
- Where Unit really comes in handy is in richer languages with
- various sorts of side effects — e.g., assignment statements
- that mutate variables or pointers, exceptions and other sorts of
- nonlocal control structures, etc. In such languages, it is
- convenient to have a type for the (trivial) result of an
- expression that is evaluated only for its effect.
-
-
-
Sums
-
-
-
- Many programs need to deal with values that can take two distinct
- forms. For example, we might identify employees in an accounting
- application using using either their name or their id number.
- A search function might return either a matching value or an
- error code.
-
-
-
- These are specific examples of a binary sum type,
- which describes a set of values drawn from exactly two given types, e.g.
-
- Nat + Bool
-
-
-
-
- We create elements of these types by tagging elements of
- the component types. For example, if n is a Nat then inlv
- is an element of Nat+Bool; similarly, if b is a Bool then
- inrb is a Nat+Bool. The names of the tags inl and inr
- arise from thinking of them as functions
-
-
-
- that "inject" elements of Nat or Bool into the left and right
- components of the sum type Nat+Bool. (But note that we don't
- actually treat them as functions in the way we formalize them:
- inl and inr are keywords, and inlt and inrt are primitive
- syntactic forms, not function applications. This allows us to give
- them their own special typing rules.)
-
-
- In general, the elements of a type T1+T2 consist of the
- elements of T1 tagged with the token inl, plus the elements of
- T2 tagged with inr.
-
-
- One important usage of sums is signaling errors:
-
- div : Nat -> Nat -> (Nat + Unit) =
- div =
- λx:Nat. λy:Nat.
- if iszero y then
- inr unit
- else
- inl ...
-
- The type Nat+Unit above is in fact isomorphic to optionnat
- in Coq, and we've already seen how to signal errors with options.
-
-
- To use elements of sum types, we introduce a case
- construct (a very simplified form of Coq's match) to destruct
- them. For example, the following procedure converts a Nat+Bool
- into a Nat:
-
-
-
-
-
-
- getNat =
- λx:Nat+Bool.
- case x of
- inl n => n
- | inr b => if b then 1 else 0
-
-
-
-
- More formally...
-
-
- Syntax:
-
- t ::= Terms
- | ...
- | inl T t tagging (left)
- | inr T t tagging (right)
- | case t of case
- inl x => t
- | inr x => t
-
- v ::= Values
- | ...
- | inl T v tagged value (left)
- | inr T v tagged value (right)
-
- T ::= Types
- | ...
- | T + T sum type
-
-
-
-
- Evaluation:
-
-
-
-
-
-
t1⇒ t1'
-
- (ST_Inl)
-
-
-
-
-
-
inl T t1⇒ inl T t1'
-
-
-
-
-
t1⇒ t1'
-
- (ST_Inr)
-
-
-
-
-
-
inr T t1⇒ inr T t1'
-
-
-
-
-
t0 ⇒ t0'
-
- (ST_Case)
-
-
-
-
-
-
case t0 of inl x1 ⇒ t1 | inr x2 ⇒ t2⇒
-
-
-
-
case t0' of inl x1 ⇒ t1 | inr x2 ⇒ t2
-
-
-
-
-
-
- (ST_CaseInl)
-
-
-
-
-
-
case (inl T v0) of inl x1 ⇒ t1 | inr x2 ⇒ t2
-
-
-
-
⇒ [x1:=v0]t1
-
-
-
-
-
-
- (ST_CaseInr)
-
-
-
-
-
-
case (inr T v0) of inl x1 ⇒ t1 | inr x2 ⇒ t2
-
-
-
-
⇒ [x2:=v0]t2
-
-
-
-
-
- Typing:
-
-
-
Γ⊢ t1 : T1
-
- (T_Inl)
-
-
-
-
-
-
Γ⊢ inl T2 t1 : T1 + T2
-
-
-
-
-
Γ⊢ t1 : T2
-
- (T_Inr)
-
-
-
-
-
-
Γ⊢ inr T1 t1 : T1 + T2
-
-
-
-
-
Γ⊢ t0 : T1+T2
-
-
-
-
Γ , x1:T1⊢ t1 : T
-
-
-
-
Γ , x2:T2⊢ t2 : T
-
- (T_Case)
-
-
-
-
-
-
Γ⊢ case t0 of inl x1 ⇒ t1 | inr x2 ⇒ t2 : T
-
-
-
-
-
- We use the type annotation in inl and inr to make the typing
- simpler, similarly to what we did for functions. Without this extra
- information, the typing rule T_Inl, for example, would have to
- say that, once we have shown that t1 is an element of type T1,
- we can derive that inlt1 is an element of T1+T2 for any
- type T2. For example, we could derive both inl5:Nat+Nat
- and inl5:Nat+Bool (and infinitely many other types).
- This failure of uniqueness of types would mean that we cannot
- build a typechecking algorithm simply by "reading the rules from
- bottom to top" as we could for all the other features seen so far.
-
-
-
- There are various ways to deal with this difficulty. One simple
- one — which we've adopted here — forces the programmer to
- explicitly annotate the "other side" of a sum type when performing
- an injection. This is rather heavyweight for programmers (and so
- real languages adopt other solutions), but it is easy to
- understand and formalize.
-
-
-
Lists
-
-
-
- The typing features we have seen can be classified into base
- types like Bool, and type constructors like → and × that
- build new types from old ones. Another useful type constructor is
- List. For every type T, the type ListT describes
- finite-length lists whose elements are drawn from T.
-
-
-
- In principle, we could encode lists using pairs, sums and
- recursive types. But giving semantics to recursive types is
- non-trivial. Instead, we'll just discuss the special case of lists
- directly.
-
-
-
- Below we give the syntax, semantics, and typing rules for lists.
- Except for the fact that explicit type annotations are mandatory
- on nil and cannot appear on cons, these lists are essentially
- identical to those we built in Coq. We use lcase to destruct
- lists, to avoid dealing with questions like "what is the head of
- the empty list?"
-
-
- For example, here is a function that calculates the sum of
- the first two elements of a list of numbers:
-
- λx:List Nat.
- lcase x of nil -> 0
- | a::x' -> lcase x' of nil -> a
- | b::x'' -> a+b
-
-
-
-
-
-
-
- Syntax:
-
- t ::= Terms
- | ...
- | nil T
- | cons t t
- | lcase t of nil -> t | x::x -> t
-
- v ::= Values
- | ...
- | nil T nil value
- | cons v v cons value
-
- T ::= Types
- | ...
- | List T list of Ts
-
-
-
-
- Reduction:
-
-
-
t1⇒ t1'
-
- (ST_Cons1)
-
-
-
-
-
-
cons t1 t2⇒ cons t1' t2
-
-
-
-
-
t2⇒ t2'
-
- (ST_Cons2)
-
-
-
-
-
-
cons v1 t2⇒ cons v1 t2'
-
-
-
-
-
t1⇒ t1'
-
- (ST_Lcase1)
-
-
-
-
-
-
(lcase t1 of nil → t2 | xh::xt → t3) ⇒
-
-
-
-
(lcase t1' of nil → t2 | xh::xt → t3)
-
-
-
-
-
-
- (ST_LcaseNil)
-
-
-
-
-
-
(lcase nil T of nil → t2 | xh::xt → t3)
-
-
-
-
⇒ t2
-
-
-
-
-
-
- (ST_LcaseCons)
-
-
-
-
-
-
(lcase (cons vh vt) of nil → t2 | xh::xt → t3)
-
-
-
-
⇒ [xh:=vh,xt:=vt]t3
-
-
-
-
-
- Typing:
-
-
-
-
- (T_Nil)
-
-
-
-
-
-
Γ⊢ nil T : List T
-
-
-
-
-
Γ⊢ t1 : T Γ⊢ t2 : List T
-
- (T_Cons)
-
-
-
-
-
-
Γ⊢ cons t1 t2: List T
-
-
-
-
-
Γ⊢ t1 : List T1
-
-
-
-
Γ⊢ t2 : T
-
-
-
-
Γ , h:T1, t:List T1⊢ t3 : T
-
- (T_Lcase)
-
-
-
-
-
-
Γ⊢ (lcase t1 of nil → t2 | h::t → t3) : T
-
-
-
-
-
-
General Recursion
-
-
-
- Another facility found in most programming languages (including
- Coq) is the ability to define recursive functions. For example,
- we might like to be able to define the factorial function like
- this:
-
- fact = λx:Nat.
- if x=0 then 1 else x * (fact (pred x)))
-
- But this would require quite a bit of work to formalize: we'd have
- to introduce a notion of "function definitions" and carry around an
- "environment" of such definitions in the definition of the step
- relation.
-
-
- Here is another way that is straightforward to formalize: instead
- of writing recursive definitions where the right-hand side can
- contain the identifier being defined, we can define a fixed-point
- operator that performs the "unfolding" of the recursive definition
- in the right-hand side lazily during reduction.
-
- fact =
- fix
- (\f:Nat->Nat.
- λx:Nat.
- if x=0 then 1 else x * (f (pred x)))
-
-
-
-
- The intuition is that the higher-order function f passed
- to fix is a generator for the fact function: if fact is
- applied to a function that approximates the desired behavior of
- fact up to some number n (that is, a function that returns
- correct results on inputs less than or equal to n), then it
- returns a better approximation to fact — a function that returns
- correct results for inputs up to n+1. Applying fix to this
- generator returns its fixed point — a function that gives the
- desired behavior for all inputs n.
-
-
-
- (The term "fixed point" has exactly the same sense as in ordinary
- mathematics, where a fixed point of a function f is an input x
- such that f(x)=x. Here, a fixed point of a function F of
- type (say) (Nat→Nat)->(Nat→Nat) is a function f such that F
- f is behaviorally equivalent to f.)
-
-
- Syntax:
-
- t ::= Terms
- | ...
- | fix t fixed-point operator
-
- Reduction:
-
-
-
t1⇒ t1'
-
- (ST_Fix1)
-
-
-
-
-
-
fix t1⇒ fix t1'
-
-
-
-
-
F = \xf:T1.t2
-
- (ST_FixAbs)
-
-
-
-
-
-
fix F ⇒ [xf:=fix F]t2
-
-
-
Typing:
-
-
-
Γ⊢ t1 : T1->T1
-
- (T_Fix)
-
-
-
-
-
-
Γ⊢ fix t1 : T1
-
-
-
-
-
- Let's see how ST_FixAbs works by reducing fact3=fixF3,
- where F=(\f.\x.ifx=0then1elsex×(f(predx))) (we are
- omitting type annotations for brevity here).
-
-fix F 3
-
-⇒ST_FixAbs
-
-(\x. if x=0 then 1 else x * (fix F (pred x))) 3
-
-⇒ST_AppAbs
-
-if 3=0 then 1 else 3 * (fix F (pred 3))
-
-⇒ST_If0_Nonzero
-
-3 * (fix F (pred 3))
-
-⇒ST_FixAbs+ST_Mult2
-
-3 * ((\x. if x=0 then 1 else x * (fix F (pred x))) (pred 3))
-
-⇒ST_PredNat+ST_Mult2+ST_App2
-
-3 * ((\x. if x=0 then 1 else x * (fix F (pred x))) 2)
-
-⇒ST_AppAbs+ST_Mult2
-
-3 * (if 2=0 then 1 else 2 * (fix F (pred 2)))
-
-⇒ST_If0_Nonzero+ST_Mult2
-
-3 * (2 * (fix F (pred 2)))
-
-⇒ST_FixAbs+2xST_Mult2
-
-3 * (2 * ((\x. if x=0 then 1 else x * (fix F (pred x))) (pred 2)))
-
-⇒ST_PredNat+2xST_Mult2+ST_App2
-
-3 * (2 * ((\x. if x=0 then 1 else x * (fix F (pred x))) 1))
-
-⇒ST_AppAbs+2xST_Mult2
-
-3 * (2 * (if 1=0 then 1 else 1 * (fix F (pred 1))))
-
-⇒ST_If0_Nonzero+2xST_Mult2
-
-3 * (2 * (1 * (fix F (pred 1))))
-
-⇒ST_FixAbs+3xST_Mult2
-
-3 * (2 * (1 * ((\x. if x=0 then 1 else x * (fix F (pred x))) (pred 1))))
-
-⇒ST_PredNat+3xST_Mult2+ST_App2
-
-3 * (2 * (1 * ((\x. if x=0 then 1 else x * (fix F (pred x))) 0)))
-
-⇒ST_AppAbs+3xST_Mult2
-
-3 * (2 * (1 * (if 0=0 then 1 else 0 * (fix F (pred 0)))))
-
-⇒ST_If0Zero+3xST_Mult2
-
-3 * (2 * (1 * 1))
-
-⇒ST_MultNats+2xST_Mult2
-
-3 * (2 * 1)
-
-⇒ST_MultNats+ST_Mult2
-
-3 * 2
-
-⇒ST_MultNats
-
-6
-
-
-
-
-
Exercise: 1 star (halve_fix)
- Translate this informal recursive definition into one using fix:
-
- halve =
- λx:Nat.
- if x=0 then 0
- else if (pred x)=0 then 0
- else 1 + (halve (pred (pred x))))
-
-(* FILL IN HERE *)
-☐
-
-
-
-
Exercise: 1 star (fact_steps)
- Write down the sequence of steps that the term fact1 goes
- through to reduce to a normal form (assuming the usual reduction
- rules for arithmetic operations).
-
-
-
- (* FILL IN HERE *)
-☐
-
-
-
- The ability to form the fixed point of a function of type T→T
- for any T has some surprising consequences. In particular, it
- implies that every type is inhabited by some term. To see this,
- observe that, for every type T, we can define the term
-
-
-
-
- fix (\x:T.x)
-
-
-
- By T_Fix and T_Abs, this term has type T. By ST_FixAbs
- it reduces to itself, over and over again. Thus it is an
- undefined element of T.
-
-
-
- More usefully, here's an example using fix to define a
- two-argument recursive function:
-
- equal =
- fix
- (\eq:Nat->Nat->Bool.
- λm:Nat. λn:Nat.
- if m=0 then iszero n
- else if n=0 then false
- else eq (pred m) (pred n))
-
-
-
-
- And finally, here is an example where fix is used to define a
- pair of recursive functions (illustrating the fact that the type
- T1 in the rule T_Fix need not be a function type):
-
- evenodd =
- fix
- (\eo: (Nat->Bool * Nat->Bool).
- let e = λn:Nat. if n=0 then true else eo.snd (pred n) in
- let o = λn:Nat. if n=0 then false else eo.fst (pred n) in
- (e,o))
-
- even = evenodd.fst
- odd = evenodd.snd
-
-
-
-
-
-
-
-
-
-
Records
-
-
-
- As a final example of a basic extension of the STLC, let's
- look briefly at how to define records and their types.
- Intuitively, records can be obtained from pairs by two kinds of
- generalization: they are n-ary products (rather than just binary)
- and their fields are accessed by label (rather than position).
-
-
-
- Conceptually, this extension is a straightforward generalization
- of pairs and product types, but notationally it becomes a little
- heavier; for this reason, we postpone its formal treatment to a
- separate chapter (Records).
-
-
- Records are not included in the extended exercise below, but
- they will be useful to motivate the Sub chapter.
-
-
- Syntax:
-
- t ::= Terms
- | ...
- | {i1=t1, ..., in=tn} record
- | t.i projection
-
- v ::= Values
- | ...
- | {i1=v1, ..., in=vn} record value
-
- T ::= Types
- | ...
- | {i1:T1, ..., in:Tn} record type
-
- Intuitively, the generalization is pretty obvious. But it's worth
- noticing that what we've actually written is rather informal: in
- particular, we've written "..." in several places to mean "any
- number of these," and we've omitted explicit mention of the usual
- side-condition that the labels of a record should not contain
- repetitions.
-
-
-(* It is possible to devise informal notations that are
- more precise, but these tend to be quite heavy and to obscure the
- main points of the definitions. So we'll leave these a bit loose
- here (they are informal anyway, after all) and do the work of
- tightening things up elsewhere (in chapter Records). *)
-
-
-
-
-
- Reduction:
-
-
-
ti ⇒ ti'
-
- (ST_Rcd)
-
-
-
-
-
-
{i1=v1, ..., im=vm, in=ti, ...}
-
-
-
-
⇒ {i1=v1, ..., im=vm, in=ti', ...}
-
-
-
-
-
t1⇒ t1'
-
- (ST_Proj1)
-
-
-
-
-
-
t1.i ⇒ t1'.i
-
-
-
-
-
-
- (ST_ProjRcd)
-
-
-
-
-
-
{..., i=vi, ...}.i ⇒ vi
-
-
-
Again, these rules are a bit informal. For example, the first rule
- is intended to be read "if ti is the leftmost field that is not a
- value and if ti steps to ti', then the whole record steps..."
- In the last rule, the intention is that there should only be one
- field called i, and that all the other fields must contain values.
-
-
-
-
-
- Typing:
-
-
-
Γ⊢ t1 : T1 ... Γ⊢ tn : Tn
-
- (T_Rcd)
-
-
-
-
-
-
Γ⊢ {i1=t1, ..., in=tn} : {i1:T1, ..., in:Tn}
-
-
-
-
-
Γ⊢ t : {..., i:Ti, ...}
-
- (T_Proj)
-
-
-
-
-
-
Γ⊢ t.i : Ti
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Encoding Records (Optional)
-
-
-
- There are several ways to make the above definitions precise.
-
-
-
-
-
We can directly formalize the syntactic forms and inference
- rules, staying as close as possible to the form we've given
- them above. This is conceptually straightforward, and it's
- probably what we'd want to do if we were building a real
- compiler — in particular, it will allow is to print error
- messages in the form that programmers will find easy to
- understand. But the formal versions of the rules will not be
- pretty at all!
-
-
-
-
-
-
We could look for a smoother way of presenting records — for
- example, a binary presentation with one constructor for the
- empty record and another constructor for adding a single field
- to an existing record, instead of a single monolithic
- constructor that builds a whole record at once. This is the
- right way to go if we are primarily interested in studying the
- metatheory of the calculi with records, since it leads to
- clean and elegant definitions and proofs. Chapter Records
- shows how this can be done.
-
-
-
-
-
-
Alternatively, if we like, we can avoid formalizing records
- altogether, by stipulating that record notations are just
- informal shorthands for more complex expressions involving
- pairs and product types. We sketch this approach here.
-
-
-
-
-
-
- First, observe that we can encode arbitrary-size tuples using
- nested pairs and the unit value. To avoid overloading the pair
- notation (t1,t2), we'll use curly braces without labels to write
- down tuples, so {} is the empty tuple, {5} is a singleton
- tuple, {5,6} is a 2-tuple (morally the same as a pair),
- {5,6,7} is a triple, etc.
-
- {} ----> unit
- {t1, t2, ..., tn} ----> (t1, trest)
- where {t2, ..., tn} ----> trest
-
- Similarly, we can encode tuple types using nested product types:
-
- The operation of projecting a field from a tuple can be encoded
- using a sequence of second projections followed by a first projection:
-
- t.0 ----> t.fst
- t.(n+1) ----> (t.snd).n
-
-
-
-
- Next, suppose that there is some total ordering on record labels,
- so that we can associate each label with a unique natural number.
- This number is called the position of the label. For example,
- we might assign positions like this:
-
- LABEL POSITION
- a 0
- b 1
- c 2
- ... ...
- foo 1004
- ... ...
- bar 10562
- ... ...
-
-
-
-
- We use these positions to encode record values as tuples (i.e., as
- nested pairs) by sorting the fields according to their positions.
- For example:
-
- Note that each field appears in the position associated with its
- label, that the size of the tuple is determined by the label with
- the highest position, and that we fill in unused positions with
- unit.
-
-
-
- We do exactly the same thing with record types:
-
-
- Finally, record projection is encoded as a tuple projection from
- the appropriate position:
-
- t.l ----> t.(position of l)
-
-
-
-
- It is not hard to check that all the typing rules for the original
- "direct" presentation of records are validated by this
- encoding. (The reduction rules are "almost validated" — not
- quite, because the encoding reorders fields.)
-
-
- Of course, this encoding will not be very efficient if we
- happen to use a record with label bar! But things are not
- actually as bad as they might seem: for example, if we assume that
- our compiler can see the whole program at the same time, we can
- choose the numbering of labels so that we assign small positions
- to the most frequently used labels. Indeed, there are industrial
- compilers that essentially do this!
-
-
-
Variants (Optional Reading)
-
-
-
- Just as products can be generalized to records, sums can be
- generalized to n-ary labeled types called variants. Instead of
- T1+T2, we can write something like <l1:T1,l2:T2,...ln:Tn>
- where l1,l2,... are field labels which are used both to build
- instances and as case arm labels.
-
-
-
- These n-ary variants give us almost enough mechanism to build
- arbitrary inductive data types like lists and trees from
- scratch — the only thing missing is a way to allow recursion in
- type definitions. We won't cover this here, but detailed
- treatments can be found in many textbooks — e.g., Types and
- Programming Languages.
-
-
-
-
-
-
-
-
Exercise: Formalizing the Extensions
-
-
-
-
Exercise: 4 stars, advanced (STLC_extensions)
- In this problem you will formalize a couple of the extensions
- described above. We've provided the necessary additions to the
- syntax of terms and types, and we've included a few examples that
- you can test your definitions with to make sure they are working
- as expected. You'll fill in the rest of the definitions and
- extend all the proofs accordingly.
-
-
-
- To get you started, we've provided implementations for:
-
-
-
-
-
numbers
-
-
-
pairs and units
-
-
-
sums
-
-
-
lists
-
-
-
-
-
-
- You need to complete the implementations for:
-
-
-
-
-
let (which involves binding)
-
-
-
fix
-
-
-
-
-
-
- A good strategy is to work on the extensions one at a time, in
- multiple passes, rather than trying to work through the file from
- start to finish in a single pass. For each definition or proof,
- begin by reading carefully through the parts that are provided for
- you, referring to the text in the Stlc chapter for high-level
- intuitions and the embedded comments for detailed mechanics.
-
-
-Note that, for brevity, we've omitted booleans and instead
- provided a single if0 form combining a zero test and a
- conditional. That is, instead of writing
-
-
- This section presents formalized versions of the examples from
- above (plus several more). The ones at the beginning focus on
- specific features; you can use these to make sure your definition
- of a given feature is reasonable before moving on to extending the
- proofs later in the file with the cases relating to this feature.
- The later examples require all the features together, so you'll
- need to come back to these when you've got all the definitions
- filled in.
-
-
-
-
-ModuleExamples.
-
-
-
-
-
-
Preliminaries
-
-
-
- First, let's define a few variable names:
-
-
-
-
-Notationa := (Id 0).
-Notationf := (Id 1).
-Notationg := (Id 2).
-Notationl := (Id 3).
-Notationk := (Id 6).
-Notationi1 := (Id 7).
-Notationi2 := (Id 8).
-Notationx := (Id 9).
-Notationy := (Id 10).
-NotationprocessSum := (Id 11).
-Notationn := (Id 12).
-Notationeq := (Id 13).
-Notationm := (Id 14).
-Notationevenodd := (Id 15).
-Notationeven := (Id 16).
-Notationodd := (Id 17).
-Notationeo := (Id 18).
-
-
-
-
-
-Next, a bit of Coq hackery to automate searching for typing
- derivations. You don't need to understand this bit in detail —
- just have a look over it so that you'll know what to look for if
- you ever find yourself needing to make custom extensions to
- auto.
-
-
-
- The following Hint declarations say that, whenever auto
- arrives at a goal of the form (Γ⊢(tappe1e1)∈T), it
- should consider eapplyT_App, leaving an existential variable
- for the middle type T1, and similar for lcase. That variable
- will then be filled in during the search for type derivations for
- e1 and e2. We also include a hint to "try harder" when
- solving equality goals; this is useful to automate uses of
- T_Var (which includes an equality as a precondition).
-
-
-
-
-HintExtern 2 (has_type_ (tapp__) _) ⇒
- eapplyT_App; auto.
-(* You'll want to uncomment the following line once
- you've defined the T_Lcase constructor for the typing
- relation: *)
-(*
-Hint Extern 2 (has_type _ (tlcase _ _ _ _ _) _) =>
- eapply T_Lcase; auto.
-*)
-HintExtern 2 (_ = _) ⇒ compute; reflexivity.
-
-
-
-Remove the comment braces once you've implemented enough of the
- definitions that you think this should work.
-
-
-
-
-(*
-Example typechecks :
- (@empty ty) |- test ∈ TNat.
-Proof.
- unfold test.
- (* This typing derivation is quite deep, so we need to increase the
- max search depth of auto from the default 5 to 10. *)
- auto 10.
-Qed.
-
-Example numtest_reduces :
- test ==>* tnat 5.
-Proof.
- unfold test. normalize.
-Qed.
-*)
-
-
-EndNumtest.
-
-
-
-
- The proofs of progress and preservation for this system are
- essentially the same (though of course somewhat longer) as for the
- pure simply typed lambda-calculus.
-
-
-
-
-
-
-
-
Progress
-
-
-
-
-
-Theoremprogress : ∀tT,
- empty⊢t ∈ T→
- valuet∨∃t', t⇒t'.
-Proofwitheauto.
- (* Theorem: Suppose empty |- t : T. Then either
- 1. t is a value, or
- 2. t ==> t' for some t'.
- Proof: By induction on the given typing derivation. *)
- introstTHt.
- remember (@emptyty) asΓ.
- generalizedependentHeqGamma.
- has_type_cases (inductionHt) Case; introsHeqGamma; subst.
- Case "T_Var".
- (* The final rule in the given typing derivation cannot be T_Var,
- since it can never be the case that empty⊢x:T (since the
- context is empty). *)
- inversionH.
- Case "T_Abs".
- (* If the T_Abs rule was the last used, then t=tabsxT11t12,
- which is a value. *)
- left...
- Case "T_App".
- (* If the last rule applied was T_App, then t=t1t2, and we know
- from the form of the rule that
- empty⊢t1:T1→T2
- empty⊢t2:T1
- By the induction hypothesis, each of t1 and t2 either is a value
- or can take a step. *)
- right.
- destructIHHt1; subst...
- SCase "t1 is a value".
- destructIHHt2; subst...
- SSCase "t2 is a value".
- (* If both t1 and t2 are values, then we know that
- t1=tabsxT11t12, since abstractions are the only values
- that can have an arrow type. But
- (tabsxT11t12)t2⇒[x:=t2]t12 by ST_AppAbs. *)
- inversionH; subst; try (solvebyinversion).
- ∃(substxt2t12)...
- SSCase "t2 steps".
- (* If t1 is a value and t2⇒t2', then t1t2⇒t1t2'
- by ST_App2. *)
- inversionH0as [t2'Hstp]. ∃(tappt1t2')...
- SCase "t1 steps".
- (* Finally, If t1⇒t1', then t1t2⇒t1't2 by ST_App1. *)
- inversionHas [t1'Hstp]. ∃(tappt1't2)...
- Case "T_Nat".
- left...
- Case "T_Succ".
- right.
- destructIHHt...
- SCase "t1 is a value".
- inversionH; subst; trysolvebyinversion.
- ∃(tnat (Sn1))...
- SCase "t1 steps".
- inversionHas [t1'Hstp].
- ∃(tsucct1')...
- Case "T_Pred".
- right.
- destructIHHt...
- SCase "t1 is a value".
- inversionH; subst; trysolvebyinversion.
- ∃(tnat (predn1))...
- SCase "t1 steps".
- inversionHas [t1'Hstp].
- ∃(tpredt1')...
- Case "T_Mult".
- right.
- destructIHHt1...
- SCase "t1 is a value".
- destructIHHt2...
- SSCase "t2 is a value".
- inversionH; subst; trysolvebyinversion.
- inversionH0; subst; trysolvebyinversion.
- ∃(tnat (multn1n0))...
- SSCase "t2 steps".
- inversionH0as [t2'Hstp].
- ∃(tmultt1t2')...
- SCase "t1 steps".
- inversionHas [t1'Hstp].
- ∃(tmultt1't2)...
- Case "T_If0".
- right.
- destructIHHt1...
- SCase "t1 is a value".
- inversionH; subst; trysolvebyinversion.
- destructn1as [|n1'].
- SSCase "n1=0".
- ∃t2...
- SSCase "n1≠0".
- ∃t3...
- SCase "t1 steps".
- inversionHas [t1'H0].
- ∃(tif0t1't2t3)...
- Case "T_Pair".
- destructIHHt1...
- SCase "t1 is a value".
- destructIHHt2...
- SSCase "t2 steps".
- right. inversionH0as [t2'Hstp].
- ∃(tpairt1t2')...
- SCase "t1 steps".
- right. inversionHas [t1'Hstp].
- ∃(tpairt1't2)...
- Case "T_Fst".
- right.
- destructIHHt...
- SCase "t1 is a value".
- inversionH; subst; trysolvebyinversion.
- ∃v1...
- SCase "t1 steps".
- inversionHas [t1'Hstp].
- ∃(tfstt1')...
- Case "T_Snd".
- right.
- destructIHHt...
- SCase "t1 is a value".
- inversionH; subst; trysolvebyinversion.
- ∃v2...
- SCase "t1 steps".
- inversionHas [t1'Hstp].
- ∃(tsndt1')...
- Case "T_Unit".
- left...
-(* let *)
-(* FILL IN HERE *)
- Case "T_Inl".
- destructIHHt...
- SCase "t1 steps".
- right. inversionHas [t1'Hstp]...
- (* exists (tinl _ t1')... *)
- Case "T_Inr".
- destructIHHt...
- SCase "t1 steps".
- right. inversionHas [t1'Hstp]...
- (* exists (tinr _ t1')... *)
- Case "T_Case".
- right.
- destructIHHt1...
- SCase "t0 is a value".
- inversionH; subst; trysolvebyinversion.
- SSCase "t0 is inl".
- ∃([x1:=v]t1)...
- SSCase "t0 is inr".
- ∃([x2:=v]t2)...
- SCase "t0 steps".
- inversionHas [t0'Hstp].
- ∃(tcaset0'x1t1x2t2)...
- Case "T_Nil".
- left...
- Case "T_Cons".
- destructIHHt1...
- SCase "head is a value".
- destructIHHt2...
- SSCase "tail steps".
- right. inversionH0as [t2'Hstp].
- ∃(tconst1t2')...
- SCase "head steps".
- right. inversionHas [t1'Hstp].
- ∃(tconst1't2)...
- Case "T_Lcase".
- right.
- destructIHHt1...
- SCase "t1 is a value".
- inversionH; subst; trysolvebyinversion.
- SSCase "t1=tnil".
- ∃t2...
- SSCase "t1=tcons v1 vl".
- ∃([x2:=vl]([x1:=v1]t3))...
- SCase "t1 steps".
- inversionHas [t1'Hstp].
- ∃(tlcaset1't2x1x2t3)...
-(* fix *)
-(* FILL IN HERE *)
-Qed.
-
-
-
-
-
-Lemmasubstitution_preserves_typing : ∀ΓxUvtS,
- (extendΓxU) ⊢t ∈ S→
- empty⊢v ∈ U→
- Γ⊢ ([x:=v]t) ∈ S.
-Proofwitheauto.
- (* Theorem: If Gamma,x:U |- t : S and empty |- v : U, then
- Gamma |- x:=vt : S. *)
- introsΓxUvtSHtyptHtypv.
- generalizedependentΓ. generalizedependentS.
- (* Proof: By induction on the term t. Most cases follow directly
- from the IH, with the exception of tvar and tabs.
- The former aren't automatic because we must reason about how the
- variables interact. *)
- t_cases (inductiont) Case;
- introsSΓHtypt; simpl; inversionHtypt; subst...
- Case "tvar".
- simpl. renameiintoy.
- (* If t = y, we know that
- empty⊢v:U and
- Γ,x:U⊢y:S
- and, by inversion, extendΓxUy=SomeS. We want to
- show that Γ⊢[x:=v]y:S.
-
- There are two cases to consider: either x=y or x≠y. *)
- destruct (eq_id_decxy).
- SCase "x=y".
- (* If x=y, then we know that U=S, and that [x:=v]y=v.
- So what we really must show is that if empty⊢v:U then
- Γ⊢v:U. We have already proven a more general version
- of this theorem, called context invariance. *)
- subst.
- unfoldextendinH1. rewriteeq_idinH1.
- inversionH1; subst. clearH1.
- eapplycontext_invariance...
- introsxHcontra.
- destruct (free_in_context__SemptyHcontra) as [T'HT']...
- inversionHT'.
- SCase "x≠y".
- (* If x≠y, then Γy=SomeS and the substitution has no
- effect. We can show that Γ⊢y:S by T_Var. *)
- applyT_Var... unfoldextendinH1. rewriteneq_idinH1...
- Case "tabs".
- renameiintoy. renametintoT11.
- (* If t=tabsyT11t0, then we know that
- Γ,x:U⊢tabsyT11t0:T11→T12
- Γ,x:U,y:T11⊢t0:T12
- empty⊢v:U
- As our IH, we know that forall S Gamma,
- Γ,x:U⊢t0:S→Γ⊢[x:=v]t0:S.
-
- We can calculate that
- x:=vt = tabs y T11 (if beq_id x y then t0 else x:=vt0)
- And we must show that Γ⊢[x:=v]t:T11→T12. We know
- we will do so using T_Abs, so it remains to be shown that:
- Γ,y:T11⊢ifbeq_idxythent0else[x:=v]t0:T12
- We consider two cases: x=y and x≠y.
- *)
- applyT_Abs...
- destruct (eq_id_decxy).
- SCase "x=y".
- (* If x=y, then the substitution has no effect. Context
- invariance shows that Γ,y:U,y:T11 and Γ,y:T11 are
- equivalent. Since the former context shows that t0:T12, so
- does the latter. *)
- eapplycontext_invariance...
- subst.
- introsxHafi. unfoldextend.
- destruct (eq_id_decyx)...
- SCase "x≠y".
- (* If x≠y, then the IH and context invariance allow us to show that
- Γ,x:U,y:T11⊢t0:T12 =>
- Γ,y:T11,x:U⊢t0:T12 =>
- Γ,y:T11⊢[x:=v]t0:T12 *)
- applyIHt. eapplycontext_invariance...
- introszHafi. unfoldextend.
- destruct (eq_id_decyz)...
- subst. rewriteneq_id...
-(* let *)
-(* FILL IN HERE *)
- Case "tcase".
- renameiintox1. renamei0intox2.
- eapplyT_Case...
- SCase "left arm".
- destruct (eq_id_decxx1).
- SSCase "x = x1".
- eapplycontext_invariance...
- subst.
- introszHafi. unfoldextend.
- destruct (eq_id_decx1z)...
- SSCase "x ≠ x1".
- applyIHt2. eapplycontext_invariance...
- introszHafi. unfoldextend.
- destruct (eq_id_decx1z)...
- subst. rewriteneq_id...
- SCase "right arm".
- destruct (eq_id_decxx2).
- SSCase "x = x2".
- eapplycontext_invariance...
- subst.
- introszHafi. unfoldextend.
- destruct (eq_id_decx2z)...
- SSCase "x ≠ x2".
- applyIHt3. eapplycontext_invariance...
- introszHafi. unfoldextend.
- destruct (eq_id_decx2z)...
- subst. rewriteneq_id...
- Case "tlcase".
- renameiintoy1. renamei0intoy2.
- eapplyT_Lcase...
- destruct (eq_id_decxy1).
- SCase "x=y1".
- simpl.
- eapplycontext_invariance...
- subst.
- introszHafi. unfoldextend.
- destruct (eq_id_decy1z)...
- SCase "x≠y1".
- destruct (eq_id_decxy2).
- SSCase "x=y2".
- eapplycontext_invariance...
- subst.
- introszHafi. unfoldextend.
- destruct (eq_id_decy2z)...
- SSCase "x≠y2".
- applyIHt3. eapplycontext_invariance...
- introszHafi. unfoldextend.
- destruct (eq_id_decy1z)...
- subst. rewriteneq_id...
- destruct (eq_id_decy2z)...
- subst. rewriteneq_id...
-Qed.
-
-
-
-
-
-
Preservation
-
-
-
-
-
-Theorempreservation : ∀tt'T,
- empty⊢t ∈ T→
- t⇒t'→
- empty⊢t' ∈ T.
-Proofwitheauto.
- introstt'THT.
- (* Theorem: If empty⊢t:T and t⇒t', then empty⊢t':T. *)
- remember (@emptyty) asΓ. generalizedependentHeqGamma.
- generalizedependentt'.
- (* Proof: By induction on the given typing derivation. Many cases are
- contradictory (T_Var, T_Abs). We show just the interesting ones. *)
- has_type_cases (inductionHT) Case;
- introst'HeqGammaHE; subst; inversionHE; subst...
- Case "T_App".
- (* If the last rule used was T_App, then t=t1t2, and three rules
- could have been used to show t⇒t': ST_App1, ST_App2, and
- ST_AppAbs. In the first two cases, the result follows directly from
- the IH. *)
- inversionHE; subst...
- SCase "ST_AppAbs".
- (* For the third case, suppose
- t1=tabsxT11t12
- and
- t2=v2.
- We must show that empty⊢[x:=v2]t12:T2.
- We know by assumption that
- empty⊢tabsxT11t12:T1→T2
- and by inversion
- x:T1⊢t12:T2
- We have already proven that substitution_preserves_typing and
- empty⊢v2:T1
- by assumption, so we are done. *)
- applysubstitution_preserves_typingwithT1...
- inversionHT1...
- Case "T_Fst".
- inversionHT...
- Case "T_Snd".
- inversionHT...
-(* let *)
-(* FILL IN HERE *)
- Case "T_Case".
- SCase "ST_CaseInl".
- inversionHT1; subst.
- eapplysubstitution_preserves_typing...
- SCase "ST_CaseInr".
- inversionHT1; subst.
- eapplysubstitution_preserves_typing...
- Case "T_Lcase".
- SCase "ST_LcaseCons".
- inversionHT1; subst.
- applysubstitution_preserves_typingwith (TListT1)...
- applysubstitution_preserves_typingwithT1...
-(* fix *)
-(* FILL IN HERE *)
-Qed.
-
-
-
-
\ No newline at end of file
diff --git a/MoreStlc.v b/MoreStlc.v
deleted file mode 100644
index e739dbd..0000000
--- a/MoreStlc.v
+++ /dev/null
@@ -1,2234 +0,0 @@
-(** * MoreStlc: More on the Simply Typed Lambda-Calculus *)
-
-Require Export Stlc.
-
-(* ###################################################################### *)
-(** * Simple Extensions to STLC *)
-
-(** The simply typed lambda-calculus has enough structure to make its
- theoretical properties interesting, but it is not much of a
- programming language. In this chapter, we begin to close the gap
- with real-world languages by introducing a number of familiar
- features that have straightforward treatments at the level of
- typing. *)
-
-(** ** Numbers *)
-
-(** Adding types, constants, and primitive operations for numbers is
- easy -- just a matter of combining the [Types] and [Stlc]
- chapters. *)
-
-(** ** [let]-bindings *)
-
-(** When writing a complex expression, it is often useful to give
- names to some of its subexpressions: this avoids repetition and
- often increases readability. Most languages provide one or more
- ways of doing this. In OCaml (and Coq), for example, we can write
- [let x=t1 in t2] to mean ``evaluate the expression [t1] and bind
- the name [x] to the resulting value while evaluating [t2].''
-
- Our [let]-binder follows OCaml's in choosing a call-by-value
- evaluation order, where the [let]-bound term must be fully
- evaluated before evaluation of the [let]-body can begin. The
- typing rule [T_Let] tells us that the type of a [let] can be
- calculated by calculating the type of the [let]-bound term,
- extending the context with a binding with this type, and in this
- enriched context calculating the type of the body, which is then
- the type of the whole [let] expression.
-
- At this point in the course, it's probably easier simply to look
- at the rules defining this new feature as to wade through a lot of
- english text conveying the same information. Here they are: *)
-
-
-(** Syntax:
-<<
- t ::= Terms
- | ... (other terms same as before)
- | let x=t in t let-binding
->>
-*)
-
-(**
- Reduction:
- t1 ==> t1'
- ---------------------------------- (ST_Let1)
- let x=t1 in t2 ==> let x=t1' in t2
-
- ---------------------------- (ST_LetValue)
- let x=v1 in t2 ==> [x:=v1]t2
- Typing:
- Gamma |- t1 : T1 Gamma , x:T1 |- t2 : T2
- -------------------------------------------- (T_Let)
- Gamma |- let x=t1 in t2 : T2
- *)
-
-(** ** Pairs *)
-
-(** Our functional programming examples in Coq have made
- frequent use of _pairs_ of values. The type of such pairs is
- called a _product type_.
-
- The formalization of pairs is almost too simple to be worth
- discussing. However, let's look briefly at the various parts of
- the definition to emphasize the common pattern. *)
-
-(** In Coq, the primitive way of extracting the components of a pair
- is _pattern matching_. An alternative style is to take [fst] and
- [snd] -- the first- and second-projection operators -- as
- primitives. Just for fun, let's do our products this way. For
- example, here's how we'd write a function that takes a pair of
- numbers and returns the pair of their sum and difference:
-<<
- \x:Nat*Nat.
- let sum = x.fst + x.snd in
- let diff = x.fst - x.snd in
- (sum,diff)
->>
-*)
-
-(** Adding pairs to the simply typed lambda-calculus, then, involves
- adding two new forms of term -- pairing, written [(t1,t2)], and
- projection, written [t.fst] for the first projection from [t] and
- [t.snd] for the second projection -- plus one new type constructor,
- [T1*T2], called the _product_ of [T1] and [T2]. *)
-
-(** Syntax:
-<<
- t ::= Terms
- | ...
- | (t,t) pair
- | t.fst first projection
- | t.snd second projection
-
- v ::= Values
- | ...
- | (v,v) pair value
-
- T ::= Types
- | ...
- | T * T product type
->>
-*)
-
-(** For evaluation, we need several new rules specifying how pairs and
- projection behave.
- t1 ==> t1'
- -------------------- (ST_Pair1)
- (t1,t2) ==> (t1',t2)
-
- t2 ==> t2'
- -------------------- (ST_Pair2)
- (v1,t2) ==> (v1,t2')
-
- t1 ==> t1'
- ------------------ (ST_Fst1)
- t1.fst ==> t1'.fst
-
- ------------------ (ST_FstPair)
- (v1,v2).fst ==> v1
-
- t1 ==> t1'
- ------------------ (ST_Snd1)
- t1.snd ==> t1'.snd
-
- ------------------ (ST_SndPair)
- (v1,v2).snd ==> v2
-*)
-
-(**
- Rules [ST_FstPair] and [ST_SndPair] specify that, when a fully
- evaluated pair meets a first or second projection, the result is
- the appropriate component. The congruence rules [ST_Fst1] and
- [ST_Snd1] allow reduction to proceed under projections, when the
- term being projected from has not yet been fully evaluated.
- [ST_Pair1] and [ST_Pair2] evaluate the parts of pairs: first the
- left part, and then -- when a value appears on the left -- the right
- part. The ordering arising from the use of the metavariables [v]
- and [t] in these rules enforces a left-to-right evaluation
- strategy for pairs. (Note the implicit convention that
- metavariables like [v] and [v1] can only denote values.) We've
- also added a clause to the definition of values, above, specifying
- that [(v1,v2)] is a value. The fact that the components of a pair
- value must themselves be values ensures that a pair passed as an
- argument to a function will be fully evaluated before the function
- body starts executing. *)
-
-(** The typing rules for pairs and projections are straightforward.
- Gamma |- t1 : T1 Gamma |- t2 : T2
- --------------------------------------- (T_Pair)
- Gamma |- (t1,t2) : T1*T2
-
- Gamma |- t1 : T11*T12
- --------------------- (T_Fst)
- Gamma |- t1.fst : T11
-
- Gamma |- t1 : T11*T12
- --------------------- (T_Snd)
- Gamma |- t1.snd : T12
-*)
-
-(** The rule [T_Pair] says that [(t1,t2)] has type [T1*T2] if [t1] has
- type [T1] and [t2] has type [T2]. Conversely, the rules [T_Fst]
- and [T_Snd] tell us that, if [t1] has a product type
- [T11*T12] (i.e., if it will evaluate to a pair), then the types of
- the projections from this pair are [T11] and [T12]. *)
-
-(** ** Unit *)
-
-(** Another handy base type, found especially in languages in
- the ML family, is the singleton type [Unit]. *)
-(** It has a single element -- the term constant [unit] (with a small
- [u]) -- and a typing rule making [unit] an element of [Unit]. We
- also add [unit] to the set of possible result values of
- computations -- indeed, [unit] is the _only_ possible result of
- evaluating an expression of type [Unit]. *)
-
-(** Syntax:
-<<
- t ::= Terms
- | ...
- | unit unit value
-
- v ::= Values
- | ...
- | unit unit
-
- T ::= Types
- | ...
- | Unit Unit type
->>
- Typing:
- -------------------- (T_Unit)
- Gamma |- unit : Unit
-*)
-
-(** It may seem a little strange to bother defining a type that
- has just one element -- after all, wouldn't every computation
- living in such a type be trivial?
-
- This is a fair question, and indeed in the STLC the [Unit] type is
- not especially critical (though we'll see two uses for it below).
- Where [Unit] really comes in handy is in richer languages with
- various sorts of _side effects_ -- e.g., assignment statements
- that mutate variables or pointers, exceptions and other sorts of
- nonlocal control structures, etc. In such languages, it is
- convenient to have a type for the (trivial) result of an
- expression that is evaluated only for its effect. *)
-
-(** ** Sums *)
-
-(** Many programs need to deal with values that can take two distinct
- forms. For example, we might identify employees in an accounting
- application using using _either_ their name _or_ their id number.
- A search function might return _either_ a matching value _or_ an
- error code.
-
- These are specific examples of a binary _sum type_,
- which describes a set of values drawn from exactly two given types, e.g.
-<<
- Nat + Bool
->>
-*)
-
-
-
-(** We create elements of these types by _tagging_ elements of
- the component types. For example, if [n] is a [Nat] then [inl v]
- is an element of [Nat+Bool]; similarly, if [b] is a [Bool] then
- [inr b] is a [Nat+Bool]. The names of the tags [inl] and [inr]
- arise from thinking of them as functions
-
-<<
- inl : Nat -> Nat + Bool
- inr : Bool -> Nat + Bool
->>
-
- that "inject" elements of [Nat] or [Bool] into the left and right
- components of the sum type [Nat+Bool]. (But note that we don't
- actually treat them as functions in the way we formalize them:
- [inl] and [inr] are keywords, and [inl t] and [inr t] are primitive
- syntactic forms, not function applications. This allows us to give
- them their own special typing rules.) *)
-
-(** In general, the elements of a type [T1 + T2] consist of the
- elements of [T1] tagged with the token [inl], plus the elements of
- [T2] tagged with [inr]. *)
-
-(** One important usage of sums is signaling errors:
-<<
- div : Nat -> Nat -> (Nat + Unit) =
- div =
- \x:Nat. \y:Nat.
- if iszero y then
- inr unit
- else
- inl ...
->>
- The type [Nat + Unit] above is in fact isomorphic to [option nat]
- in Coq, and we've already seen how to signal errors with options. *)
-
-(** To _use_ elements of sum types, we introduce a [case]
- construct (a very simplified form of Coq's [match]) to destruct
- them. For example, the following procedure converts a [Nat+Bool]
- into a [Nat]: *)
-
-(**
-<<
- getNat =
- \x:Nat+Bool.
- case x of
- inl n => n
- | inr b => if b then 1 else 0
->>
-*)
-
-(** More formally... *)
-
-(** Syntax:
-<<
- t ::= Terms
- | ...
- | inl T t tagging (left)
- | inr T t tagging (right)
- | case t of case
- inl x => t
- | inr x => t
-
- v ::= Values
- | ...
- | inl T v tagged value (left)
- | inr T v tagged value (right)
-
- T ::= Types
- | ...
- | T + T sum type
->>
-*)
-
-(** Evaluation:
-
- t1 ==> t1'
- ---------------------- (ST_Inl)
- inl T t1 ==> inl T t1'
-
- t1 ==> t1'
- ---------------------- (ST_Inr)
- inr T t1 ==> inr T t1'
-
- t0 ==> t0'
- ------------------------------------------- (ST_Case)
- case t0 of inl x1 => t1 | inr x2 => t2 ==>
- case t0' of inl x1 => t1 | inr x2 => t2
-
- ---------------------------------------------- (ST_CaseInl)
- case (inl T v0) of inl x1 => t1 | inr x2 => t2
- ==> [x1:=v0]t1
-
- ---------------------------------------------- (ST_CaseInr)
- case (inr T v0) of inl x1 => t1 | inr x2 => t2
- ==> [x2:=v0]t2
-*)
-
-(** Typing:
- Gamma |- t1 : T1
- ---------------------------- (T_Inl)
- Gamma |- inl T2 t1 : T1 + T2
-
- Gamma |- t1 : T2
- ---------------------------- (T_Inr)
- Gamma |- inr T1 t1 : T1 + T2
-
- Gamma |- t0 : T1+T2
- Gamma , x1:T1 |- t1 : T
- Gamma , x2:T2 |- t2 : T
- --------------------------------------------------- (T_Case)
- Gamma |- case t0 of inl x1 => t1 | inr x2 => t2 : T
-
- We use the type annotation in [inl] and [inr] to make the typing
- simpler, similarly to what we did for functions. *)
-(** Without this extra
- information, the typing rule [T_Inl], for example, would have to
- say that, once we have shown that [t1] is an element of type [T1],
- we can derive that [inl t1] is an element of [T1 + T2] for _any_
- type T2. For example, we could derive both [inl 5 : Nat + Nat]
- and [inl 5 : Nat + Bool] (and infinitely many other types).
- This failure of uniqueness of types would mean that we cannot
- build a typechecking algorithm simply by "reading the rules from
- bottom to top" as we could for all the other features seen so far.
-
- There are various ways to deal with this difficulty. One simple
- one -- which we've adopted here -- forces the programmer to
- explicitly annotate the "other side" of a sum type when performing
- an injection. This is rather heavyweight for programmers (and so
- real languages adopt other solutions), but it is easy to
- understand and formalize. *)
-
-
-
-(** ** Lists *)
-
-(** The typing features we have seen can be classified into _base
- types_ like [Bool], and _type constructors_ like [->] and [*] that
- build new types from old ones. Another useful type constructor is
- [List]. For every type [T], the type [List T] describes
- finite-length lists whose elements are drawn from [T].
-
- In principle, we could encode lists using pairs, sums and
- _recursive_ types. But giving semantics to recursive types is
- non-trivial. Instead, we'll just discuss the special case of lists
- directly.
-
- Below we give the syntax, semantics, and typing rules for lists.
- Except for the fact that explicit type annotations are mandatory
- on [nil] and cannot appear on [cons], these lists are essentially
- identical to those we built in Coq. We use [lcase] to destruct
- lists, to avoid dealing with questions like "what is the [head] of
- the empty list?" *)
-
-(** For example, here is a function that calculates the sum of
- the first two elements of a list of numbers:
-<<
- \x:List Nat.
- lcase x of nil -> 0
- | a::x' -> lcase x' of nil -> a
- | b::x'' -> a+b
->>
-*)
-
-(**
- Syntax:
-<<
- t ::= Terms
- | ...
- | nil T
- | cons t t
- | lcase t of nil -> t | x::x -> t
-
- v ::= Values
- | ...
- | nil T nil value
- | cons v v cons value
-
- T ::= Types
- | ...
- | List T list of Ts
->>
-*)
-
-(** Reduction:
- t1 ==> t1'
- -------------------------- (ST_Cons1)
- cons t1 t2 ==> cons t1' t2
-
- t2 ==> t2'
- -------------------------- (ST_Cons2)
- cons v1 t2 ==> cons v1 t2'
-
- t1 ==> t1'
- ---------------------------------------- (ST_Lcase1)
- (lcase t1 of nil -> t2 | xh::xt -> t3) ==>
- (lcase t1' of nil -> t2 | xh::xt -> t3)
-
- ----------------------------------------- (ST_LcaseNil)
- (lcase nil T of nil -> t2 | xh::xt -> t3)
- ==> t2
-
- ----------------------------------------------- (ST_LcaseCons)
- (lcase (cons vh vt) of nil -> t2 | xh::xt -> t3)
- ==> [xh:=vh,xt:=vt]t3
-*)
-
-(** Typing:
- ----------------------- (T_Nil)
- Gamma |- nil T : List T
-
- Gamma |- t1 : T Gamma |- t2 : List T
- ----------------------------------------- (T_Cons)
- Gamma |- cons t1 t2: List T
-
- Gamma |- t1 : List T1
- Gamma |- t2 : T
- Gamma , h:T1, t:List T1 |- t3 : T
- ------------------------------------------------- (T_Lcase)
- Gamma |- (lcase t1 of nil -> t2 | h::t -> t3) : T
-*)
-
-
-(** ** General Recursion *)
-
-(** Another facility found in most programming languages (including
- Coq) is the ability to define recursive functions. For example,
- we might like to be able to define the factorial function like
- this:
-<<
- fact = \x:Nat.
- if x=0 then 1 else x * (fact (pred x)))
->>
- But this would require quite a bit of work to formalize: we'd have
- to introduce a notion of "function definitions" and carry around an
- "environment" of such definitions in the definition of the [step]
- relation. *)
-
-(** Here is another way that is straightforward to formalize: instead
- of writing recursive definitions where the right-hand side can
- contain the identifier being defined, we can define a _fixed-point
- operator_ that performs the "unfolding" of the recursive definition
- in the right-hand side lazily during reduction.
-<<
- fact =
- fix
- (\f:Nat->Nat.
- \x:Nat.
- if x=0 then 1 else x * (f (pred x)))
->>
-*)
-
-
-(** The intuition is that the higher-order function [f] passed
- to [fix] is a _generator_ for the [fact] function: if [fact] is
- applied to a function that approximates the desired behavior of
- [fact] up to some number [n] (that is, a function that returns
- correct results on inputs less than or equal to [n]), then it
- returns a better approximation to [fact] -- a function that returns
- correct results for inputs up to [n+1]. Applying [fix] to this
- generator returns its _fixed point_ -- a function that gives the
- desired behavior for all inputs [n].
-
- (The term "fixed point" has exactly the same sense as in ordinary
- mathematics, where a fixed point of a function [f] is an input [x]
- such that [f(x) = x]. Here, a fixed point of a function [F] of
- type (say) [(Nat->Nat)->(Nat->Nat)] is a function [f] such that [F
- f] is behaviorally equivalent to [f].) *)
-
-(** Syntax:
-<<
- t ::= Terms
- | ...
- | fix t fixed-point operator
->>
- Reduction:
- t1 ==> t1'
- ------------------ (ST_Fix1)
- fix t1 ==> fix t1'
-
- F = \xf:T1.t2
- ----------------------- (ST_FixAbs)
- fix F ==> [xf:=fix F]t2
- Typing:
- Gamma |- t1 : T1->T1
- -------------------- (T_Fix)
- Gamma |- fix t1 : T1
- *)
-
-(** Let's see how [ST_FixAbs] works by reducing [fact 3 = fix F 3],
- where [F = (\f. \x. if x=0 then 1 else x * (f (pred x)))] (we are
- omitting type annotations for brevity here).
-<<
-fix F 3
->>
-[==>] [ST_FixAbs]
-<<
-(\x. if x=0 then 1 else x * (fix F (pred x))) 3
->>
-[==>] [ST_AppAbs]
-<<
-if 3=0 then 1 else 3 * (fix F (pred 3))
->>
-[==>] [ST_If0_Nonzero]
-<<
-3 * (fix F (pred 3))
->>
-[==>] [ST_FixAbs + ST_Mult2]
-<<
-3 * ((\x. if x=0 then 1 else x * (fix F (pred x))) (pred 3))
->>
-[==>] [ST_PredNat + ST_Mult2 + ST_App2]
-<<
-3 * ((\x. if x=0 then 1 else x * (fix F (pred x))) 2)
->>
-[==>] [ST_AppAbs + ST_Mult2]
-<<
-3 * (if 2=0 then 1 else 2 * (fix F (pred 2)))
->>
-[==>] [ST_If0_Nonzero + ST_Mult2]
-<<
-3 * (2 * (fix F (pred 2)))
->>
-[==>] [ST_FixAbs + 2 x ST_Mult2]
-<<
-3 * (2 * ((\x. if x=0 then 1 else x * (fix F (pred x))) (pred 2)))
->>
-[==>] [ST_PredNat + 2 x ST_Mult2 + ST_App2]
-<<
-3 * (2 * ((\x. if x=0 then 1 else x * (fix F (pred x))) 1))
->>
-[==>] [ST_AppAbs + 2 x ST_Mult2]
-<<
-3 * (2 * (if 1=0 then 1 else 1 * (fix F (pred 1))))
->>
-[==>] [ST_If0_Nonzero + 2 x ST_Mult2]
-<<
-3 * (2 * (1 * (fix F (pred 1))))
->>
-[==>] [ST_FixAbs + 3 x ST_Mult2]
-<<
-3 * (2 * (1 * ((\x. if x=0 then 1 else x * (fix F (pred x))) (pred 1))))
->>
-[==>] [ST_PredNat + 3 x ST_Mult2 + ST_App2]
-<<
-3 * (2 * (1 * ((\x. if x=0 then 1 else x * (fix F (pred x))) 0)))
->>
-[==>] [ST_AppAbs + 3 x ST_Mult2]
-<<
-3 * (2 * (1 * (if 0=0 then 1 else 0 * (fix F (pred 0)))))
->>
-[==>] [ST_If0Zero + 3 x ST_Mult2]
-<<
-3 * (2 * (1 * 1))
->>
-[==>] [ST_MultNats + 2 x ST_Mult2]
-<<
-3 * (2 * 1)
->>
-[==>] [ST_MultNats + ST_Mult2]
-<<
-3 * 2
->>
-[==>] [ST_MultNats]
-<<
-6
->>
-*)
-
-
-(** **** Exercise: 1 star (halve_fix) *)
-(** Translate this informal recursive definition into one using [fix]:
-<<
- halve =
- \x:Nat.
- if x=0 then 0
- else if (pred x)=0 then 0
- else 1 + (halve (pred (pred x))))
->>
-(* FILL IN HERE *)
-[]
-*)
-
-(** **** Exercise: 1 star (fact_steps) *)
-(** Write down the sequence of steps that the term [fact 1] goes
- through to reduce to a normal form (assuming the usual reduction
- rules for arithmetic operations).
-
- (* FILL IN HERE *)
-[]
-*)
-
-(** The ability to form the fixed point of a function of type [T->T]
- for any [T] has some surprising consequences. In particular, it
- implies that _every_ type is inhabited by some term. To see this,
- observe that, for every type [T], we can define the term
- fix (\x:T.x)
- By [T_Fix] and [T_Abs], this term has type [T]. By [ST_FixAbs]
- it reduces to itself, over and over again. Thus it is an
- _undefined element_ of [T].
-
- More usefully, here's an example using [fix] to define a
- two-argument recursive function:
-<<
- equal =
- fix
- (\eq:Nat->Nat->Bool.
- \m:Nat. \n:Nat.
- if m=0 then iszero n
- else if n=0 then false
- else eq (pred m) (pred n))
->>
-
- And finally, here is an example where [fix] is used to define a
- _pair_ of recursive functions (illustrating the fact that the type
- [T1] in the rule [T_Fix] need not be a function type):
-<<
- evenodd =
- fix
- (\eo: (Nat->Bool * Nat->Bool).
- let e = \n:Nat. if n=0 then true else eo.snd (pred n) in
- let o = \n:Nat. if n=0 then false else eo.fst (pred n) in
- (e,o))
-
- even = evenodd.fst
- odd = evenodd.snd
->>
-*)
-
-(* ###################################################################### *)
-(** ** Records *)
-
-(** As a final example of a basic extension of the STLC, let's
- look briefly at how to define _records_ and their types.
- Intuitively, records can be obtained from pairs by two kinds of
- generalization: they are n-ary products (rather than just binary)
- and their fields are accessed by _label_ (rather than position).
-
- Conceptually, this extension is a straightforward generalization
- of pairs and product types, but notationally it becomes a little
- heavier; for this reason, we postpone its formal treatment to a
- separate chapter ([Records]). *)
-
-(** Records are not included in the extended exercise below, but
- they will be useful to motivate the [Sub] chapter. *)
-
-(** Syntax:
-<<
- t ::= Terms
- | ...
- | {i1=t1, ..., in=tn} record
- | t.i projection
-
- v ::= Values
- | ...
- | {i1=v1, ..., in=vn} record value
-
- T ::= Types
- | ...
- | {i1:T1, ..., in:Tn} record type
->>
- Intuitively, the generalization is pretty obvious. But it's worth
- noticing that what we've actually written is rather informal: in
- particular, we've written "[...]" in several places to mean "any
- number of these," and we've omitted explicit mention of the usual
- side-condition that the labels of a record should not contain
- repetitions. *)
-(* It is possible to devise informal notations that are
- more precise, but these tend to be quite heavy and to obscure the
- main points of the definitions. So we'll leave these a bit loose
- here (they are informal anyway, after all) and do the work of
- tightening things up elsewhere (in chapter [Records]). *)
-
-(**
- Reduction:
- ti ==> ti'
- ------------------------------------ (ST_Rcd)
- {i1=v1, ..., im=vm, in=ti, ...}
- ==> {i1=v1, ..., im=vm, in=ti', ...}
-
- t1 ==> t1'
- -------------- (ST_Proj1)
- t1.i ==> t1'.i
-
- ------------------------- (ST_ProjRcd)
- {..., i=vi, ...}.i ==> vi
- Again, these rules are a bit informal. For example, the first rule
- is intended to be read "if [ti] is the leftmost field that is not a
- value and if [ti] steps to [ti'], then the whole record steps..."
- In the last rule, the intention is that there should only be one
- field called i, and that all the other fields must contain values. *)
-
-(**
- Typing:
- Gamma |- t1 : T1 ... Gamma |- tn : Tn
- -------------------------------------------------- (T_Rcd)
- Gamma |- {i1=t1, ..., in=tn} : {i1:T1, ..., in:Tn}
-
- Gamma |- t : {..., i:Ti, ...}
- ----------------------------- (T_Proj)
- Gamma |- t.i : Ti
-
-*)
-
-(* ###################################################################### *)
-(** *** Encoding Records (Optional) *)
-
-(** There are several ways to make the above definitions precise.
-
- - We can directly formalize the syntactic forms and inference
- rules, staying as close as possible to the form we've given
- them above. This is conceptually straightforward, and it's
- probably what we'd want to do if we were building a real
- compiler -- in particular, it will allow is to print error
- messages in the form that programmers will find easy to
- understand. But the formal versions of the rules will not be
- pretty at all!
-
- - We could look for a smoother way of presenting records -- for
- example, a binary presentation with one constructor for the
- empty record and another constructor for adding a single field
- to an existing record, instead of a single monolithic
- constructor that builds a whole record at once. This is the
- right way to go if we are primarily interested in studying the
- metatheory of the calculi with records, since it leads to
- clean and elegant definitions and proofs. Chapter [Records]
- shows how this can be done.
-
- - Alternatively, if we like, we can avoid formalizing records
- altogether, by stipulating that record notations are just
- informal shorthands for more complex expressions involving
- pairs and product types. We sketch this approach here.
-
- First, observe that we can encode arbitrary-size tuples using
- nested pairs and the [unit] value. To avoid overloading the pair
- notation [(t1,t2)], we'll use curly braces without labels to write
- down tuples, so [{}] is the empty tuple, [{5}] is a singleton
- tuple, [{5,6}] is a 2-tuple (morally the same as a pair),
- [{5,6,7}] is a triple, etc.
-<<
- {} ----> unit
- {t1, t2, ..., tn} ----> (t1, trest)
- where {t2, ..., tn} ----> trest
->>
- Similarly, we can encode tuple types using nested product types:
-<<
- {} ----> Unit
- {T1, T2, ..., Tn} ----> T1 * TRest
- where {T2, ..., Tn} ----> TRest
->>
- The operation of projecting a field from a tuple can be encoded
- using a sequence of second projections followed by a first projection:
-<<
- t.0 ----> t.fst
- t.(n+1) ----> (t.snd).n
->>
-
- Next, suppose that there is some total ordering on record labels,
- so that we can associate each label with a unique natural number.
- This number is called the _position_ of the label. For example,
- we might assign positions like this:
-<<
- LABEL POSITION
- a 0
- b 1
- c 2
- ... ...
- foo 1004
- ... ...
- bar 10562
- ... ...
->>
-
- We use these positions to encode record values as tuples (i.e., as
- nested pairs) by sorting the fields according to their positions.
- For example:
-<<
- {a=5, b=6} ----> {5,6}
- {a=5, c=7} ----> {5,unit,7}
- {c=7, a=5} ----> {5,unit,7}
- {c=5, b=3} ----> {unit,3,5}
- {f=8,c=5,a=7} ----> {7,unit,5,unit,unit,8}
- {f=8,c=5} ----> {unit,unit,5,unit,unit,8}
->>
- Note that each field appears in the position associated with its
- label, that the size of the tuple is determined by the label with
- the highest position, and that we fill in unused positions with
- [unit].
-
- We do exactly the same thing with record types:
-<<
- {a:Nat, b:Nat} ----> {Nat,Nat}
- {c:Nat, a:Nat} ----> {Nat,Unit,Nat}
- {f:Nat,c:Nat} ----> {Unit,Unit,Nat,Unit,Unit,Nat}
->>
-
- Finally, record projection is encoded as a tuple projection from
- the appropriate position:
-<<
- t.l ----> t.(position of l)
->>
-
- It is not hard to check that all the typing rules for the original
- "direct" presentation of records are validated by this
- encoding. (The reduction rules are "almost validated" -- not
- quite, because the encoding reorders fields.) *)
-
-(** Of course, this encoding will not be very efficient if we
- happen to use a record with label [bar]! But things are not
- actually as bad as they might seem: for example, if we assume that
- our compiler can see the whole program at the same time, we can
- _choose_ the numbering of labels so that we assign small positions
- to the most frequently used labels. Indeed, there are industrial
- compilers that essentially do this! *)
-
-(** *** Variants (Optional Reading) *)
-
-(** Just as products can be generalized to records, sums can be
- generalized to n-ary labeled types called _variants_. Instead of
- [T1+T2], we can write something like []
- where [l1],[l2],... are field labels which are used both to build
- instances and as case arm labels.
-
- These n-ary variants give us almost enough mechanism to build
- arbitrary inductive data types like lists and trees from
- scratch -- the only thing missing is a way to allow _recursion_ in
- type definitions. We won't cover this here, but detailed
- treatments can be found in many textbooks -- e.g., Types and
- Programming Languages. *)
-
-(* ###################################################################### *)
-(** * Exercise: Formalizing the Extensions *)
-
-(** **** Exercise: 4 stars, advanced (STLC_extensions) *)
-(** In this problem you will formalize a couple of the extensions
- described above. We've provided the necessary additions to the
- syntax of terms and types, and we've included a few examples that
- you can test your definitions with to make sure they are working
- as expected. You'll fill in the rest of the definitions and
- extend all the proofs accordingly.
-
- To get you started, we've provided implementations for:
- - numbers
- - pairs and units
- - sums
- - lists
-
- You need to complete the implementations for:
- - let (which involves binding)
- - [fix]
-
- A good strategy is to work on the extensions one at a time, in
- multiple passes, rather than trying to work through the file from
- start to finish in a single pass. For each definition or proof,
- begin by reading carefully through the parts that are provided for
- you, referring to the text in the [Stlc] chapter for high-level
- intuitions and the embedded comments for detailed mechanics.
-*)
-
-Module STLCExtended.
-
-(* ###################################################################### *)
-(** *** Syntax and Operational Semantics *)
-
-Inductive ty : Type :=
- | TArrow : ty -> ty -> ty
- | TNat : ty
- | TUnit : ty
- | TProd : ty -> ty -> ty
- | TSum : ty -> ty -> ty
- | TList : ty -> ty.
-
-Tactic Notation "T_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "TArrow" | Case_aux c "TNat"
- | Case_aux c "TProd" | Case_aux c "TUnit"
- | Case_aux c "TSum" | Case_aux c "TList" ].
-
-Inductive tm : Type :=
- (* pure STLC *)
- | tvar : id -> tm
- | tapp : tm -> tm -> tm
- | tabs : id -> ty -> tm -> tm
- (* numbers *)
- | tnat : nat -> tm
- | tsucc : tm -> tm
- | tpred : tm -> tm
- | tmult : tm -> tm -> tm
- | tif0 : tm -> tm -> tm -> tm
- (* pairs *)
- | tpair : tm -> tm -> tm
- | tfst : tm -> tm
- | tsnd : tm -> tm
- (* units *)
- | tunit : tm
- (* let *)
- | tlet : id -> tm -> tm -> tm
- (* i.e., [let x = t1 in t2] *)
- (* sums *)
- | tinl : ty -> tm -> tm
- | tinr : ty -> tm -> tm
- | tcase : tm -> id -> tm -> id -> tm -> tm
- (* i.e., [case t0 of inl x1 => t1 | inr x2 => t2] *)
- (* lists *)
- | tnil : ty -> tm
- | tcons : tm -> tm -> tm
- | tlcase : tm -> tm -> id -> id -> tm -> tm
- (* i.e., [lcase t1 of | nil -> t2 | x::y -> t3] *)
- (* fix *)
- | tfix : tm -> tm.
-
-(** Note that, for brevity, we've omitted booleans and instead
- provided a single [if0] form combining a zero test and a
- conditional. That is, instead of writing
-<<
- if x = 0 then ... else ...
->>
- we'll write this:
-<<
- if0 x then ... else ...
->>
-*)
-
-Tactic Notation "t_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "tvar" | Case_aux c "tapp" | Case_aux c "tabs"
- | Case_aux c "tnat" | Case_aux c "tsucc" | Case_aux c "tpred"
- | Case_aux c "tmult" | Case_aux c "tif0"
- | Case_aux c "tpair" | Case_aux c "tfst" | Case_aux c "tsnd"
- | Case_aux c "tunit" | Case_aux c "tlet"
- | Case_aux c "tinl" | Case_aux c "tinr" | Case_aux c "tcase"
- | Case_aux c "tnil" | Case_aux c "tcons" | Case_aux c "tlcase"
- | Case_aux c "tfix" ].
-
-(* ###################################################################### *)
-(** *** Substitution *)
-
-Fixpoint subst (x:id) (s:tm) (t:tm) : tm :=
- match t with
- | tvar y =>
- if eq_id_dec x y then s else t
- | tabs y T t1 =>
- tabs y T (if eq_id_dec x y then t1 else (subst x s t1))
- | tapp t1 t2 =>
- tapp (subst x s t1) (subst x s t2)
- | tnat n =>
- tnat n
- | tsucc t1 =>
- tsucc (subst x s t1)
- | tpred t1 =>
- tpred (subst x s t1)
- | tmult t1 t2 =>
- tmult (subst x s t1) (subst x s t2)
- | tif0 t1 t2 t3 =>
- tif0 (subst x s t1) (subst x s t2) (subst x s t3)
- | tpair t1 t2 =>
- tpair (subst x s t1) (subst x s t2)
- | tfst t1 =>
- tfst (subst x s t1)
- | tsnd t1 =>
- tsnd (subst x s t1)
- | tunit => tunit
- (* FILL IN HERE *)
- | tinl T t1 =>
- tinl T (subst x s t1)
- | tinr T t1 =>
- tinr T (subst x s t1)
- | tcase t0 y1 t1 y2 t2 =>
- tcase (subst x s t0)
- y1 (if eq_id_dec x y1 then t1 else (subst x s t1))
- y2 (if eq_id_dec x y2 then t2 else (subst x s t2))
- | tnil T =>
- tnil T
- | tcons t1 t2 =>
- tcons (subst x s t1) (subst x s t2)
- | tlcase t1 t2 y1 y2 t3 =>
- tlcase (subst x s t1) (subst x s t2) y1 y2
- (if eq_id_dec x y1 then
- t3
- else if eq_id_dec x y2 then t3
- else (subst x s t3))
-(* FILL IN HERE *)
- | _ => t (* ... and delete this line *)
- end.
-
-Notation "'[' x ':=' s ']' t" := (subst x s t) (at level 20).
-
-
-(* ###################################################################### *)
-(** *** Reduction *)
-
-(** Next we define the values of our language. *)
-
-Inductive value : tm -> Prop :=
- | v_abs : forall x T11 t12,
- value (tabs x T11 t12)
- (* Numbers are values: *)
- | v_nat : forall n1,
- value (tnat n1)
- (* A pair is a value if both components are: *)
- | v_pair : forall v1 v2,
- value v1 ->
- value v2 ->
- value (tpair v1 v2)
- (* A unit is always a value *)
- | v_unit : value tunit
- (* A tagged value is a value: *)
- | v_inl : forall v T,
- value v ->
- value (tinl T v)
- | v_inr : forall v T,
- value v ->
- value (tinr T v)
- (* A list is a value iff its head and tail are values: *)
- | v_lnil : forall T, value (tnil T)
- | v_lcons : forall v1 vl,
- value v1 ->
- value vl ->
- value (tcons v1 vl)
- .
-
-Hint Constructors value.
-
-Reserved Notation "t1 '==>' t2" (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_AppAbs : forall x T11 t12 v2,
- value v2 ->
- (tapp (tabs x T11 t12) v2) ==> [x:=v2]t12
- | ST_App1 : forall t1 t1' t2,
- t1 ==> t1' ->
- (tapp t1 t2) ==> (tapp t1' t2)
- | ST_App2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- (tapp v1 t2) ==> (tapp v1 t2')
- (* nats *)
- | ST_Succ1 : forall t1 t1',
- t1 ==> t1' ->
- (tsucc t1) ==> (tsucc t1')
- | ST_SuccNat : forall n1,
- (tsucc (tnat n1)) ==> (tnat (S n1))
- | ST_Pred : forall t1 t1',
- t1 ==> t1' ->
- (tpred t1) ==> (tpred t1')
- | ST_PredNat : forall n1,
- (tpred (tnat n1)) ==> (tnat (pred n1))
- | ST_Mult1 : forall t1 t1' t2,
- t1 ==> t1' ->
- (tmult t1 t2) ==> (tmult t1' t2)
- | ST_Mult2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- (tmult v1 t2) ==> (tmult v1 t2')
- | ST_MultNats : forall n1 n2,
- (tmult (tnat n1) (tnat n2)) ==> (tnat (mult n1 n2))
- | ST_If01 : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- (tif0 t1 t2 t3) ==> (tif0 t1' t2 t3)
- | ST_If0Zero : forall t2 t3,
- (tif0 (tnat 0) t2 t3) ==> t2
- | ST_If0Nonzero : forall n t2 t3,
- (tif0 (tnat (S n)) t2 t3) ==> t3
- (* pairs *)
- | ST_Pair1 : forall t1 t1' t2,
- t1 ==> t1' ->
- (tpair t1 t2) ==> (tpair t1' t2)
- | ST_Pair2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- (tpair v1 t2) ==> (tpair v1 t2')
- | ST_Fst1 : forall t1 t1',
- t1 ==> t1' ->
- (tfst t1) ==> (tfst t1')
- | ST_FstPair : forall v1 v2,
- value v1 ->
- value v2 ->
- (tfst (tpair v1 v2)) ==> v1
- | ST_Snd1 : forall t1 t1',
- t1 ==> t1' ->
- (tsnd t1) ==> (tsnd t1')
- | ST_SndPair : forall v1 v2,
- value v1 ->
- value v2 ->
- (tsnd (tpair v1 v2)) ==> v2
- (* let *)
- (* FILL IN HERE *)
- (* sums *)
- | ST_Inl : forall t1 t1' T,
- t1 ==> t1' ->
- (tinl T t1) ==> (tinl T t1')
- | ST_Inr : forall t1 t1' T,
- t1 ==> t1' ->
- (tinr T t1) ==> (tinr T t1')
- | ST_Case : forall t0 t0' x1 t1 x2 t2,
- t0 ==> t0' ->
- (tcase t0 x1 t1 x2 t2) ==> (tcase t0' x1 t1 x2 t2)
- | ST_CaseInl : forall v0 x1 t1 x2 t2 T,
- value v0 ->
- (tcase (tinl T v0) x1 t1 x2 t2) ==> [x1:=v0]t1
- | ST_CaseInr : forall v0 x1 t1 x2 t2 T,
- value v0 ->
- (tcase (tinr T v0) x1 t1 x2 t2) ==> [x2:=v0]t2
- (* lists *)
- | ST_Cons1 : forall t1 t1' t2,
- t1 ==> t1' ->
- (tcons t1 t2) ==> (tcons t1' t2)
- | ST_Cons2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- (tcons v1 t2) ==> (tcons v1 t2')
- | ST_Lcase1 : forall t1 t1' t2 x1 x2 t3,
- t1 ==> t1' ->
- (tlcase t1 t2 x1 x2 t3) ==> (tlcase t1' t2 x1 x2 t3)
- | ST_LcaseNil : forall T t2 x1 x2 t3,
- (tlcase (tnil T) t2 x1 x2 t3) ==> t2
- | ST_LcaseCons : forall v1 vl t2 x1 x2 t3,
- value v1 ->
- value vl ->
- (tlcase (tcons v1 vl) t2 x1 x2 t3) ==> (subst x2 vl (subst x1 v1 t3))
- (* fix *)
-(* FILL IN HERE *)
-
-where "t1 '==>' t2" := (step t1 t2).
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_AppAbs" | Case_aux c "ST_App1" | Case_aux c "ST_App2"
- | Case_aux c "ST_Succ1" | Case_aux c "ST_SuccNat"
- | Case_aux c "ST_Pred1" | Case_aux c "ST_PredNat"
- | Case_aux c "ST_Mult1" | Case_aux c "ST_Mult2"
- | Case_aux c "ST_MultNats" | Case_aux c "ST_If01"
- | Case_aux c "ST_If0Zero" | Case_aux c "ST_If0Nonzero"
- | Case_aux c "ST_Pair1" | Case_aux c "ST_Pair2"
- | Case_aux c "ST_Fst1" | Case_aux c "ST_FstPair"
- | Case_aux c "ST_Snd1" | Case_aux c "ST_SndPair"
- (* FILL IN HERE *)
- | Case_aux c "ST_Inl" | Case_aux c "ST_Inr" | Case_aux c "ST_Case"
- | Case_aux c "ST_CaseInl" | Case_aux c "ST_CaseInr"
- | Case_aux c "ST_Cons1" | Case_aux c "ST_Cons2" | Case_aux c "ST_Lcase1"
- | Case_aux c "ST_LcaseNil" | Case_aux c "ST_LcaseCons"
-(* FILL IN HERE *)
- ].
-
-Notation multistep := (multi step).
-Notation "t1 '==>*' t2" := (multistep t1 t2) (at level 40).
-
-Hint Constructors step.
-
-(* ###################################################################### *)
-(** *** Typing *)
-
-Definition context := partial_map ty.
-
-(** Next we define the typing rules. These are nearly direct
- transcriptions of the inference rules shown above. *)
-
-Reserved Notation "Gamma '|-' t '\in' T" (at level 40).
-
-Inductive has_type : context -> tm -> ty -> Prop :=
- (* Typing rules for proper terms *)
- | T_Var : forall Gamma x T,
- Gamma x = Some T ->
- Gamma |- (tvar x) \in T
- | T_Abs : forall Gamma x T11 T12 t12,
- (extend Gamma x T11) |- t12 \in T12 ->
- Gamma |- (tabs x T11 t12) \in (TArrow T11 T12)
- | T_App : forall T1 T2 Gamma t1 t2,
- Gamma |- t1 \in (TArrow T1 T2) ->
- Gamma |- t2 \in T1 ->
- Gamma |- (tapp t1 t2) \in T2
- (* nats *)
- | T_Nat : forall Gamma n1,
- Gamma |- (tnat n1) \in TNat
- | T_Succ : forall Gamma t1,
- Gamma |- t1 \in TNat ->
- Gamma |- (tsucc t1) \in TNat
- | T_Pred : forall Gamma t1,
- Gamma |- t1 \in TNat ->
- Gamma |- (tpred t1) \in TNat
- | T_Mult : forall Gamma t1 t2,
- Gamma |- t1 \in TNat ->
- Gamma |- t2 \in TNat ->
- Gamma |- (tmult t1 t2) \in TNat
- | T_If0 : forall Gamma t1 t2 t3 T1,
- Gamma |- t1 \in TNat ->
- Gamma |- t2 \in T1 ->
- Gamma |- t3 \in T1 ->
- Gamma |- (tif0 t1 t2 t3) \in T1
- (* pairs *)
- | T_Pair : forall Gamma t1 t2 T1 T2,
- Gamma |- t1 \in T1 ->
- Gamma |- t2 \in T2 ->
- Gamma |- (tpair t1 t2) \in (TProd T1 T2)
- | T_Fst : forall Gamma t T1 T2,
- Gamma |- t \in (TProd T1 T2) ->
- Gamma |- (tfst t) \in T1
- | T_Snd : forall Gamma t T1 T2,
- Gamma |- t \in (TProd T1 T2) ->
- Gamma |- (tsnd t) \in T2
- (* unit *)
- | T_Unit : forall Gamma,
- Gamma |- tunit \in TUnit
- (* let *)
-(* FILL IN HERE *)
- (* sums *)
- | T_Inl : forall Gamma t1 T1 T2,
- Gamma |- t1 \in T1 ->
- Gamma |- (tinl T2 t1) \in (TSum T1 T2)
- | T_Inr : forall Gamma t2 T1 T2,
- Gamma |- t2 \in T2 ->
- Gamma |- (tinr T1 t2) \in (TSum T1 T2)
- | T_Case : forall Gamma t0 x1 T1 t1 x2 T2 t2 T,
- Gamma |- t0 \in (TSum T1 T2) ->
- (extend Gamma x1 T1) |- t1 \in T ->
- (extend Gamma x2 T2) |- t2 \in T ->
- Gamma |- (tcase t0 x1 t1 x2 t2) \in T
- (* lists *)
- | T_Nil : forall Gamma T,
- Gamma |- (tnil T) \in (TList T)
- | T_Cons : forall Gamma t1 t2 T1,
- Gamma |- t1 \in T1 ->
- Gamma |- t2 \in (TList T1) ->
- Gamma |- (tcons t1 t2) \in (TList T1)
- | T_Lcase : forall Gamma t1 T1 t2 x1 x2 t3 T2,
- Gamma |- t1 \in (TList T1) ->
- Gamma |- t2 \in T2 ->
- (extend (extend Gamma x2 (TList T1)) x1 T1) |- t3 \in T2 ->
- Gamma |- (tlcase t1 t2 x1 x2 t3) \in T2
- (* fix *)
-(* FILL IN HERE *)
-
-where "Gamma '|-' t '\in' T" := (has_type Gamma t T).
-
-Hint Constructors has_type.
-
-Tactic Notation "has_type_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "T_Var" | Case_aux c "T_Abs" | Case_aux c "T_App"
- | Case_aux c "T_Nat" | Case_aux c "T_Succ" | Case_aux c "T_Pred"
- | Case_aux c "T_Mult" | Case_aux c "T_If0"
- | Case_aux c "T_Pair" | Case_aux c "T_Fst" | Case_aux c "T_Snd"
- | Case_aux c "T_Unit"
-(* let *)
-(* FILL IN HERE *)
- | Case_aux c "T_Inl" | Case_aux c "T_Inr" | Case_aux c "T_Case"
- | Case_aux c "T_Nil" | Case_aux c "T_Cons" | Case_aux c "T_Lcase"
-(* fix *)
-(* FILL IN HERE *)
-].
-
-(* ###################################################################### *)
-(** ** Examples *)
-
-(** This section presents formalized versions of the examples from
- above (plus several more). The ones at the beginning focus on
- specific features; you can use these to make sure your definition
- of a given feature is reasonable before moving on to extending the
- proofs later in the file with the cases relating to this feature.
- The later examples require all the features together, so you'll
- need to come back to these when you've got all the definitions
- filled in. *)
-
-Module Examples.
-
-(** *** Preliminaries *)
-
-(** First, let's define a few variable names: *)
-
-Notation a := (Id 0).
-Notation f := (Id 1).
-Notation g := (Id 2).
-Notation l := (Id 3).
-Notation k := (Id 6).
-Notation i1 := (Id 7).
-Notation i2 := (Id 8).
-Notation x := (Id 9).
-Notation y := (Id 10).
-Notation processSum := (Id 11).
-Notation n := (Id 12).
-Notation eq := (Id 13).
-Notation m := (Id 14).
-Notation evenodd := (Id 15).
-Notation even := (Id 16).
-Notation odd := (Id 17).
-Notation eo := (Id 18).
-
-(** Next, a bit of Coq hackery to automate searching for typing
- derivations. You don't need to understand this bit in detail --
- just have a look over it so that you'll know what to look for if
- you ever find yourself needing to make custom extensions to
- [auto].
-
- The following [Hint] declarations say that, whenever [auto]
- arrives at a goal of the form [(Gamma |- (tapp e1 e1) \in T)], it
- should consider [eapply T_App], leaving an existential variable
- for the middle type T1, and similar for [lcase]. That variable
- will then be filled in during the search for type derivations for
- [e1] and [e2]. We also include a hint to "try harder" when
- solving equality goals; this is useful to automate uses of
- [T_Var] (which includes an equality as a precondition). *)
-
-Hint Extern 2 (has_type _ (tapp _ _) _) =>
- eapply T_App; auto.
-(* You'll want to uncomment the following line once
- you've defined the [T_Lcase] constructor for the typing
- relation: *)
-(*
-Hint Extern 2 (has_type _ (tlcase _ _ _ _ _) _) =>
- eapply T_Lcase; auto.
-*)
-Hint Extern 2 (_ = _) => compute; reflexivity.
-
-(** *** Numbers *)
-
-Module Numtest.
-
-(* if0 (pred (succ (pred (2 * 0))) then 5 else 6 *)
-Definition test :=
- tif0
- (tpred
- (tsucc
- (tpred
- (tmult
- (tnat 2)
- (tnat 0)))))
- (tnat 5)
- (tnat 6).
-
-(** Remove the comment braces once you've implemented enough of the
- definitions that you think this should work. *)
-
-(*
-Example typechecks :
- (@empty ty) |- test \in TNat.
-Proof.
- unfold test.
- (* This typing derivation is quite deep, so we need to increase the
- max search depth of [auto] from the default 5 to 10. *)
- auto 10.
-Qed.
-
-Example numtest_reduces :
- test ==>* tnat 5.
-Proof.
- unfold test. normalize.
-Qed.
-*)
-
-End Numtest.
-
-(** *** Products *)
-
-Module Prodtest.
-
-(* ((5,6),7).fst.snd *)
-Definition test :=
- tsnd
- (tfst
- (tpair
- (tpair
- (tnat 5)
- (tnat 6))
- (tnat 7))).
-
-(*
-Example typechecks :
- (@empty ty) |- test \in TNat.
-Proof. unfold test. eauto 15. Qed.
-
-Example reduces :
- test ==>* tnat 6.
-Proof. unfold test. normalize. Qed.
-*)
-
-End Prodtest.
-
-(** *** [let] *)
-
-Module LetTest.
-
-(* let x = pred 6 in succ x *)
-Definition test :=
- tlet
- x
- (tpred (tnat 6))
- (tsucc (tvar x)).
-
-(*
-Example typechecks :
- (@empty ty) |- test \in TNat.
-Proof. unfold test. eauto 15. Qed.
-
-Example reduces :
- test ==>* tnat 6.
-Proof. unfold test. normalize. Qed.
-*)
-
-End LetTest.
-
-(** *** Sums *)
-
-Module Sumtest1.
-
-(* case (inl Nat 5) of
- inl x => x
- | inr y => y *)
-
-Definition test :=
- tcase (tinl TNat (tnat 5))
- x (tvar x)
- y (tvar y).
-
-(*
-Example typechecks :
- (@empty ty) |- test \in TNat.
-Proof. unfold test. eauto 15. Qed.
-
-Example reduces :
- test ==>* (tnat 5).
-Proof. unfold test. normalize. Qed.
-*)
-
-End Sumtest1.
-
-Module Sumtest2.
-
-(* let processSum =
- \x:Nat+Nat.
- case x of
- inl n => n
- inr n => if0 n then 1 else 0 in
- (processSum (inl Nat 5), processSum (inr Nat 5)) *)
-
-Definition test :=
- tlet
- processSum
- (tabs x (TSum TNat TNat)
- (tcase (tvar x)
- n (tvar n)
- n (tif0 (tvar n) (tnat 1) (tnat 0))))
- (tpair
- (tapp (tvar processSum) (tinl TNat (tnat 5)))
- (tapp (tvar processSum) (tinr TNat (tnat 5)))).
-
-(*
-Example typechecks :
- (@empty ty) |- test \in (TProd TNat TNat).
-Proof. unfold test. eauto 15. Qed.
-
-Example reduces :
- test ==>* (tpair (tnat 5) (tnat 0)).
-Proof. unfold test. normalize. Qed.
-*)
-
-End Sumtest2.
-
-(** *** Lists *)
-
-Module ListTest.
-
-(* let l = cons 5 (cons 6 (nil Nat)) in
- lcase l of
- nil => 0
- | x::y => x*x *)
-
-Definition test :=
- tlet l
- (tcons (tnat 5) (tcons (tnat 6) (tnil TNat)))
- (tlcase (tvar l)
- (tnat 0)
- x y (tmult (tvar x) (tvar x))).
-
-(*
-Example typechecks :
- (@empty ty) |- test \in TNat.
-Proof. unfold test. eauto 20. Qed.
-
-Example reduces :
- test ==>* (tnat 25).
-Proof. unfold test. normalize. Qed.
-*)
-
-End ListTest.
-
-(** *** [fix] *)
-
-Module FixTest1.
-
-(* fact := fix
- (\f:nat->nat.
- \a:nat.
- if a=0 then 1 else a * (f (pred a))) *)
-Definition fact :=
- tfix
- (tabs f (TArrow TNat TNat)
- (tabs a TNat
- (tif0
- (tvar a)
- (tnat 1)
- (tmult
- (tvar a)
- (tapp (tvar f) (tpred (tvar a))))))).
-
-(** (Warning: you may be able to typecheck [fact] but still have some
- rules wrong!) *)
-
-(*
-Example fact_typechecks :
- (@empty ty) |- fact \in (TArrow TNat TNat).
-Proof. unfold fact. auto 10.
-Qed.
-*)
-
-(*
-Example fact_example:
- (tapp fact (tnat 4)) ==>* (tnat 24).
-Proof. unfold fact. normalize. Qed.
-*)
-
-End FixTest1.
-
-Module FixTest2.
-
-(* map :=
- \g:nat->nat.
- fix
- (\f:[nat]->[nat].
- \l:[nat].
- case l of
- | [] -> []
- | x::l -> (g x)::(f l)) *)
-Definition map :=
- tabs g (TArrow TNat TNat)
- (tfix
- (tabs f (TArrow (TList TNat) (TList TNat))
- (tabs l (TList TNat)
- (tlcase (tvar l)
- (tnil TNat)
- a l (tcons (tapp (tvar g) (tvar a))
- (tapp (tvar f) (tvar l))))))).
-
-(*
-(* Make sure you've uncommented the last [Hint Extern] above... *)
-Example map_typechecks :
- empty |- map \in
- (TArrow (TArrow TNat TNat)
- (TArrow (TList TNat)
- (TList TNat))).
-Proof. unfold map. auto 10. Qed.
-
-Example map_example :
- tapp (tapp map (tabs a TNat (tsucc (tvar a))))
- (tcons (tnat 1) (tcons (tnat 2) (tnil TNat)))
- ==>* (tcons (tnat 2) (tcons (tnat 3) (tnil TNat))).
-Proof. unfold map. normalize. Qed.
-*)
-
-End FixTest2.
-
-Module FixTest3.
-
-(* equal =
- fix
- (\eq:Nat->Nat->Bool.
- \m:Nat. \n:Nat.
- if0 m then (if0 n then 1 else 0)
- else if0 n then 0
- else eq (pred m) (pred n)) *)
-
-Definition equal :=
- tfix
- (tabs eq (TArrow TNat (TArrow TNat TNat))
- (tabs m TNat
- (tabs n TNat
- (tif0 (tvar m)
- (tif0 (tvar n) (tnat 1) (tnat 0))
- (tif0 (tvar n)
- (tnat 0)
- (tapp (tapp (tvar eq)
- (tpred (tvar m)))
- (tpred (tvar n)))))))).
-
-(*
-Example equal_typechecks :
- (@empty ty) |- equal \in (TArrow TNat (TArrow TNat TNat)).
-Proof. unfold equal. auto 10.
-Qed.
-*)
-
-(*
-Example equal_example1:
- (tapp (tapp equal (tnat 4)) (tnat 4)) ==>* (tnat 1).
-Proof. unfold equal. normalize. Qed.
-*)
-
-(*
-Example equal_example2:
- (tapp (tapp equal (tnat 4)) (tnat 5)) ==>* (tnat 0).
-Proof. unfold equal. normalize. Qed.
-*)
-
-End FixTest3.
-
-Module FixTest4.
-
-(* let evenodd =
- fix
- (\eo: (Nat->Nat * Nat->Nat).
- let e = \n:Nat. if0 n then 1 else eo.snd (pred n) in
- let o = \n:Nat. if0 n then 0 else eo.fst (pred n) in
- (e,o)) in
- let even = evenodd.fst in
- let odd = evenodd.snd in
- (even 3, even 4)
-*)
-
-Definition eotest :=
- tlet evenodd
- (tfix
- (tabs eo (TProd (TArrow TNat TNat) (TArrow TNat TNat))
- (tpair
- (tabs n TNat
- (tif0 (tvar n)
- (tnat 1)
- (tapp (tsnd (tvar eo)) (tpred (tvar n)))))
- (tabs n TNat
- (tif0 (tvar n)
- (tnat 0)
- (tapp (tfst (tvar eo)) (tpred (tvar n))))))))
- (tlet even (tfst (tvar evenodd))
- (tlet odd (tsnd (tvar evenodd))
- (tpair
- (tapp (tvar even) (tnat 3))
- (tapp (tvar even) (tnat 4))))).
-
-(*
-Example eotest_typechecks :
- (@empty ty) |- eotest \in (TProd TNat TNat).
-Proof. unfold eotest. eauto 30.
-Qed.
-*)
-
-(*
-Example eotest_example1:
- eotest ==>* (tpair (tnat 0) (tnat 1)).
-Proof. unfold eotest. normalize. Qed.
-*)
-
-End FixTest4.
-
-End Examples.
-
-(* ###################################################################### *)
-(** ** Properties of Typing *)
-
-(** The proofs of progress and preservation for this system are
- essentially the same (though of course somewhat longer) as for the
- pure simply typed lambda-calculus. *)
-
-(* ###################################################################### *)
-(** *** Progress *)
-
-Theorem progress : forall t T,
- empty |- t \in T ->
- value t \/ exists t', t ==> t'.
-Proof with eauto.
- (* Theorem: Suppose empty |- t : T. Then either
- 1. t is a value, or
- 2. t ==> t' for some t'.
- Proof: By induction on the given typing derivation. *)
- intros t T Ht.
- remember (@empty ty) as Gamma.
- generalize dependent HeqGamma.
- has_type_cases (induction Ht) Case; intros HeqGamma; subst.
- Case "T_Var".
- (* The final rule in the given typing derivation cannot be [T_Var],
- since it can never be the case that [empty |- x : T] (since the
- context is empty). *)
- inversion H.
- Case "T_Abs".
- (* If the [T_Abs] rule was the last used, then [t = tabs x T11 t12],
- which is a value. *)
- left...
- Case "T_App".
- (* If the last rule applied was T_App, then [t = t1 t2], and we know
- from the form of the rule that
- [empty |- t1 : T1 -> T2]
- [empty |- t2 : T1]
- By the induction hypothesis, each of t1 and t2 either is a value
- or can take a step. *)
- right.
- destruct IHHt1; subst...
- SCase "t1 is a value".
- destruct IHHt2; subst...
- SSCase "t2 is a value".
- (* If both [t1] and [t2] are values, then we know that
- [t1 = tabs x T11 t12], since abstractions are the only values
- that can have an arrow type. But
- [(tabs x T11 t12) t2 ==> [x:=t2]t12] by [ST_AppAbs]. *)
- inversion H; subst; try (solve by inversion).
- exists (subst x t2 t12)...
- SSCase "t2 steps".
- (* If [t1] is a value and [t2 ==> t2'], then [t1 t2 ==> t1 t2']
- by [ST_App2]. *)
- inversion H0 as [t2' Hstp]. exists (tapp t1 t2')...
- SCase "t1 steps".
- (* Finally, If [t1 ==> t1'], then [t1 t2 ==> t1' t2] by [ST_App1]. *)
- inversion H as [t1' Hstp]. exists (tapp t1' t2)...
- Case "T_Nat".
- left...
- Case "T_Succ".
- right.
- destruct IHHt...
- SCase "t1 is a value".
- inversion H; subst; try solve by inversion.
- exists (tnat (S n1))...
- SCase "t1 steps".
- inversion H as [t1' Hstp].
- exists (tsucc t1')...
- Case "T_Pred".
- right.
- destruct IHHt...
- SCase "t1 is a value".
- inversion H; subst; try solve by inversion.
- exists (tnat (pred n1))...
- SCase "t1 steps".
- inversion H as [t1' Hstp].
- exists (tpred t1')...
- Case "T_Mult".
- right.
- destruct IHHt1...
- SCase "t1 is a value".
- destruct IHHt2...
- SSCase "t2 is a value".
- inversion H; subst; try solve by inversion.
- inversion H0; subst; try solve by inversion.
- exists (tnat (mult n1 n0))...
- SSCase "t2 steps".
- inversion H0 as [t2' Hstp].
- exists (tmult t1 t2')...
- SCase "t1 steps".
- inversion H as [t1' Hstp].
- exists (tmult t1' t2)...
- Case "T_If0".
- right.
- destruct IHHt1...
- SCase "t1 is a value".
- inversion H; subst; try solve by inversion.
- destruct n1 as [|n1'].
- SSCase "n1=0".
- exists t2...
- SSCase "n1<>0".
- exists t3...
- SCase "t1 steps".
- inversion H as [t1' H0].
- exists (tif0 t1' t2 t3)...
- Case "T_Pair".
- destruct IHHt1...
- SCase "t1 is a value".
- destruct IHHt2...
- SSCase "t2 steps".
- right. inversion H0 as [t2' Hstp].
- exists (tpair t1 t2')...
- SCase "t1 steps".
- right. inversion H as [t1' Hstp].
- exists (tpair t1' t2)...
- Case "T_Fst".
- right.
- destruct IHHt...
- SCase "t1 is a value".
- inversion H; subst; try solve by inversion.
- exists v1...
- SCase "t1 steps".
- inversion H as [t1' Hstp].
- exists (tfst t1')...
- Case "T_Snd".
- right.
- destruct IHHt...
- SCase "t1 is a value".
- inversion H; subst; try solve by inversion.
- exists v2...
- SCase "t1 steps".
- inversion H as [t1' Hstp].
- exists (tsnd t1')...
- Case "T_Unit".
- left...
-(* let *)
-(* FILL IN HERE *)
- Case "T_Inl".
- destruct IHHt...
- SCase "t1 steps".
- right. inversion H as [t1' Hstp]...
- (* exists (tinl _ t1')... *)
- Case "T_Inr".
- destruct IHHt...
- SCase "t1 steps".
- right. inversion H as [t1' Hstp]...
- (* exists (tinr _ t1')... *)
- Case "T_Case".
- right.
- destruct IHHt1...
- SCase "t0 is a value".
- inversion H; subst; try solve by inversion.
- SSCase "t0 is inl".
- exists ([x1:=v]t1)...
- SSCase "t0 is inr".
- exists ([x2:=v]t2)...
- SCase "t0 steps".
- inversion H as [t0' Hstp].
- exists (tcase t0' x1 t1 x2 t2)...
- Case "T_Nil".
- left...
- Case "T_Cons".
- destruct IHHt1...
- SCase "head is a value".
- destruct IHHt2...
- SSCase "tail steps".
- right. inversion H0 as [t2' Hstp].
- exists (tcons t1 t2')...
- SCase "head steps".
- right. inversion H as [t1' Hstp].
- exists (tcons t1' t2)...
- Case "T_Lcase".
- right.
- destruct IHHt1...
- SCase "t1 is a value".
- inversion H; subst; try solve by inversion.
- SSCase "t1=tnil".
- exists t2...
- SSCase "t1=tcons v1 vl".
- exists ([x2:=vl]([x1:=v1]t3))...
- SCase "t1 steps".
- inversion H as [t1' Hstp].
- exists (tlcase t1' t2 x1 x2 t3)...
-(* fix *)
-(* FILL IN HERE *)
-Qed.
-
-(* ###################################################################### *)
-(** *** Context Invariance *)
-
-Inductive appears_free_in : id -> tm -> Prop :=
- | afi_var : forall x,
- appears_free_in x (tvar x)
- | afi_app1 : forall x t1 t2,
- appears_free_in x t1 -> appears_free_in x (tapp t1 t2)
- | afi_app2 : forall x t1 t2,
- appears_free_in x t2 -> appears_free_in x (tapp t1 t2)
- | afi_abs : forall x y T11 t12,
- y <> x ->
- appears_free_in x t12 ->
- appears_free_in x (tabs y T11 t12)
- (* nats *)
- | afi_succ : forall x t,
- appears_free_in x t ->
- appears_free_in x (tsucc t)
- | afi_pred : forall x t,
- appears_free_in x t ->
- appears_free_in x (tpred t)
- | afi_mult1 : forall x t1 t2,
- appears_free_in x t1 ->
- appears_free_in x (tmult t1 t2)
- | afi_mult2 : forall x t1 t2,
- appears_free_in x t2 ->
- appears_free_in x (tmult t1 t2)
- | afi_if01 : forall x t1 t2 t3,
- appears_free_in x t1 ->
- appears_free_in x (tif0 t1 t2 t3)
- | afi_if02 : forall x t1 t2 t3,
- appears_free_in x t2 ->
- appears_free_in x (tif0 t1 t2 t3)
- | afi_if03 : forall x t1 t2 t3,
- appears_free_in x t3 ->
- appears_free_in x (tif0 t1 t2 t3)
- (* pairs *)
- | afi_pair1 : forall x t1 t2,
- appears_free_in x t1 ->
- appears_free_in x (tpair t1 t2)
- | afi_pair2 : forall x t1 t2,
- appears_free_in x t2 ->
- appears_free_in x (tpair t1 t2)
- | afi_fst : forall x t,
- appears_free_in x t ->
- appears_free_in x (tfst t)
- | afi_snd : forall x t,
- appears_free_in x t ->
- appears_free_in x (tsnd t)
- (* let *)
-(* FILL IN HERE *)
- (* sums *)
- | afi_inl : forall x t T,
- appears_free_in x t ->
- appears_free_in x (tinl T t)
- | afi_inr : forall x t T,
- appears_free_in x t ->
- appears_free_in x (tinr T t)
- | afi_case0 : forall x t0 x1 t1 x2 t2,
- appears_free_in x t0 ->
- appears_free_in x (tcase t0 x1 t1 x2 t2)
- | afi_case1 : forall x t0 x1 t1 x2 t2,
- x1 <> x ->
- appears_free_in x t1 ->
- appears_free_in x (tcase t0 x1 t1 x2 t2)
- | afi_case2 : forall x t0 x1 t1 x2 t2,
- x2 <> x ->
- appears_free_in x t2 ->
- appears_free_in x (tcase t0 x1 t1 x2 t2)
- (* lists *)
- | afi_cons1 : forall x t1 t2,
- appears_free_in x t1 ->
- appears_free_in x (tcons t1 t2)
- | afi_cons2 : forall x t1 t2,
- appears_free_in x t2 ->
- appears_free_in x (tcons t1 t2)
- | afi_lcase1 : forall x t1 t2 y1 y2 t3,
- appears_free_in x t1 ->
- appears_free_in x (tlcase t1 t2 y1 y2 t3)
- | afi_lcase2 : forall x t1 t2 y1 y2 t3,
- appears_free_in x t2 ->
- appears_free_in x (tlcase t1 t2 y1 y2 t3)
- | afi_lcase3 : forall x t1 t2 y1 y2 t3,
- y1 <> x ->
- y2 <> x ->
- appears_free_in x t3 ->
- appears_free_in x (tlcase t1 t2 y1 y2 t3)
- (* fix *)
-(* FILL IN HERE *)
- .
-
-Hint Constructors appears_free_in.
-
-Lemma context_invariance : forall Gamma Gamma' t S,
- Gamma |- t \in S ->
- (forall x, appears_free_in x t -> Gamma x = Gamma' x) ->
- Gamma' |- t \in S.
-Proof with eauto.
- intros. generalize dependent Gamma'.
- has_type_cases (induction H) Case;
- intros Gamma' Heqv...
- Case "T_Var".
- apply T_Var... rewrite <- Heqv...
- Case "T_Abs".
- apply T_Abs... apply IHhas_type. intros y Hafi.
- unfold extend.
- destruct (eq_id_dec x y)...
- Case "T_Mult".
- apply T_Mult...
- Case "T_If0".
- apply T_If0...
- Case "T_Pair".
- apply T_Pair...
-(* let *)
-(* FILL IN HERE *)
- Case "T_Case".
- eapply T_Case...
- apply IHhas_type2. intros y Hafi.
- unfold extend.
- destruct (eq_id_dec x1 y)...
- apply IHhas_type3. intros y Hafi.
- unfold extend.
- destruct (eq_id_dec x2 y)...
- Case "T_Cons".
- apply T_Cons...
- Case "T_Lcase".
- eapply T_Lcase... apply IHhas_type3. intros y Hafi.
- unfold extend.
- destruct (eq_id_dec x1 y)...
- destruct (eq_id_dec x2 y)...
-Qed.
-
-Lemma free_in_context : forall x t T Gamma,
- appears_free_in x t ->
- Gamma |- t \in T ->
- exists T', Gamma x = Some T'.
-Proof with eauto.
- intros x t T Gamma Hafi Htyp.
- has_type_cases (induction Htyp) Case; inversion Hafi; subst...
- Case "T_Abs".
- destruct IHHtyp as [T' Hctx]... exists T'.
- unfold extend in Hctx.
- rewrite neq_id in Hctx...
-(* let *)
-(* FILL IN HERE *)
- Case "T_Case".
- SCase "left".
- destruct IHHtyp2 as [T' Hctx]... exists T'.
- unfold extend in Hctx.
- rewrite neq_id in Hctx...
- SCase "right".
- destruct IHHtyp3 as [T' Hctx]... exists T'.
- unfold extend in Hctx.
- rewrite neq_id in Hctx...
- Case "T_Lcase".
- clear Htyp1 IHHtyp1 Htyp2 IHHtyp2.
- destruct IHHtyp3 as [T' Hctx]... exists T'.
- unfold extend in Hctx.
- rewrite neq_id in Hctx... rewrite neq_id in Hctx...
-Qed.
-
-(* ###################################################################### *)
-(** *** Substitution *)
-
-Lemma substitution_preserves_typing : forall Gamma x U v t S,
- (extend Gamma x U) |- t \in S ->
- empty |- v \in U ->
- Gamma |- ([x:=v]t) \in S.
-Proof with eauto.
- (* Theorem: If Gamma,x:U |- t : S and empty |- v : U, then
- Gamma |- [x:=v]t : S. *)
- intros Gamma x U v t S Htypt Htypv.
- generalize dependent Gamma. generalize dependent S.
- (* Proof: By induction on the term t. Most cases follow directly
- from the IH, with the exception of tvar and tabs.
- The former aren't automatic because we must reason about how the
- variables interact. *)
- t_cases (induction t) Case;
- intros S Gamma Htypt; simpl; inversion Htypt; subst...
- Case "tvar".
- simpl. rename i into y.
- (* If t = y, we know that
- [empty |- v : U] and
- [Gamma,x:U |- y : S]
- and, by inversion, [extend Gamma x U y = Some S]. We want to
- show that [Gamma |- [x:=v]y : S].
-
- There are two cases to consider: either [x=y] or [x<>y]. *)
- destruct (eq_id_dec x y).
- SCase "x=y".
- (* If [x = y], then we know that [U = S], and that [[x:=v]y = v].
- So what we really must show is that if [empty |- v : U] then
- [Gamma |- v : U]. We have already proven a more general version
- of this theorem, called context invariance. *)
- subst.
- unfold extend in H1. rewrite eq_id in H1.
- inversion H1; subst. clear H1.
- eapply context_invariance...
- intros x Hcontra.
- destruct (free_in_context _ _ S empty Hcontra) as [T' HT']...
- inversion HT'.
- SCase "x<>y".
- (* If [x <> y], then [Gamma y = Some S] and the substitution has no
- effect. We can show that [Gamma |- y : S] by [T_Var]. *)
- apply T_Var... unfold extend in H1. rewrite neq_id in H1...
- Case "tabs".
- rename i into y. rename t into T11.
- (* If [t = tabs y T11 t0], then we know that
- [Gamma,x:U |- tabs y T11 t0 : T11->T12]
- [Gamma,x:U,y:T11 |- t0 : T12]
- [empty |- v : U]
- As our IH, we know that forall S Gamma,
- [Gamma,x:U |- t0 : S -> Gamma |- [x:=v]t0 : S].
-
- We can calculate that
- [x:=v]t = tabs y T11 (if beq_id x y then t0 else [x:=v]t0)
- And we must show that [Gamma |- [x:=v]t : T11->T12]. We know
- we will do so using [T_Abs], so it remains to be shown that:
- [Gamma,y:T11 |- if beq_id x y then t0 else [x:=v]t0 : T12]
- We consider two cases: [x = y] and [x <> y].
- *)
- apply T_Abs...
- destruct (eq_id_dec x y).
- SCase "x=y".
- (* If [x = y], then the substitution has no effect. Context
- invariance shows that [Gamma,y:U,y:T11] and [Gamma,y:T11] are
- equivalent. Since the former context shows that [t0 : T12], so
- does the latter. *)
- eapply context_invariance...
- subst.
- intros x Hafi. unfold extend.
- destruct (eq_id_dec y x)...
- SCase "x<>y".
- (* If [x <> y], then the IH and context invariance allow us to show that
- [Gamma,x:U,y:T11 |- t0 : T12] =>
- [Gamma,y:T11,x:U |- t0 : T12] =>
- [Gamma,y:T11 |- [x:=v]t0 : T12] *)
- apply IHt. eapply context_invariance...
- intros z Hafi. unfold extend.
- destruct (eq_id_dec y z)...
- subst. rewrite neq_id...
-(* let *)
-(* FILL IN HERE *)
- Case "tcase".
- rename i into x1. rename i0 into x2.
- eapply T_Case...
- SCase "left arm".
- destruct (eq_id_dec x x1).
- SSCase "x = x1".
- eapply context_invariance...
- subst.
- intros z Hafi. unfold extend.
- destruct (eq_id_dec x1 z)...
- SSCase "x <> x1".
- apply IHt2. eapply context_invariance...
- intros z Hafi. unfold extend.
- destruct (eq_id_dec x1 z)...
- subst. rewrite neq_id...
- SCase "right arm".
- destruct (eq_id_dec x x2).
- SSCase "x = x2".
- eapply context_invariance...
- subst.
- intros z Hafi. unfold extend.
- destruct (eq_id_dec x2 z)...
- SSCase "x <> x2".
- apply IHt3. eapply context_invariance...
- intros z Hafi. unfold extend.
- destruct (eq_id_dec x2 z)...
- subst. rewrite neq_id...
- Case "tlcase".
- rename i into y1. rename i0 into y2.
- eapply T_Lcase...
- destruct (eq_id_dec x y1).
- SCase "x=y1".
- simpl.
- eapply context_invariance...
- subst.
- intros z Hafi. unfold extend.
- destruct (eq_id_dec y1 z)...
- SCase "x<>y1".
- destruct (eq_id_dec x y2).
- SSCase "x=y2".
- eapply context_invariance...
- subst.
- intros z Hafi. unfold extend.
- destruct (eq_id_dec y2 z)...
- SSCase "x<>y2".
- apply IHt3. eapply context_invariance...
- intros z Hafi. unfold extend.
- destruct (eq_id_dec y1 z)...
- subst. rewrite neq_id...
- destruct (eq_id_dec y2 z)...
- subst. rewrite neq_id...
-Qed.
-
-(* ###################################################################### *)
-(** *** Preservation *)
-
-Theorem preservation : forall t t' T,
- empty |- t \in T ->
- t ==> t' ->
- empty |- t' \in T.
-Proof with eauto.
- intros t t' T HT.
- (* Theorem: If [empty |- t : T] and [t ==> t'], then [empty |- t' : T]. *)
- remember (@empty ty) as Gamma. generalize dependent HeqGamma.
- generalize dependent t'.
- (* Proof: By induction on the given typing derivation. Many cases are
- contradictory ([T_Var], [T_Abs]). We show just the interesting ones. *)
- has_type_cases (induction HT) Case;
- intros t' HeqGamma HE; subst; inversion HE; subst...
- Case "T_App".
- (* If the last rule used was [T_App], then [t = t1 t2], and three rules
- could have been used to show [t ==> t']: [ST_App1], [ST_App2], and
- [ST_AppAbs]. In the first two cases, the result follows directly from
- the IH. *)
- inversion HE; subst...
- SCase "ST_AppAbs".
- (* For the third case, suppose
- [t1 = tabs x T11 t12]
- and
- [t2 = v2].
- We must show that [empty |- [x:=v2]t12 : T2].
- We know by assumption that
- [empty |- tabs x T11 t12 : T1->T2]
- and by inversion
- [x:T1 |- t12 : T2]
- We have already proven that substitution_preserves_typing and
- [empty |- v2 : T1]
- by assumption, so we are done. *)
- apply substitution_preserves_typing with T1...
- inversion HT1...
- Case "T_Fst".
- inversion HT...
- Case "T_Snd".
- inversion HT...
-(* let *)
-(* FILL IN HERE *)
- Case "T_Case".
- SCase "ST_CaseInl".
- inversion HT1; subst.
- eapply substitution_preserves_typing...
- SCase "ST_CaseInr".
- inversion HT1; subst.
- eapply substitution_preserves_typing...
- Case "T_Lcase".
- SCase "ST_LcaseCons".
- inversion HT1; subst.
- apply substitution_preserves_typing with (TList T1)...
- apply substitution_preserves_typing with T1...
-(* fix *)
-(* FILL IN HERE *)
-Qed.
-(** [] *)
-
-End STLCExtended.
-
-(* $Date: 2013-12-03 07:45:41 -0500 (Tue, 03 Dec 2013) $ *)
-
-
diff --git a/Poly.html b/Poly.html
deleted file mode 100644
index 917d5dc..0000000
--- a/Poly.html
+++ /dev/null
@@ -1,1860 +0,0 @@
-
-
-
-
-
-Poly: Polymorphism and Higher-Order Functions
-
-
-
-
-
-
-
-
-
-
-
-
-
-
PolyPolymorphism and Higher-Order Functions
-
-
-
-
-
-
-
-
- In this chapter we continue our development of basic
- concepts of functional programming. The critical new ideas are
- polymorphism (abstracting functions over the types of the data
- they manipulate) and higher-order functions (treating functions
- as data).
-
-
-
-
-
-RequireExportLists.
-
-
-
-
-
-
Polymorphism
-
-
-
-
-
-
-
Polymorphic Lists
-
-
-
- For the last couple of chapters, we've been working just
- with lists of numbers. Obviously, interesting programs also need
- to be able to manipulate lists with elements from other types —
- lists of strings, lists of booleans, lists of lists, etc. We
- could just define a new inductive datatype for each of these,
- for example...
-
-... but this would quickly become tedious, partly because we
- have to make up different constructor names for each datatype, but
- mostly because we would also need to define new versions of all
- our list manipulating functions (length, rev, etc.) for each
- new datatype definition.
-
-
-
-
-
-
- To avoid all this repetition, Coq supports polymorphic
- inductive type definitions. For example, here is a polymorphic
- list datatype.
-
-This is exactly like the definition of natlist from the
- previous chapter, except that the nat argument to the cons
- constructor has been replaced by an arbitrary type X, a binding
- for X has been added to the header, and the occurrences of
- natlist in the types of the constructors have been replaced by
- listX. (We can re-use the constructor names nil and cons
- because the earlier definition of natlist was inside of a
- Module definition that is now out of scope.)
-
-
- What sort of thing is list itself? One good way to think
- about it is that list is a function from Types to
- Inductive definitions; or, to put it another way, list is a
- function from Types to Types. For any particular type X,
- the type listX is an Inductively defined set of lists whose
- elements are things of type X.
-
-
- With this definition, when we use the constructors nil and
- cons to build lists, we need to tell Coq the type of the
- elements in the lists we are building — that is, nil and cons
- are now polymorphic constructors. Observe the types of these
- constructors:
-
-
-
-
-Checknil.
-(* ===> nil : forall X : Type, list X *)
-Checkcons.
-(* ===> cons : forall X : Type, X -> list X -> list X *)
-
-
-
-
-
-The "∀X" in these types can be read as an additional
- argument to the constructors that determines the expected types of
- the arguments that follow. When nil and cons are used, these
- arguments are supplied in the same way as the others. For
- example, the list containing 2 and 1 is written like this:
-
-(We've gone back to writing nil and cons explicitly here
- because we haven't yet defined the [] and :: notations for
- the new version of lists. We'll do that in a bit.)
-
-
- We can now go back and make polymorphic (or "generic")
- versions of all the list-processing functions that we wrote
- before. Here is length, for example:
-
-Note that the uses of nil and cons in match patterns
- do not require any type annotations: Coq already knows that the list
- l contains elements of type X, so there's no reason to include
- X in the pattern. (More precisely, the type X is a parameter
- of the whole definition of list, not of the individual
- constructors. We'll come back to this point later.)
-
-
-
- As with nil and cons, we can use length by applying it first
- to a type and then to its list argument:
-
-Indeed it will. Let's see what type Coq has assigned to app':
-
-
-
-
-Checkapp'.
-(* ===> forall X : Type, list X -> list X -> list X *)
-Checkapp.
-(* ===> forall X : Type, list X -> list X -> list X *)
-
-
-
-
-
-It has exactly the same type type as app. Coq was able to
- use a process called type inference to deduce what the types of
- X, l1, and l2 must be, based on how they are used. For
- example, since X is used as an argument to cons, it must be a
- Type, since cons expects a Type as its first argument;
- matching l1 with nil and cons means it must be a list; and
- so on.
-
-
-
- This powerful facility means we don't always have to write
- explicit type annotations everywhere, although explicit type
- annotations are still quite useful as documentation and sanity
- checks. You should try to find a balance in your own code between
- too many type annotations (so many that they clutter and distract)
- and too few (which forces readers to perform type inference in
- their heads in order to understand your code).
-
-
-
-
-
-
-
-
Type Argument Synthesis
-
-
-
- Whenever we use a polymorphic function, we need to pass it
- one or more types in addition to its other arguments. For
- example, the recursive call in the body of the length function
- above must pass along the type X. But just like providing
- explicit type annotations everywhere, this is heavy and verbose.
- Since the second argument to length is a list of Xs, it seems
- entirely obvious that the first argument can only be X — why
- should we have to write it explicitly?
-
-
-
- Fortunately, Coq permits us to avoid this kind of redundancy. In
- place of any type argument we can write the "implicit argument"
- _, which can be read as "Please figure out for yourself what
- type belongs here." More precisely, when Coq encounters a _, it
- will attempt to unify all locally available information — the
- type of the function being applied, the types of the other
- arguments, and the type expected by the context in which the
- application appears — to determine what concrete type should
- replace the _.
-
-
-
- This may sound similar to type annotation inference — and,
- indeed, the two procedures rely on the same underlying mechanisms.
- Instead of simply omitting the types of some arguments to a
- function, like
-
-
-
-
- app'Xl1l2 : listX :=
-
-
-
- we can also replace the types with _, like
-
-
-
-
- app' (X : _) (l1l2 : _) : listX :=
-
-
-
- which tells Coq to attempt to infer the missing information, just
- as with argument synthesis.
-
-
-
- Using implicit arguments, the length function can be written
- like this:
-
-In this instance, we don't save much by writing _ instead of
- X. But in many cases the difference can be significant. For
- example, suppose we want to write down a list containing the
- numbers 1, 2, and 3. Instead of writing this...
-
-
- If fact, we can go further. To avoid having to sprinkle _'s
- throughout our programs, we can tell Coq always to infer the
- type argument(s) of a given function. The Arguments directive
- specifies the name of the function or constructor, and then lists
- its argument names, with curly braces around any arguments to be
- treated as implicit.
-
-
-
-
-
-Argumentsnil {X}.
-Argumentscons {X} __. (* use underscore for argument position that has no name *)
-Argumentslength {X} l.
-Argumentsapp {X} l1l2.
-Argumentsrev {X} l.
-Argumentssnoc {X} lv.
-
-
-(* note: no _ arguments required... *)
-Definitionlist123'' := cons 1 (cons 2 (cons 3 nil)).
-Check (lengthlist123'').
-
-
-
-
-
-
-
-
-
- Alternatively, we can declare an argument to be implicit while
- defining the function itself, by surrounding the argument in curly
- braces. For example:
-
-(Note that we didn't even have to provide a type argument to
- the recursive call to length''; indeed, it is invalid to provide
- one.) We will use this style whenever possible, although we will
- continue to use use explicit Argument declarations for
- Inductive constructors.
-
-
-
-
-
-
- One small problem with declaring arguments Implicit is
- that, occasionally, Coq does not have enough local information to
- determine a type argument; in such cases, we need to tell Coq that
- we want to give the argument explicitly this time, even though
- we've globally declared it to be Implicit. For example, suppose we
- write this:
-
-
-
-
-(* Definition mynil := nil. *)
-
-
-
-
-
-If we uncomment this definition, Coq will give us an error,
- because it doesn't know what type argument to supply to nil. We
- can help it by providing an explicit type declaration (so that Coq
- has more information available when it gets to the "application"
- of nil):
-
-
-
-
-Definitionmynil : listnat := nil.
-
-
-
-
-
-Alternatively, we can force the implicit arguments to be explicit by
- prefixing the function name with @.
-
- Using argument synthesis and implicit arguments, we can
- define convenient notation for lists, as before. Since we have
- made the constructor type arguments implicit, Coq will know to
- automatically infer these when we use the notations.
-
- Here are a few simple exercises, just like ones in the Lists
- chapter, for practice with polymorphism. Fill in the definitions
- and complete the proofs below.
-
-
-
-
-Fixpointrepeat {X : Type} (n : X) (count : nat) : listX :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_repeat1:
- repeattrue 2 = construe (construenil).
- (* FILL IN HERE *)Admitted.
-
-
-Theoremnil_app : ∀X:Type, ∀l:listX,
- app [] l = l.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremrev_snoc : ∀X : Type,
- ∀v : X,
- ∀s : listX,
- rev (snocsv) = v :: (revs).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremrev_involutive : ∀X : Type, ∀l : listX,
- rev (revl) = l.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-Theoremsnoc_with_append : ∀X : Type,
- ∀l1l2 : listX,
- ∀v : X,
- snoc (l1 ++ l2) v = l1 ++ (snocl2v).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Polymorphic Pairs
-
-
-
- Following the same pattern, the type definition we gave in
- the last chapter for pairs of numbers can be generalized to
- polymorphic pairs (or products):
-
-(The annotation :type_scope tells Coq that this abbreviation
- should be used when parsing types. This avoids a clash with the
- multiplication symbol.)
-
-
-
- A note of caution: it is easy at first to get (x,y) and
- X×Y confused. Remember that (x,y) is a value built from two
- other values; X×Y is a type built from two other types. If
- x has type X and y has type Y, then (x,y) has type
- X×Y.
-
-
- The first and second projection functions now look pretty
- much as they would in any functional programming language.
-
-The following function takes two lists and combines them
- into a list of pairs. In many functional programming languages,
- it is called zip. We call it combine for consistency with
- Coq's standard library. Note that the pair notation can be used both in expressions and in
- patterns...
-
- The function split is the right inverse of combine: it takes a
- list of pairs and returns a pair of lists. In many functional
- programing languages, this function is called unzip.
-
-
-
- Uncomment the material below and fill in the definition of
- split. Make sure it passes the given unit tests.
-
-
-
-
-Fixpointsplit
- {XY : Type} (l : list (X×Y))
- : (listX) × (listY) :=
-(* FILL IN HERE *)admit.
-
-
-Exampletest_split:
- split [(1,false);(2,false)] = ([1;2],[false;false]).
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Polymorphic Options
-
-
-
- One last polymorphic type for now: polymorphic options.
- The type declaration generalizes the one for natoption in the
- previous chapter:
-
- We can now rewrite the index function so that it works
- with any type of lists.
-
-
-
-
-Fixpointindex {X : Type} (n : nat)
- (l : listX) : optionX :=
- matchlwith
- | [] ⇒ None
- | a :: l' ⇒ ifbeq_natnOthenSomeaelseindex (predn) l'
- end.
-
-
-Exampletest_index1 : index 0 [4;5;6;7] = Some 4.
-Proof. reflexivity. Qed.
-Exampletest_index2 : index 1 [[1];[2]] = Some [2].
-Proof. reflexivity. Qed.
-Exampletest_index3 : index 2 [true] = None.
-Proof. reflexivity. Qed.
-
-
-
-
-
-
Exercise: 1 star, optional (hd_opt_poly)
- Complete the definition of a polymorphic version of the
- hd_opt function from the last chapter. Be sure that it
- passes the unit tests below.
-
-
-
-
-Definitionhd_opt {X : Type} (l : listX) : optionX :=
- (* FILL IN HERE *)admit.
-
-
-
-
-
-Once again, to force the implicit arguments to be explicit,
- we can use @ before the name of the function.
-
-
-
-
-Check @hd_opt.
-
-
-Exampletest_hd_opt1 : hd_opt [1;2] = Some 1.
- (* FILL IN HERE *)Admitted.
-Exampletest_hd_opt2 : hd_opt [[1];[2]] = Some [1].
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Functions as Data
-
-
-
-
-
-
-
Higher-Order Functions
-
-
-
- Like many other modern programming languages — including
- all functional languages (ML, Haskell, Scheme, etc.) — Coq
- treats functions as first-class citizens, allowing functions to be
- passed as arguments to other functions, returned as results,
- stored in data structures, etc.
-
-
-
- Functions that manipulate other functions are often called
- higher-order functions. Here's a simple one:
-
-
-
-
-Definitiondoit3times {X:Type} (f:X→X) (n:X) : X :=
- f (f (fn)).
-
-
-
-
-
-The argument f here is itself a function (from X to
- X); the body of doit3times applies f three times to some
- value n.
-
-
- In fact, the multiple-argument functions we have already
- seen are also examples of passing functions as data. To see why,
- recall the type of plus.
-
-Each → in this expression is actually a binary operator
- on types. (This is the same as saying that Coq primitively
- supports only one-argument functions — do you see why?) This
- operator is right-associative, so the type of plus is really a
- shorthand for nat→(nat→nat) — i.e., it can be read as
- saying that "plus is a one-argument function that takes a nat
- and returns a one-argument function that takes another nat and
- returns a nat." In the examples above, we have always applied
- plus to both of its arguments at once, but if we like we can
- supply just the first. This is called partial application.
-
- In Coq, a function f:A→B→C really has the type A
- →(B→C). That is, if you give f a value of type A, it
- will give you function f':B→C. If you then give f' a
- value of type B, it will return a value of type C. This
- allows for partial application, as in plus3. Processing a list
- of arguments with functions that return functions is called
- currying, in honor of the logician Haskell Curry.
-
-
-
- Conversely, we can reinterpret the type A→B→C as (A×
- B)→C. This is called uncurrying. With an uncurried binary
- function, both arguments must be given at once as a pair; there is
- no partial application.
-
-
- We can define currying as follows:
-
-
-
-
-Definitionprod_curry {XYZ : Type}
- (f : X × Y→Z) (x : X) (y : Y) : Z := f (x, y).
-
-
-
-
-
-As an exercise, define its inverse, prod_uncurry. Then prove
- the theorems below to show that the two are inverses.
-
-
-
-
-Definitionprod_uncurry {XYZ : Type}
- (f : X→Y→Z) (p : X × Y) : Z :=
- (* FILL IN HERE *)admit.
-
-
-
-
-
-(Thought exercise: before running these commands, can you
- calculate the types of prod_curry and prod_uncurry?)
-
-
-
-
-Check @prod_curry.
-Check @prod_uncurry.
-
-
-Theoremuncurry_curry : ∀(XYZ : Type) (f : X→Y→Z) xy,
- prod_curry (prod_uncurryf) xy = fxy.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremcurry_uncurry : ∀(XYZ : Type)
- (f : (X × Y) →Z) (p : X × Y),
- prod_uncurry (prod_curryf) p = fp.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Filter
-
-
-
- Here is a useful higher-order function, which takes a list
- of Xs and a predicate on X (a function from X to bool)
- and "filters" the list, returning a new list containing just those
- elements for which the predicate returns true.
-
-
- It is a little annoying to be forced to define the function
- length_is_1 and give it a name just to be able to pass it as an
- argument to filter, since we will probably never use it again.
- Moreover, this is not an isolated example. When using
- higher-order functions, we often want to pass as arguments
- "one-off" functions that we will never use again; having to give
- each of these functions a name would be tedious.
-
-
-
- Fortunately, there is a better way. It is also possible to
- construct a function "on the fly" without declaring it at the top
- level or giving it a name; this is analogous to the notation we've
- been using for writing down constant lists, natural numbers, and
- so on.
-
-
- Use filter (instead of Fixpoint) to write a Coq function
- filter_even_gt7 that takes a list of natural numbers as input
- and returns a list of just those that are even and greater than
- 7.
-
-
-
-
-Definitionfilter_even_gt7 (l : listnat) : listnat :=
- (* FILL IN HERE *)admit.
-
-
-Exampletest_filter_even_gt7_1 :
- filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
- (* FILL IN HERE *)Admitted.
-
-
-Exampletest_filter_even_gt7_2 :
- filter_even_gt7 [5;2;6;19;129] = [].
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (partition)
- Use filter to write a Coq function partition:
-
-
- Given a set X, a test function of type X→bool and a list
- X, partition should return a pair of lists. The first member of
- the pair is the sublist of the original list containing the
- elements that satisfy the test, and the second is the sublist
- containing those that fail the test. The order of elements in the
- two sublists should be the same as their order in the original
- list.
-
-
-
-
-
-Definitionpartition {X : Type} (test : X→bool) (l : listX)
- : listX × listX :=
-(* FILL IN HERE *)admit.
-
-
-Exampletest_partition1: partitionoddb [1;2;3;4;5] = ([1;3;5], [2;4]).
-(* FILL IN HERE *)Admitted.
-Exampletest_partition2: partition (funx ⇒ false) [5;9;0] = ([], [5;9;0]).
-(* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Map
-
-
-
- Another handy higher-order function is called map.
-
- It takes a function f and a list l=[n1,n2,n3,...]
- and returns the list [fn1,fn2,fn3,...] , where f has
- been applied to each element of l in turn. For example:
-
-The element types of the input and output lists need not be
- the same (map takes two type arguments, X and Y). This
- version of map can thus be applied to a list of numbers and a
- function from numbers to booleans to yield a list of booleans:
-
- Show that map and rev commute. You may need to define an
- auxiliary lemma.
-
-
-
-
-Theoremmap_rev : ∀(XY : Type) (f : X→Y) (l : listX),
- mapf (revl) = rev (mapfl).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars (flat_map)
- The function map maps a listX to a listY using a function
- of type X→Y. We can define a similar function, flat_map,
- which maps a listX to a listY using a function f of type
- X→listY. Your definition should work by 'flattening' the
- results of f, like so:
-
-
- The definitions and uses of filter and map use implicit
- arguments in many places. Replace the curly braces around the
- implicit arguments with parentheses, and then fill in explicit
- type parameters where necessary and use Coq to check that you've
- done so correctly. (This exercise is not to be turned in; it is
- probably easiest to do it on a copy of this file that you can
- throw away afterwards.) ☐
-
-
-
-
-
-
-
-
Fold
-
-
-
- An even more powerful higher-order function is called
- fold. This function is the inspiration for the "reduce"
- operation that lies at the heart of Google's map/reduce
- distributed programming framework.
-
-
-
-
-Fixpointfold {XY:Type} (f: X→Y→Y) (l:listX) (b:Y) : Y :=
- matchlwith
- | nil ⇒ b
- | h :: t ⇒ fh (foldftb)
- end.
-
-
-
-
-
-
-
-
-
- Intuitively, the behavior of the fold operation is to
- insert a given binary operator f between every pair of elements
- in a given list. For example, foldplus[1;2;3;4] intuitively
- means 1+2+3+4. To make this precise, we also need a "starting
- element" that serves as the initial second input to f. So, for
- example,
-
-
- Observe that the type of fold is parameterized by two type
- variables, X and Y, and the parameter f is a binary operator
- that takes an X and a Y and returns a Y. Can you think of a
- situation where it would be useful for X and Y to be
- different?
-
-
-
-
-
-
-
-
Functions For Constructing Functions
-
-
-
- Most of the higher-order functions we have talked about so
- far take functions as arguments. Now let's look at some
- examples involving returning functions as the results of other
- functions.
-
-
-
- To begin, here is a function that takes a value x (drawn from
- some type X) and returns a function from nat to X that
- yields x whenever it is called, ignoring its nat argument.
-
- Similarly, but a bit more interestingly, here is a function
- that takes a function f from numbers to some type X, a number
- k, and a value x, and constructs a function that behaves
- exactly like f except that, when called with the argument k,
- it returns x.
-
-For example, we can apply override twice to obtain a
- function from numbers to booleans that returns false on 1 and
- 3 and returns true on all other arguments.
-
- Before starting to work on the following proof, make sure you
- understand exactly what the theorem is saying and can paraphrase
- it in your own words. The proof itself is straightforward.
-
-
-
-
-Theoremoverride_example : ∀(b:bool),
- (override (constfunb) 3 true) 2 = b.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- We'll use function overriding heavily in parts of the rest of the
- course, and we will end up needing to know quite a bit about its
- properties. To prove these properties, though, we need to know
- about a few more of Coq's tactics; developing these is the main
- topic of the next chapter. For now, though, let's introduce just
- one very useful tactic that will also help us with proving
- properties of some of the other functions we have introduced in
- this chapter.
-
-
-
-
-
-
-
-
-
-
The unfold Tactic
-
-
-
- Sometimes, a proof will get stuck because Coq doesn't
- automatically expand a function call into its definition. (This
- is a feature, not a bug: if Coq automatically expanded everything
- possible, our proof goals would quickly become enormous — hard to
- read and slow for Coq to manipulate!)
-
-
-
-
-Theoremunfold_example_bad : ∀mn,
- 3 + n = m→
- plus3n + 1 = m + 1.
-Proof.
- introsmnH.
-(* At this point, we'd like to do rewrite→H, since
- plus3n is definitionally equal to 3+n. However,
- Coq doesn't automatically expand plus3n to its
- definition. *)
- Abort.
-
-
-
-
-
-The unfold tactic can be used to explicitly replace a
- defined name by the right-hand side of its definition.
-
-Now we can prove a first property of override: If we
- override a function at some argument k and then look up k, we
- get back the overridden value.
-
-
-
-
\ No newline at end of file
diff --git a/Poly.v b/Poly.v
deleted file mode 100644
index 426d534..0000000
--- a/Poly.v
+++ /dev/null
@@ -1,1082 +0,0 @@
-(** * Poly: Polymorphism and Higher-Order Functions *)
-
-(** In this chapter we continue our development of basic
- concepts of functional programming. The critical new ideas are
- _polymorphism_ (abstracting functions over the types of the data
- they manipulate) and _higher-order functions_ (treating functions
- as data).
-*)
-
-Require Export Lists.
-
-(* ###################################################### *)
-(** * Polymorphism *)
-(* ###################################################### *)
-(** ** Polymorphic Lists *)
-
-(** For the last couple of chapters, we've been working just
- with lists of numbers. Obviously, interesting programs also need
- to be able to manipulate lists with elements from other types --
- lists of strings, lists of booleans, lists of lists, etc. We
- _could_ just define a new inductive datatype for each of these,
- for example... *)
-
-Inductive boollist : Type :=
- | bool_nil : boollist
- | bool_cons : bool -> boollist -> boollist.
-
-(** ... but this would quickly become tedious, partly because we
- have to make up different constructor names for each datatype, but
- mostly because we would also need to define new versions of all
- our list manipulating functions ([length], [rev], etc.) for each
- new datatype definition. *)
-
-(** *** *)
-
-(** To avoid all this repetition, Coq supports _polymorphic_
- inductive type definitions. For example, here is a _polymorphic
- list_ datatype. *)
-
-Inductive list (X:Type) : Type :=
- | nil : list X
- | cons : X -> list X -> list X.
-
-
-(** This is exactly like the definition of [natlist] from the
- previous chapter, except that the [nat] argument to the [cons]
- constructor has been replaced by an arbitrary type [X], a binding
- for [X] has been added to the header, and the occurrences of
- [natlist] in the types of the constructors have been replaced by
- [list X]. (We can re-use the constructor names [nil] and [cons]
- because the earlier definition of [natlist] was inside of a
- [Module] definition that is now out of scope.) *)
-
-(** What sort of thing is [list] itself? One good way to think
- about it is that [list] is a _function_ from [Type]s to
- [Inductive] definitions; or, to put it another way, [list] is a
- function from [Type]s to [Type]s. For any particular type [X],
- the type [list X] is an [Inductive]ly defined set of lists whose
- elements are things of type [X]. *)
-
-(** With this definition, when we use the constructors [nil] and
- [cons] to build lists, we need to tell Coq the type of the
- elements in the lists we are building -- that is, [nil] and [cons]
- are now _polymorphic constructors_. Observe the types of these
- constructors: *)
-
-Check nil.
-(* ===> nil : forall X : Type, list X *)
-Check cons.
-(* ===> cons : forall X : Type, X -> list X -> list X *)
-
-(** The "[forall X]" in these types can be read as an additional
- argument to the constructors that determines the expected types of
- the arguments that follow. When [nil] and [cons] are used, these
- arguments are supplied in the same way as the others. For
- example, the list containing [2] and [1] is written like this: *)
-
-Check (cons nat 2 (cons nat 1 (nil nat))).
-
-(** (We've gone back to writing [nil] and [cons] explicitly here
- because we haven't yet defined the [ [] ] and [::] notations for
- the new version of lists. We'll do that in a bit.) *)
-
-(** We can now go back and make polymorphic (or "generic")
- versions of all the list-processing functions that we wrote
- before. Here is [length], for example: *)
-
-(** *** *)
-
-Fixpoint length (X:Type) (l:list X) : nat :=
- match l with
- | nil => 0
- | cons h t => S (length X t)
- end.
-
-(** Note that the uses of [nil] and [cons] in [match] patterns
- do not require any type annotations: Coq already knows that the list
- [l] contains elements of type [X], so there's no reason to include
- [X] in the pattern. (More precisely, the type [X] is a parameter
- of the whole definition of [list], not of the individual
- constructors. We'll come back to this point later.)
-
- As with [nil] and [cons], we can use [length] by applying it first
- to a type and then to its list argument: *)
-
-Example test_length1 :
- length nat (cons nat 1 (cons nat 2 (nil nat))) = 2.
-Proof. reflexivity. Qed.
-
-(** To use our length with other kinds of lists, we simply
- instantiate it with an appropriate type parameter: *)
-
-Example test_length2 :
- length bool (cons bool true (nil bool)) = 1.
-Proof. reflexivity. Qed.
-
-
-(** *** *)
-(** Let's close this subsection by re-implementing a few other
- standard list functions on our new polymorphic lists: *)
-
-Fixpoint app (X : Type) (l1 l2 : list X)
- : (list X) :=
- match l1 with
- | nil => l2
- | cons h t => cons X h (app X t l2)
- end.
-
-Fixpoint snoc (X:Type) (l:list X) (v:X) : (list X) :=
- match l with
- | nil => cons X v (nil X)
- | cons h t => cons X h (snoc X t v)
- end.
-
-Fixpoint rev (X:Type) (l:list X) : list X :=
- match l with
- | nil => nil X
- | cons h t => snoc X (rev X t) h
- end.
-
-
-
-Example test_rev1 :
- rev nat (cons nat 1 (cons nat 2 (nil nat)))
- = (cons nat 2 (cons nat 1 (nil nat))).
-Proof. reflexivity. Qed.
-
-Example test_rev2:
- rev bool (nil bool) = nil bool.
-Proof. reflexivity. Qed.
-
-Module MumbleBaz.
-(** **** Exercise: 2 stars (mumble_grumble) *)
-(** Consider the following two inductively defined types. *)
-
-Inductive mumble : Type :=
- | a : mumble
- | b : mumble -> nat -> mumble
- | c : mumble.
-Inductive grumble (X:Type) : Type :=
- | d : mumble -> grumble X
- | e : X -> grumble X.
-
-(** Which of the following are well-typed elements of [grumble X] for
- some type [X]?
- - [d (b a 5)]
- - [d mumble (b a 5)]
- - [d bool (b a 5)]
- - [e bool true]
- - [e mumble (b c 0)]
- - [e bool (b c 0)]
- - [c]
-(* FILL IN HERE *)
-[] *)
-
-
-(** **** Exercise: 2 stars (baz_num_elts) *)
-(** Consider the following inductive definition: *)
-
-Inductive baz : Type :=
- | x : baz -> baz
- | y : baz -> bool -> baz.
-
-(** How _many_ elements does the type [baz] have?
-(* FILL IN HERE *)
-[] *)
-
-End MumbleBaz.
-
-(* ###################################################### *)
-(** *** Type Annotation Inference *)
-
-(** Let's write the definition of [app] again, but this time we won't
- specify the types of any of the arguments. Will Coq still accept
- it? *)
-
-Fixpoint app' X l1 l2 : list X :=
- match l1 with
- | nil => l2
- | cons h t => cons X h (app' X t l2)
- end.
-
-(** Indeed it will. Let's see what type Coq has assigned to [app']: *)
-
-Check app'.
-(* ===> forall X : Type, list X -> list X -> list X *)
-Check app.
-(* ===> forall X : Type, list X -> list X -> list X *)
-
-(** It has exactly the same type type as [app]. Coq was able to
- use a process called _type inference_ to deduce what the types of
- [X], [l1], and [l2] must be, based on how they are used. For
- example, since [X] is used as an argument to [cons], it must be a
- [Type], since [cons] expects a [Type] as its first argument;
- matching [l1] with [nil] and [cons] means it must be a [list]; and
- so on.
-
- This powerful facility means we don't always have to write
- explicit type annotations everywhere, although explicit type
- annotations are still quite useful as documentation and sanity
- checks. You should try to find a balance in your own code between
- too many type annotations (so many that they clutter and distract)
- and too few (which forces readers to perform type inference in
- their heads in order to understand your code). *)
-
-(* ###################################################### *)
-(** *** Type Argument Synthesis *)
-
-(** Whenever we use a polymorphic function, we need to pass it
- one or more types in addition to its other arguments. For
- example, the recursive call in the body of the [length] function
- above must pass along the type [X]. But just like providing
- explicit type annotations everywhere, this is heavy and verbose.
- Since the second argument to [length] is a list of [X]s, it seems
- entirely obvious that the first argument can only be [X] -- why
- should we have to write it explicitly?
-
- Fortunately, Coq permits us to avoid this kind of redundancy. In
- place of any type argument we can write the "implicit argument"
- [_], which can be read as "Please figure out for yourself what
- type belongs here." More precisely, when Coq encounters a [_], it
- will attempt to _unify_ all locally available information -- the
- type of the function being applied, the types of the other
- arguments, and the type expected by the context in which the
- application appears -- to determine what concrete type should
- replace the [_].
-
- This may sound similar to type annotation inference -- and,
- indeed, the two procedures rely on the same underlying mechanisms.
- Instead of simply omitting the types of some arguments to a
- function, like
- app' X l1 l2 : list X :=
- we can also replace the types with [_], like
- app' (X : _) (l1 l2 : _) : list X :=
- which tells Coq to attempt to infer the missing information, just
- as with argument synthesis.
-
- Using implicit arguments, the [length] function can be written
- like this: *)
-
-Fixpoint length' (X:Type) (l:list X) : nat :=
- match l with
- | nil => 0
- | cons h t => S (length' _ t)
- end.
-
-(** In this instance, we don't save much by writing [_] instead of
- [X]. But in many cases the difference can be significant. For
- example, suppose we want to write down a list containing the
- numbers [1], [2], and [3]. Instead of writing this... *)
-
-Definition list123 :=
- cons nat 1 (cons nat 2 (cons nat 3 (nil nat))).
-
-(** ...we can use argument synthesis to write this: *)
-
-Definition list123' := cons _ 1 (cons _ 2 (cons _ 3 (nil _))).
-
-(* ###################################################### *)
-(** *** Implicit Arguments *)
-
-(** If fact, we can go further. To avoid having to sprinkle [_]'s
- throughout our programs, we can tell Coq _always_ to infer the
- type argument(s) of a given function. The [Arguments] directive
- specifies the name of the function or constructor, and then lists
- its argument names, with curly braces around any arguments to be
- treated as implicit.
- *)
-
-Arguments nil {X}.
-Arguments cons {X} _ _. (* use underscore for argument position that has no name *)
-Arguments length {X} l.
-Arguments app {X} l1 l2.
-Arguments rev {X} l.
-Arguments snoc {X} l v.
-
-(* note: no _ arguments required... *)
-Definition list123'' := cons 1 (cons 2 (cons 3 nil)).
-Check (length list123'').
-
-(** *** *)
-
-(** Alternatively, we can declare an argument to be implicit while
- defining the function itself, by surrounding the argument in curly
- braces. For example: *)
-
-Fixpoint length'' {X:Type} (l:list X) : nat :=
- match l with
- | nil => 0
- | cons h t => S (length'' t)
- end.
-
-(** (Note that we didn't even have to provide a type argument to
- the recursive call to [length'']; indeed, it is invalid to provide
- one.) We will use this style whenever possible, although we will
- continue to use use explicit [Argument] declarations for
- [Inductive] constructors. *)
-
-(** *** *)
-
-(** One small problem with declaring arguments [Implicit] is
- that, occasionally, Coq does not have enough local information to
- determine a type argument; in such cases, we need to tell Coq that
- we want to give the argument explicitly this time, even though
- we've globally declared it to be [Implicit]. For example, suppose we
- write this: *)
-
-(* Definition mynil := nil. *)
-
-(** If we uncomment this definition, Coq will give us an error,
- because it doesn't know what type argument to supply to [nil]. We
- can help it by providing an explicit type declaration (so that Coq
- has more information available when it gets to the "application"
- of [nil]): *)
-
-Definition mynil : list nat := nil.
-
-(** Alternatively, we can force the implicit arguments to be explicit by
- prefixing the function name with [@]. *)
-
-Check @nil.
-
-Definition mynil' := @nil nat.
-
-(** *** *)
-(** Using argument synthesis and implicit arguments, we can
- define convenient notation for lists, as before. Since we have
- made the constructor type arguments implicit, Coq will know to
- automatically infer these when we use the notations. *)
-
-Notation "x :: y" := (cons x y)
- (at level 60, right associativity).
-Notation "[ ]" := nil.
-Notation "[ x ; .. ; y ]" := (cons x .. (cons y []) ..).
-Notation "x ++ y" := (app x y)
- (at level 60, right associativity).
-
-(** Now lists can be written just the way we'd hope: *)
-
-Definition list123''' := [1; 2; 3].
-
-Check ([3 + 4] ++ nil).
-
-
-
-
-(* ###################################################### *)
-(** *** Exercises: Polymorphic Lists *)
-
-(** **** Exercise: 2 stars, optional (poly_exercises) *)
-(** Here are a few simple exercises, just like ones in the [Lists]
- chapter, for practice with polymorphism. Fill in the definitions
- and complete the proofs below. *)
-
-Fixpoint repeat {X : Type} (n : X) (count : nat) : list X :=
- (* FILL IN HERE *) admit.
-
-Example test_repeat1:
- repeat true 2 = cons true (cons true nil).
- (* FILL IN HERE *) Admitted.
-
-Theorem nil_app : forall X:Type, forall l:list X,
- app [] l = l.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem rev_snoc : forall X : Type,
- forall v : X,
- forall s : list X,
- rev (snoc s v) = v :: (rev s).
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem rev_involutive : forall X : Type, forall l : list X,
- rev (rev l) = l.
-Proof.
-(* FILL IN HERE *) Admitted.
-
-Theorem snoc_with_append : forall X : Type,
- forall l1 l2 : list X,
- forall v : X,
- snoc (l1 ++ l2) v = l1 ++ (snoc l2 v).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** ** Polymorphic Pairs *)
-
-(** Following the same pattern, the type definition we gave in
- the last chapter for pairs of numbers can be generalized to
- _polymorphic pairs_ (or _products_): *)
-
-Inductive prod (X Y : Type) : Type :=
- pair : X -> Y -> prod X Y.
-
-Arguments pair {X} {Y} _ _.
-
-(** As with lists, we make the type arguments implicit and define the
- familiar concrete notation. *)
-
-Notation "( x , y )" := (pair x y).
-
-(** We can also use the [Notation] mechanism to define the standard
- notation for pair _types_: *)
-
-Notation "X * Y" := (prod X Y) : type_scope.
-
-(** (The annotation [: type_scope] tells Coq that this abbreviation
- should be used when parsing types. This avoids a clash with the
- multiplication symbol.) *)
-
-(** *** *)
-(** A note of caution: it is easy at first to get [(x,y)] and
- [X*Y] confused. Remember that [(x,y)] is a _value_ built from two
- other values; [X*Y] is a _type_ built from two other types. If
- [x] has type [X] and [y] has type [Y], then [(x,y)] has type
- [X*Y]. *)
-
-(** The first and second projection functions now look pretty
- much as they would in any functional programming language. *)
-
-Definition fst {X Y : Type} (p : X * Y) : X :=
- match p with (x,y) => x end.
-
-Definition snd {X Y : Type} (p : X * Y) : Y :=
- match p with (x,y) => y end.
-
-(** The following function takes two lists and combines them
- into a list of pairs. In many functional programming languages,
- it is called [zip]. We call it [combine] for consistency with
- Coq's standard library. *)
-(** Note that the pair notation can be used both in expressions and in
- patterns... *)
-
-Fixpoint combine {X Y : Type} (lx : list X) (ly : list Y)
- : list (X*Y) :=
- match (lx,ly) with
- | ([],_) => []
- | (_,[]) => []
- | (x::tx, y::ty) => (x,y) :: (combine tx ty)
- end.
-
-(** **** Exercise: 1 star, optional (combine_checks) *)
-(** Try answering the following questions on paper and
- checking your answers in coq:
- - What is the type of [combine] (i.e., what does [Check
- @combine] print?)
- - What does
- Eval compute in (combine [1;2] [false;false;true;true]).
- print? []
-*)
-
-(** **** Exercise: 2 stars (split) *)
-(** The function [split] is the right inverse of combine: it takes a
- list of pairs and returns a pair of lists. In many functional
- programing languages, this function is called [unzip].
-
- Uncomment the material below and fill in the definition of
- [split]. Make sure it passes the given unit tests. *)
-
-Fixpoint split
- {X Y : Type} (l : list (X*Y))
- : (list X) * (list Y) :=
-(* FILL IN HERE *) admit.
-
-Example test_split:
- split [(1,false);(2,false)] = ([1;2],[false;false]).
-Proof.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** ** Polymorphic Options *)
-
-(** One last polymorphic type for now: _polymorphic options_.
- The type declaration generalizes the one for [natoption] in the
- previous chapter: *)
-
-Inductive option (X:Type) : Type :=
- | Some : X -> option X
- | None : option X.
-
-Arguments Some {X} _.
-Arguments None {X}.
-
-(** *** *)
-(** We can now rewrite the [index] function so that it works
- with any type of lists. *)
-
-Fixpoint index {X : Type} (n : nat)
- (l : list X) : option X :=
- match l with
- | [] => None
- | a :: l' => if beq_nat n O then Some a else index (pred n) l'
- end.
-
-Example test_index1 : index 0 [4;5;6;7] = Some 4.
-Proof. reflexivity. Qed.
-Example test_index2 : index 1 [[1];[2]] = Some [2].
-Proof. reflexivity. Qed.
-Example test_index3 : index 2 [true] = None.
-Proof. reflexivity. Qed.
-
-(** **** Exercise: 1 star, optional (hd_opt_poly) *)
-(** Complete the definition of a polymorphic version of the
- [hd_opt] function from the last chapter. Be sure that it
- passes the unit tests below. *)
-
-Definition hd_opt {X : Type} (l : list X) : option X :=
- (* FILL IN HERE *) admit.
-
-(** Once again, to force the implicit arguments to be explicit,
- we can use [@] before the name of the function. *)
-
-Check @hd_opt.
-
-Example test_hd_opt1 : hd_opt [1;2] = Some 1.
- (* FILL IN HERE *) Admitted.
-Example test_hd_opt2 : hd_opt [[1];[2]] = Some [1].
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** * Functions as Data *)
-(* ###################################################### *)
-(** ** Higher-Order Functions *)
-
-(** Like many other modern programming languages -- including
- all _functional languages_ (ML, Haskell, Scheme, etc.) -- Coq
- treats functions as first-class citizens, allowing functions to be
- passed as arguments to other functions, returned as results,
- stored in data structures, etc.
-
- Functions that manipulate other functions are often called
- _higher-order_ functions. Here's a simple one: *)
-
-Definition doit3times {X:Type} (f:X->X) (n:X) : X :=
- f (f (f n)).
-
-(** The argument [f] here is itself a function (from [X] to
- [X]); the body of [doit3times] applies [f] three times to some
- value [n]. *)
-
-Check @doit3times.
-(* ===> doit3times : forall X : Type, (X -> X) -> X -> X *)
-
-Example test_doit3times: doit3times minustwo 9 = 3.
-Proof. reflexivity. Qed.
-
-Example test_doit3times': doit3times negb true = false.
-Proof. reflexivity. Qed.
-
-(* ###################################################### *)
-(** ** Partial Application *)
-
-(** In fact, the multiple-argument functions we have already
- seen are also examples of passing functions as data. To see why,
- recall the type of [plus]. *)
-
-Check plus.
-(* ==> nat -> nat -> nat *)
-
-(** Each [->] in this expression is actually a _binary_ operator
- on types. (This is the same as saying that Coq primitively
- supports only one-argument functions -- do you see why?) This
- operator is _right-associative_, so the type of [plus] is really a
- shorthand for [nat -> (nat -> nat)] -- i.e., it can be read as
- saying that "[plus] is a one-argument function that takes a [nat]
- and returns a one-argument function that takes another [nat] and
- returns a [nat]." In the examples above, we have always applied
- [plus] to both of its arguments at once, but if we like we can
- supply just the first. This is called _partial application_. *)
-
-Definition plus3 := plus 3.
-Check plus3.
-
-Example test_plus3 : plus3 4 = 7.
-Proof. reflexivity. Qed.
-Example test_plus3' : doit3times plus3 0 = 9.
-Proof. reflexivity. Qed.
-Example test_plus3'' : doit3times (plus 3) 0 = 9.
-Proof. reflexivity. Qed.
-
-(* ###################################################### *)
-(** ** Digression: Currying *)
-
-(** **** Exercise: 2 stars, advanced (currying) *)
-(** In Coq, a function [f : A -> B -> C] really has the type [A
- -> (B -> C)]. That is, if you give [f] a value of type [A], it
- will give you function [f' : B -> C]. If you then give [f'] a
- value of type [B], it will return a value of type [C]. This
- allows for partial application, as in [plus3]. Processing a list
- of arguments with functions that return functions is called
- _currying_, in honor of the logician Haskell Curry.
-
- Conversely, we can reinterpret the type [A -> B -> C] as [(A *
- B) -> C]. This is called _uncurrying_. With an uncurried binary
- function, both arguments must be given at once as a pair; there is
- no partial application. *)
-
-(** We can define currying as follows: *)
-
-Definition prod_curry {X Y Z : Type}
- (f : X * Y -> Z) (x : X) (y : Y) : Z := f (x, y).
-
-(** As an exercise, define its inverse, [prod_uncurry]. Then prove
- the theorems below to show that the two are inverses. *)
-
-Definition prod_uncurry {X Y Z : Type}
- (f : X -> Y -> Z) (p : X * Y) : Z :=
- (* FILL IN HERE *) admit.
-
-(** (Thought exercise: before running these commands, can you
- calculate the types of [prod_curry] and [prod_uncurry]?) *)
-
-Check @prod_curry.
-Check @prod_uncurry.
-
-Theorem uncurry_curry : forall (X Y Z : Type) (f : X -> Y -> Z) x y,
- prod_curry (prod_uncurry f) x y = f x y.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem curry_uncurry : forall (X Y Z : Type)
- (f : (X * Y) -> Z) (p : X * Y),
- prod_uncurry (prod_curry f) p = f p.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** ** Filter *)
-
-(** Here is a useful higher-order function, which takes a list
- of [X]s and a _predicate_ on [X] (a function from [X] to [bool])
- and "filters" the list, returning a new list containing just those
- elements for which the predicate returns [true]. *)
-
-Fixpoint filter {X:Type} (test: X->bool) (l:list X)
- : (list X) :=
- match l with
- | [] => []
- | h :: t => if test h then h :: (filter test t)
- else filter test t
- end.
-
-(** For example, if we apply [filter] to the predicate [evenb]
- and a list of numbers [l], it returns a list containing just the
- even members of [l]. *)
-
-Example test_filter1: filter evenb [1;2;3;4] = [2;4].
-Proof. reflexivity. Qed.
-
-(** *** *)
-Definition length_is_1 {X : Type} (l : list X) : bool :=
- beq_nat (length l) 1.
-
-Example test_filter2:
- filter length_is_1
- [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
- = [ [3]; [4]; [8] ].
-Proof. reflexivity. Qed.
-
-(** *** *)
-
-(** We can use [filter] to give a concise version of the
- [countoddmembers] function from the [Lists] chapter. *)
-
-Definition countoddmembers' (l:list nat) : nat :=
- length (filter oddb l).
-
-Example test_countoddmembers'1: countoddmembers' [1;0;3;1;4;5] = 4.
-Proof. reflexivity. Qed.
-Example test_countoddmembers'2: countoddmembers' [0;2;4] = 0.
-Proof. reflexivity. Qed.
-Example test_countoddmembers'3: countoddmembers' nil = 0.
-Proof. reflexivity. Qed.
-
-(* ###################################################### *)
-(** ** Anonymous Functions *)
-
-(** It is a little annoying to be forced to define the function
- [length_is_1] and give it a name just to be able to pass it as an
- argument to [filter], since we will probably never use it again.
- Moreover, this is not an isolated example. When using
- higher-order functions, we often want to pass as arguments
- "one-off" functions that we will never use again; having to give
- each of these functions a name would be tedious.
-
- Fortunately, there is a better way. It is also possible to
- construct a function "on the fly" without declaring it at the top
- level or giving it a name; this is analogous to the notation we've
- been using for writing down constant lists, natural numbers, and
- so on. *)
-
-Example test_anon_fun':
- doit3times (fun n => n * n) 2 = 256.
-Proof. reflexivity. Qed.
-
-(** Here is the motivating example from before, rewritten to use
- an anonymous function. *)
-
-Example test_filter2':
- filter (fun l => beq_nat (length l) 1)
- [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]
- = [ [3]; [4]; [8] ].
-Proof. reflexivity. Qed.
-
-(** **** Exercise: 2 stars (filter_even_gt7) *)
-
-(** Use [filter] (instead of [Fixpoint]) to write a Coq function
- [filter_even_gt7] that takes a list of natural numbers as input
- and returns a list of just those that are even and greater than
- 7. *)
-
-Definition filter_even_gt7 (l : list nat) : list nat :=
- (* FILL IN HERE *) admit.
-
-Example test_filter_even_gt7_1 :
- filter_even_gt7 [1;2;6;9;10;3;12;8] = [10;12;8].
- (* FILL IN HERE *) Admitted.
-
-Example test_filter_even_gt7_2 :
- filter_even_gt7 [5;2;6;19;129] = [].
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (partition) *)
-(** Use [filter] to write a Coq function [partition]:
- partition : forall X : Type,
- (X -> bool) -> list X -> list X * list X
- Given a set [X], a test function of type [X -> bool] and a [list
- X], [partition] should return a pair of lists. The first member of
- the pair is the sublist of the original list containing the
- elements that satisfy the test, and the second is the sublist
- containing those that fail the test. The order of elements in the
- two sublists should be the same as their order in the original
- list.
-*)
-
-Definition partition {X : Type} (test : X -> bool) (l : list X)
- : list X * list X :=
-(* FILL IN HERE *) admit.
-
-Example test_partition1: partition oddb [1;2;3;4;5] = ([1;3;5], [2;4]).
-(* FILL IN HERE *) Admitted.
-Example test_partition2: partition (fun x => false) [5;9;0] = ([], [5;9;0]).
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################### *)
-(** ** Map *)
-
-(** Another handy higher-order function is called [map]. *)
-
-Fixpoint map {X Y:Type} (f:X->Y) (l:list X)
- : (list Y) :=
- match l with
- | [] => []
- | h :: t => (f h) :: (map f t)
- end.
-
-(** *** *)
-(** It takes a function [f] and a list [ l = [n1, n2, n3, ...] ]
- and returns the list [ [f n1, f n2, f n3,...] ], where [f] has
- been applied to each element of [l] in turn. For example: *)
-
-Example test_map1: map (plus 3) [2;0;2] = [5;3;5].
-Proof. reflexivity. Qed.
-
-(** The element types of the input and output lists need not be
- the same ([map] takes _two_ type arguments, [X] and [Y]). This
- version of [map] can thus be applied to a list of numbers and a
- function from numbers to booleans to yield a list of booleans: *)
-
-Example test_map2: map oddb [2;1;2;5] = [false;true;false;true].
-Proof. reflexivity. Qed.
-
-(** It can even be applied to a list of numbers and
- a function from numbers to _lists_ of booleans to
- yield a list of lists of booleans: *)
-
-Example test_map3:
- map (fun n => [evenb n;oddb n]) [2;1;2;5]
- = [[true;false];[false;true];[true;false];[false;true]].
-Proof. reflexivity. Qed.
-
-
-
-(** ** Map for options *)
-(** **** Exercise: 3 stars (map_rev) *)
-(** Show that [map] and [rev] commute. You may need to define an
- auxiliary lemma. *)
-
-
-Theorem map_rev : forall (X Y : Type) (f : X -> Y) (l : list X),
- map f (rev l) = rev (map f l).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (flat_map) *)
-(** The function [map] maps a [list X] to a [list Y] using a function
- of type [X -> Y]. We can define a similar function, [flat_map],
- which maps a [list X] to a [list Y] using a function [f] of type
- [X -> list Y]. Your definition should work by 'flattening' the
- results of [f], like so:
- flat_map (fun n => [n;n+1;n+2]) [1;5;10]
- = [1; 2; 3; 5; 6; 7; 10; 11; 12].
-*)
-
-Fixpoint flat_map {X Y:Type} (f:X -> list Y) (l:list X)
- : (list Y) :=
- (* FILL IN HERE *) admit.
-
-Example test_flat_map1:
- flat_map (fun n => [n;n;n]) [1;5;4]
- = [1; 1; 1; 5; 5; 5; 4; 4; 4].
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Lists are not the only inductive type that we can write a
- [map] function for. Here is the definition of [map] for the
- [option] type: *)
-
-Definition option_map {X Y : Type} (f : X -> Y) (xo : option X)
- : option Y :=
- match xo with
- | None => None
- | Some x => Some (f x)
- end.
-
-(** **** Exercise: 2 stars, optional (implicit_args) *)
-(** The definitions and uses of [filter] and [map] use implicit
- arguments in many places. Replace the curly braces around the
- implicit arguments with parentheses, and then fill in explicit
- type parameters where necessary and use Coq to check that you've
- done so correctly. (This exercise is not to be turned in; it is
- probably easiest to do it on a _copy_ of this file that you can
- throw away afterwards.) [] *)
-
-(* ###################################################### *)
-(** ** Fold *)
-
-(** An even more powerful higher-order function is called
- [fold]. This function is the inspiration for the "[reduce]"
- operation that lies at the heart of Google's map/reduce
- distributed programming framework. *)
-
-Fixpoint fold {X Y:Type} (f: X->Y->Y) (l:list X) (b:Y) : Y :=
- match l with
- | nil => b
- | h :: t => f h (fold f t b)
- end.
-
-(** *** *)
-
-(** Intuitively, the behavior of the [fold] operation is to
- insert a given binary operator [f] between every pair of elements
- in a given list. For example, [ fold plus [1;2;3;4] ] intuitively
- means [1+2+3+4]. To make this precise, we also need a "starting
- element" that serves as the initial second input to [f]. So, for
- example,
- fold plus [1;2;3;4] 0
- yields
- 1 + (2 + (3 + (4 + 0))).
- Here are some more examples:
-*)
-
-Check (fold andb).
-(* ===> fold andb : list bool -> bool -> bool *)
-
-Example fold_example1 : fold mult [1;2;3;4] 1 = 24.
-Proof. reflexivity. Qed.
-
-Example fold_example2 : fold andb [true;true;false;true] true = false.
-Proof. reflexivity. Qed.
-
-Example fold_example3 : fold app [[1];[];[2;3];[4]] [] = [1;2;3;4].
-Proof. reflexivity. Qed.
-
-
-(** **** Exercise: 1 star, advanced (fold_types_different) *)
-(** Observe that the type of [fold] is parameterized by _two_ type
- variables, [X] and [Y], and the parameter [f] is a binary operator
- that takes an [X] and a [Y] and returns a [Y]. Can you think of a
- situation where it would be useful for [X] and [Y] to be
- different? *)
-
-(* ###################################################### *)
-(** ** Functions For Constructing Functions *)
-
-(** Most of the higher-order functions we have talked about so
- far take functions as _arguments_. Now let's look at some
- examples involving _returning_ functions as the results of other
- functions.
-
- To begin, here is a function that takes a value [x] (drawn from
- some type [X]) and returns a function from [nat] to [X] that
- yields [x] whenever it is called, ignoring its [nat] argument. *)
-
-Definition constfun {X: Type} (x: X) : nat->X :=
- fun (k:nat) => x.
-
-Definition ftrue := constfun true.
-
-Example constfun_example1 : ftrue 0 = true.
-Proof. reflexivity. Qed.
-
-Example constfun_example2 : (constfun 5) 99 = 5.
-Proof. reflexivity. Qed.
-
-(** *** *)
-(** Similarly, but a bit more interestingly, here is a function
- that takes a function [f] from numbers to some type [X], a number
- [k], and a value [x], and constructs a function that behaves
- exactly like [f] except that, when called with the argument [k],
- it returns [x]. *)
-
-Definition override {X: Type} (f: nat->X) (k:nat) (x:X) : nat->X:=
- fun (k':nat) => if beq_nat k k' then x else f k'.
-
-(** For example, we can apply [override] twice to obtain a
- function from numbers to booleans that returns [false] on [1] and
- [3] and returns [true] on all other arguments. *)
-
-Definition fmostlytrue := override (override ftrue 1 false) 3 false.
-
-(** *** *)
-
-Example override_example1 : fmostlytrue 0 = true.
-Proof. reflexivity. Qed.
-
-Example override_example2 : fmostlytrue 1 = false.
-Proof. reflexivity. Qed.
-
-Example override_example3 : fmostlytrue 2 = true.
-Proof. reflexivity. Qed.
-
-Example override_example4 : fmostlytrue 3 = false.
-Proof. reflexivity. Qed.
-
-(** *** *)
-
-(** **** Exercise: 1 star (override_example) *)
-(** Before starting to work on the following proof, make sure you
- understand exactly what the theorem is saying and can paraphrase
- it in your own words. The proof itself is straightforward. *)
-
-Theorem override_example : forall (b:bool),
- (override (constfun b) 3 true) 2 = b.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** We'll use function overriding heavily in parts of the rest of the
- course, and we will end up needing to know quite a bit about its
- properties. To prove these properties, though, we need to know
- about a few more of Coq's tactics; developing these is the main
- topic of the next chapter. For now, though, let's introduce just
- one very useful tactic that will also help us with proving
- properties of some of the other functions we have introduced in
- this chapter. *)
-
-(* ###################################################### *)
-
-(* ###################################################### *)
-(** * The [unfold] Tactic *)
-
-(** Sometimes, a proof will get stuck because Coq doesn't
- automatically expand a function call into its definition. (This
- is a feature, not a bug: if Coq automatically expanded everything
- possible, our proof goals would quickly become enormous -- hard to
- read and slow for Coq to manipulate!) *)
-
-Theorem unfold_example_bad : forall m n,
- 3 + n = m ->
- plus3 n + 1 = m + 1.
-Proof.
- intros m n H.
-(* At this point, we'd like to do [rewrite -> H], since
- [plus3 n] is definitionally equal to [3 + n]. However,
- Coq doesn't automatically expand [plus3 n] to its
- definition. *)
- Abort.
-
-(** The [unfold] tactic can be used to explicitly replace a
- defined name by the right-hand side of its definition. *)
-
-Theorem unfold_example : forall m n,
- 3 + n = m ->
- plus3 n + 1 = m + 1.
-Proof.
- intros m n H.
- unfold plus3.
- rewrite -> H.
- reflexivity. Qed.
-
-(** Now we can prove a first property of [override]: If we
- override a function at some argument [k] and then look up [k], we
- get back the overridden value. *)
-
-Theorem override_eq : forall {X:Type} x k (f:nat->X),
- (override f k x) k = x.
-Proof.
- intros X x k f.
- unfold override.
- rewrite <- beq_nat_refl.
- reflexivity. Qed.
-
-(** This proof was straightforward, but note that it requires
- [unfold] to expand the definition of [override]. *)
-
-(** **** Exercise: 2 stars (override_neq) *)
-Theorem override_neq : forall (X:Type) x1 x2 k1 k2 (f : nat->X),
- f k1 = x1 ->
- beq_nat k2 k1 = false ->
- (override f k2 x2) k1 = x1.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** As the inverse of [unfold], Coq also provides a tactic
- [fold], which can be used to "unexpand" a definition. It is used
- much less often. *)
-
-(* ##################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 2 stars (fold_length) *)
-(** Many common functions on lists can be implemented in terms of
- [fold]. For example, here is an alternative definition of [length]: *)
-
-Definition fold_length {X : Type} (l : list X) : nat :=
- fold (fun _ n => S n) l 0.
-
-Example test_fold_length1 : fold_length [4;7;0] = 3.
-Proof. reflexivity. Qed.
-
-(** Prove the correctness of [fold_length]. *)
-
-Theorem fold_length_correct : forall X (l : list X),
- fold_length l = length l.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (fold_map) *)
-(** We can also define [map] in terms of [fold]. Finish [fold_map]
- below. *)
-
-Definition fold_map {X Y:Type} (f : X -> Y) (l : list X) : list Y :=
-(* FILL IN HERE *) admit.
-
-(** Write down a theorem in Coq stating that [fold_map] is correct,
- and prove it. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* $Date: 2013-09-26 14:40:26 -0400 (Thu, 26 Sep 2013) $ *)
-
diff --git a/Preface.html b/Preface.html
deleted file mode 100644
index 9ef2d13..0000000
--- a/Preface.html
+++ /dev/null
@@ -1,663 +0,0 @@
-
-
-
-
-
-Preface
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Preface
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Welcome
-
-
-
- This electronic book is a course on Software Foundations, the
- mathematical underpinnings of reliable software. Topics include
- basic concepts of logic, computer-assisted theorem proving and the
- Coq proof assistant, functional programming, operational
- semantics, Hoare logic, and static type systems. The exposition
- is intended for a broad range of readers, from advanced
- undergraduates to PhD students and researchers. No specific
- background in logic or programming languages is assumed, though a
- degree of mathematical maturity will be helpful.
-
-
-
- One novelty of the course is that it is one hundred per cent
- formalized and machine-checked: the entire text is literally a
- script for Coq. It is intended to be read alongside an
- interactive session with Coq. All the details in the text are
- fully formalized in Coq, and the exercises are designed to be
- worked using Coq.
-
-
-
- The files are organized into a sequence of core chapters, covering
- about one semester's worth of material and organized into a
- coherent linear narrative, plus a number of "appendices" covering
- additional topics. All the core chapters are suitable for both
- graduate and upper-level undergraduate students.
-
-
-
-
-
-
-
-
Overview
-
-
-
- Building reliable software is hard. The scale and complexity of
- modern software systems, the number of people involved in building
- them, and the range of demands placed on them make it extremely
- difficult to build software that works as intended, even most of
- the time. At the same time, the increasing degree to which
- software is woven into almost every aspect of our society
- continually amplifies the cost of bugs and insecurities.
-
-
-
- Computer science and software engineering have responded to these
- challenges by developing a whole host of techniques for improving
- software reliability, ranging from recommendations about managing
- software projects and structuring programming teams (e.g., extreme
- programming) to design philosophies for libraries (e.g.,
- model-view-controller, publish-subscribe, etc.) and programming
- languages (e.g., object-oriented programming, aspect-oriented
- programming, functional programming), to mathematical techniques
- for specifying and reasoning about properties of software and
- tools for helping validate these properties.
-
-
-
- The present course is focused on this last set of techniques. The
- text weaves together five conceptual threads:
-
-
-
- (1) basic tools from logic for making and justifying precise
- claims about programs;
-
-
-
- (2) the use of proof assistants to construct rigorous logical
- arguments;
-
-
-
- (3) the idea of functional programming, both as a method of
- programming and as a bridge between programming and logic;
-
-
-
- (4) formal techniques for reasoning about the properties of
- specific programs (e.g., that a loop terminates on all
- inputs, or that a sorting function actually fulfills its
- specification); and
-
-
-
- (5) the use of type systems for establishing well-behavedness
- guarantees for all programs in a given programming
- language (e.g., the fact that well-typed Java programs cannot
- be subverted at runtime).
-
-
-
- Each of these topics is easily rich enough to fill a whole course
- in its own right; taking all of them together naturally means that
- much will be left unsaid. But we hope readers will agree that the
- themes illuminate and amplify each other in useful ways, and that
- bringing them together creates a foundation from which it will be
- easy to dig into any of them more deeply. Some suggestions for
- supplemental texts can be found in the Postscript chapter.
-
-
-
Logic
-
-
-
- Logic is the field of study whose subject matter is proofs —
- unassailable arguments for the truth of particular propositions.
-
-
-
- Volumes have been written about the central role of logic in
- computer science. Manna and Waldinger called it "the calculus of
- computer science," while Halpern et al.'s paper On the Unusual
- Effectiveness of Logic in Computer Science catalogs scores of
- ways in which logic offers critical tools and insights.
-
-
-
- In particular, the fundamental notion of inductive proofs is
- ubiquitous in all of computer science. You have surely seen them
- before, in contexts from discrete math to analysis of algorithms,
- but in this course we will examine them much more deeply than you
- have probably done so far.
-
-
-
Proof Assistants
-
-
-
- The flow of ideas between logic and computer science has not gone
- only one way: CS has made its own contributions to logic. One of
- these has been the development of tools for constructing proofs of
- logical propositions. These tools fall into two broad categories:
-
-
-
-
-
Automated theorem provers provide "push-button" operation:
- you give them a proposition and they return either true,
- false, or ran out of time. Although their capabilities
- are limited to fairly specific sorts of reasoning, they have
- matured enough to be useful now in a huge variety of
- settings. Examples of such tools include SAT solvers, SMT
- solvers, and model checkers.
-
-
-
-
-
-
Proof assistants are hybrid tools that try to automate the
- more routine aspects of building proofs while depending on
- human guidance for more difficult aspects. Widely used proof
- assistants include Isabelle, Agda, Twelf, ACL2, PVS, and Coq,
- among many others.
-
-
-
-
-
-
- This course is based around Coq, a proof assistant that has been
- under development since 1983 at a number of French research labs
- and universities. Coq provides a rich environment for interactive
- development of machine-checked formal reasoning. The kernel of
- the Coq system is a simple proof-checker which guarantees that
- only correct deduction steps are performed. On top of this
- kernel, the Coq environment provides high-level facilities for
- proof development, including powerful tactics for constructing
- complex proofs semi-automatically, and a large library of common
- definitions and lemmas.
-
-
-
- Coq has been a critical enabler for a huge variety of work across
- computer science and mathematics.
-
-
-
-
-
As a platform for the modeling of programming languages, it has
- become a standard tool for researchers who need to describe and
- reason about complex language definitions. It has been used,
- for example, to check the security of the JavaCard platform,
- obtaining the highest level of common criteria certification,
- and for formal specifications of the x86 and LLVM instruction
- sets.
-
-
-
-
-
-
As an environment for the development of formally certified
- programs, Coq has been used to build CompCert, a fully-verified
- optimizing compiler for C, for proving the correctness of subtle
- algorithms involving floating point numbers, and as the basis
- for Certicrypt, an environment for reasoning about the security
- of cryptographic algorithms.
-
-
-
-
-
-
As a realistic environment for experiments with programming with
- dependent types, it has inspired numerous innovations. For
- example, the Ynot project at Harvard embeds "relational Hoare
- reasoning" (an extension of the Hoare Logic we will see later
- in this course) in Coq.
-
-
-
-
-
-
As a proof assistant for higher-order logic, it has been used to
- validate a number of important results in mathematics. For
- example, its ability to include complex computations inside
- proofs made it possible to develop the first formally verified
- proof of the 4-color theorem. This proof had previously been
- controversial among mathematicians because part of it included
- checking a large number of configurations using a program. In
- the Coq formalization, everything is checked, including the
- correctness of the computational part. More recently, an even
- more massive effort led to a Coq formalization of the
- Feit-Thompson Theorem — the first major step in the
- classification of finite simple groups.
-
-
-
-
-
-
- By the way, in case you're wondering about the name, here's what
- the official Coq web site says: "Some French computer scientists
- have a tradition of naming their software as animal species: Caml,
- Elan, Foc or Phox are examples of this tacit convention. In French,
- “coq” means rooster, and it sounds like the initials of the
- Calculus of Constructions CoC on which it is based." The rooster
- is also the national symbol of France, and "Coq" are the first
- three letters of the name of Thierry Coquand, one of Coq's early
- developers.
-
-
-
Functional Programming
-
-
-
- The term functional programming refers both to a collection of
- programming idioms that can be used in almost any programming
- language and to a particular family of programming languages that are
- designed to emphasize these idioms, including Haskell, OCaml,
- Standard ML, F#, Scala, Scheme, Racket, Common Lisp, Clojure,
- Erlang, and Coq.
-
-
-
- Functional programming has been developed by researchers over many
- decades — indeed, its roots go back to Church's lambda-calculus,
- developed in the 1930s before the era of the computer began! But
- in the past two decades it has enjoyed a surge of interest among
- industrial engineers and language designers, playing a key role in
- high-value systems at companies like Jane St. Capital, Microsoft,
- Facebook, and Ericsson.
-
-
-
- The most basic tenet of functional programming is that, as much as
- possible, computation should be pure: the only effect of running
- a computation should be to produce a result; the computation
- should be free from side effects such as I/O, assignments to
- mutable variables, or redirecting pointers. For example, whereas
- an imperative sorting function might take a list of numbers and
- rearrange the pointers to put the list in order, a pure sorting
- function would take the original list and return a new list
- containing the same numbers in sorted order.
-
-
-
- One significant benefit of this style of programming is that it
- makes programs easier to understand and reason about. If every
- operation on a data structure yields a new data structure, leaving
- the old one intact, then there is no need to worry about where
- else in the program the structure is being shared, whether a
- change by one part of the program might break an invariant that
- another part of the program thinks is being enforced. These
- considerations are particularly critical in concurrent programs,
- where any mutable state that is shared between threads is a
- potential source of pernicious bugs. Indeed, a large part of the
- recent interest in functional programming in industry is due to its
- simple behavior in the presence of concurrency.
-
-
-
- Another reason for the current excitement about functional
- programming is related to this one: functional programs are often
- much easier to parallelize than their imperative counterparts. If
- running a computation has no effect other than producing a result,
- then it can be run anywhere. If a data structure is never
- modified in place, it can be copied freely, across cores or across
- the network. Indeed, the MapReduce idiom that lies at the heart
- of massively distributed query processors like Hadoop and is used
- at Google to index the entire web is an instance of functional
- programming.
-
-
-
- For purposes of this course, functional programming has one other
- significant attraction: it serves as a bridge between logic and
- computer science. Indeed, Coq itself can be seen as a combination
- of a small but extremely expressive functional programming
- language, together with a set of tools for stating and proving
- logical assertions. However, when we come to look more closely,
- we will find that these two sides of Coq are actually aspects of
- the very same underlying machinery — i.e., proofs are programs.
-
-
-
Program Verification
-
-
-
- The first third of the book is devoted to developing the
- conceptual framework of logic and functional programming and to
- gaining enough fluency with the essentials of Coq to use it for
- modeling and reasoning about nontrivial artifacts. From this
- point on, we will increasingly turn our attention to two broad
- topics of critical importance to the enterprise of building
- reliable software (and hardware!): techniques for proving specific
- properties of particular programs and for proving general
- properties of whole programming languages.
-
-
-
- For both of these, the first thing we need is a way of
- representing programs as mathematical objects (so we can talk
- about them precisely) and of describing their behavior in terms of
- mathematical functions or relations. Our tools for these tasks
- will be abstract syntax and operational semantics, a method of
- specifying the behavior of programs by writing abstract
- interpreters. At the beginning, we will work with operational
- semantics in the so-called "big-step" style, which leads to
- somewhat simpler and more readable definitions, in those cases
- where it is applicable. Later on, we will switch to a more
- detailed "small-step" style, which helps make some useful
- distinctions between different sorts of "nonterminating" program
- behaviors and which can be applied to a broader range of language
- features, including concurrency.
-
-
-
- The first programming language we consider in detail is Imp, a
- tiny toy language capturing the most fundamental features of
- conventional imperative languages: variables, assignment,
- conditionals, and loops. We study two different ways of reasoning
- about the properties of Imp programs.
-
-
-
- First, we consider what it means to say that two Imp programs are
- equivalent in the sense that they give the same behaviors for
- all initial memories. This notion of equivalence then becomes a
- criterion for judging the correctness of metaprograms —
- programs that manipulate other programs, such as compilers and
- optimizers. We build a simple optimizer for Imp and prove that it
- is correct.
-
-
-
- Second, we develop a methodology for proving that Imp programs
- satisfy some formal specification of their behavior. We introduce
- the notion of Hoare triples — Imp programs annotated with pre-
- and post-conditions describing what should be true about the
- memory in which they are started and what they promise to make
- true about the memory in which they terminate — and the reasoning
- principles of Hoare Logic, a "domain-specific logic" specialized
- for convenient compositional reasoning about imperative programs,
- with concepts like "loop invariant" built in.
-
-
-
- This part of the course will give you a taste of the key ideas and
- mathematical tools used for a wide variety of real-world software
- and hardware verification tasks.
-
-
-
-
-
-
-
Type Systems
-
-
-
- Our final major topic, covering the last third of the course, is
- type systems, a powerful set of tools for establishing
- properties of all programs in a given language.
-
-
-
- Type systems are the best established and most popular example of
- a highly successful class of formal verification techniques known
- as lightweight formal methods. These are reasoning techniques
- of modest power — modest enough that automatic checkers can be
- built into compilers, linkers, or program analyzers and thus be
- applied even by programmers unfamiliar with the underlying
- theories. (Other examples of lightweight formal methods include
- hardware and software model checkers and run-time property
- monitoring, a collection of techniques that allow a system to
- detect, dynamically, when one of its components is not behaving
- according to specification).
-
-
-
- In a sense, this topic brings us full circle: the language whose
- properties we study in this part, called the simply typed
- lambda-calculus, is essentially a simplified model of the core of
- Coq itself!
-
-
-
-
-
-
-
-
-
-
-
-
Practicalities
-
-
-
-
-
-
-
-
-
System Requirements
-
-
-
- Coq runs on Windows, Linux, and OS X. You will need:
-
-
-
-
-
A current installation of Coq, available from the Coq home
- page. Everything should work with version 8.4.
-
-
-
-
-
-
An IDE for interacting with Coq. Currently, there are two
- choices:
-
-
-
-
-
Proof General is an Emacs-based IDE. It tends to be
- preferred by users who are already comfortable with
- Emacs. It requires a separate installation (google
- "Proof General").
-
-
-
-
-
-
CoqIDE is a simpler stand-alone IDE. It is distributed
- with Coq, but on some platforms compiling it involves
- installing additional packages for GUI libraries and
- such.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Exercises
-
-
-
- Each chapter includes numerous exercises. Each is marked with a
- "star rating," which can be interpreted as follows:
-
-
-
-
-
One star: easy exercises that underscore points in the text
- and that, for most readers, should take only a minute or two.
- Get in the habit of working these as you reach them.
-
-
-
-
-
-
Two stars: straightforward exercises (five or ten minutes).
-
-
-
-
-
-
Three stars: exercises requiring a bit of thought (ten
- minutes to half an hour).
-
-
-
-
-
-
Four and five stars: more difficult exercises (half an hour
- and up).
-
-
-
-
-
-
- Also, some exercises are marked "advanced", and some are marked
- "optional." Doing just the non-optional, non-advanced exercises
- should provide good coverage of the core material. "Advanced"
- exercises are for readers who want an extra challenge (and, in
- return, a deeper contact with the material). "Optional" exercises
- provide a bit of extra practice with key concepts and introduce
- secondary themes that may be of interest to some readers.
-
-
-
-
-
-
-
-
Chapter Dependencies
-
-
-
- A diagram of the dependencies between chapters and some suggested
- paths through the material can be found in the file deps.html.
-
-
-
-
-
-
-
-
Downloading the Coq Files
-
-
-
- A tar file containing the full sources for the "release version"
- of these notes (as a collection of Coq scripts and HTML files) is
- available here:
-
- http://www.cis.upenn.edu/~bcpierce/sf
-
- If you are using the notes as part of a class, you may be given
- access to a locally extended version of the files, which you
- should use instead of the release version.
-
-
-
-
-
-
-
-
-
Note for Instructors
-
-
-
- If you intend to use these materials in your own course, you will
- undoubtedly find things you'd like to change, improve, or add.
- Your contributions are welcome!
-
-
-
- Please send an email to Benjamin Pierce, and we'll set you up with
- read/write access to our subversion repository and developers'
- mailing list; in the repository you'll find a README with further
- instructions.
-
-
-
-
-
-
-
-
Translations
-
-
-
- Thanks to the efforts of a team of volunteer translators, Software
- Foundations can now be enjoyed in Japanese:
-
-
-
-
-
\ No newline at end of file
diff --git a/Preface.v b/Preface.v
deleted file mode 100644
index 1eee244..0000000
--- a/Preface.v
+++ /dev/null
@@ -1,412 +0,0 @@
-(** * Preface *)
-
-(* ###################################################################### *)
-(** * Welcome *)
-
-(** This electronic book is a course on _Software Foundations_, the
- mathematical underpinnings of reliable software. Topics include
- basic concepts of logic, computer-assisted theorem proving and the
- Coq proof assistant, functional programming, operational
- semantics, Hoare logic, and static type systems. The exposition
- is intended for a broad range of readers, from advanced
- undergraduates to PhD students and researchers. No specific
- background in logic or programming languages is assumed, though a
- degree of mathematical maturity will be helpful.
-
- One novelty of the course is that it is one hundred per cent
- formalized and machine-checked: the entire text is literally a
- script for Coq. It is intended to be read alongside an
- interactive session with Coq. All the details in the text are
- fully formalized in Coq, and the exercises are designed to be
- worked using Coq.
-
- The files are organized into a sequence of core chapters, covering
- about one semester's worth of material and organized into a
- coherent linear narrative, plus a number of "appendices" covering
- additional topics. All the core chapters are suitable for both
- graduate and upper-level undergraduate students. *)
-
-
-(* ###################################################################### *)
-(** * Overview *)
-
-(** Building reliable software is hard. The scale and complexity of
- modern software systems, the number of people involved in building
- them, and the range of demands placed on them make it extremely
- difficult to build software that works as intended, even most of
- the time. At the same time, the increasing degree to which
- software is woven into almost every aspect of our society
- continually amplifies the cost of bugs and insecurities.
-
- Computer science and software engineering have responded to these
- challenges by developing a whole host of techniques for improving
- software reliability, ranging from recommendations about managing
- software projects and structuring programming teams (e.g., extreme
- programming) to design philosophies for libraries (e.g.,
- model-view-controller, publish-subscribe, etc.) and programming
- languages (e.g., object-oriented programming, aspect-oriented
- programming, functional programming), to mathematical techniques
- for specifying and reasoning about properties of software and
- tools for helping validate these properties.
-
- The present course is focused on this last set of techniques. The
- text weaves together five conceptual threads:
-
- (1) basic tools from _logic_ for making and justifying precise
- claims about programs;
-
- (2) the use of _proof assistants_ to construct rigorous logical
- arguments;
-
- (3) the idea of _functional programming_, both as a method of
- programming and as a bridge between programming and logic;
-
- (4) formal techniques for _reasoning about the properties of
- specific programs_ (e.g., that a loop terminates on all
- inputs, or that a sorting function actually fulfills its
- specification); and
-
- (5) the use of _type systems_ for establishing well-behavedness
- guarantees for _all_ programs in a given programming
- language (e.g., the fact that well-typed Java programs cannot
- be subverted at runtime).
-
- Each of these topics is easily rich enough to fill a whole course
- in its own right; taking all of them together naturally means that
- much will be left unsaid. But we hope readers will agree that the
- themes illuminate and amplify each other in useful ways, and that
- bringing them together creates a foundation from which it will be
- easy to dig into any of them more deeply. Some suggestions for
- supplemental texts can be found in the [Postscript] chapter. *)
-
-(** ** Logic *)
-
-(** Logic is the field of study whose subject matter is _proofs_ --
- unassailable arguments for the truth of particular propositions.
-
- Volumes have been written about the central role of logic in
- computer science. Manna and Waldinger called it "the calculus of
- computer science," while Halpern et al.'s paper _On the Unusual
- Effectiveness of Logic in Computer Science_ catalogs scores of
- ways in which logic offers critical tools and insights.
-
- In particular, the fundamental notion of inductive proofs is
- ubiquitous in all of computer science. You have surely seen them
- before, in contexts from discrete math to analysis of algorithms,
- but in this course we will examine them much more deeply than you
- have probably done so far. *)
-
-(** ** Proof Assistants *)
-
-(** The flow of ideas between logic and computer science has not gone
- only one way: CS has made its own contributions to logic. One of
- these has been the development of tools for constructing proofs of
- logical propositions. These tools fall into two broad categories:
-
- - _Automated theorem provers_ provide "push-button" operation:
- you give them a proposition and they return either _true_,
- _false_, or _ran out of time_. Although their capabilities
- are limited to fairly specific sorts of reasoning, they have
- matured enough to be useful now in a huge variety of
- settings. Examples of such tools include SAT solvers, SMT
- solvers, and model checkers.
-
- - _Proof assistants_ are hybrid tools that try to automate the
- more routine aspects of building proofs while depending on
- human guidance for more difficult aspects. Widely used proof
- assistants include Isabelle, Agda, Twelf, ACL2, PVS, and Coq,
- among many others.
-
- This course is based around Coq, a proof assistant that has been
- under development since 1983 at a number of French research labs
- and universities. Coq provides a rich environment for interactive
- development of machine-checked formal reasoning. The kernel of
- the Coq system is a simple proof-checker which guarantees that
- only correct deduction steps are performed. On top of this
- kernel, the Coq environment provides high-level facilities for
- proof development, including powerful tactics for constructing
- complex proofs semi-automatically, and a large library of common
- definitions and lemmas.
-
- Coq has been a critical enabler for a huge variety of work across
- computer science and mathematics.
-
- - As a platform for the modeling of programming languages, it has
- become a standard tool for researchers who need to describe and
- reason about complex language definitions. It has been used,
- for example, to check the security of the JavaCard platform,
- obtaining the highest level of common criteria certification,
- and for formal specifications of the x86 and LLVM instruction
- sets.
-
- - As an environment for the development of formally certified
- programs, Coq has been used to build CompCert, a fully-verified
- optimizing compiler for C, for proving the correctness of subtle
- algorithms involving floating point numbers, and as the basis
- for Certicrypt, an environment for reasoning about the security
- of cryptographic algorithms.
-
- - As a realistic environment for experiments with programming with
- dependent types, it has inspired numerous innovations. For
- example, the Ynot project at Harvard embeds "relational Hoare
- reasoning" (an extension of the _Hoare Logic_ we will see later
- in this course) in Coq.
-
- - As a proof assistant for higher-order logic, it has been used to
- validate a number of important results in mathematics. For
- example, its ability to include complex computations inside
- proofs made it possible to develop the first formally verified
- proof of the 4-color theorem. This proof had previously been
- controversial among mathematicians because part of it included
- checking a large number of configurations using a program. In
- the Coq formalization, everything is checked, including the
- correctness of the computational part. More recently, an even
- more massive effort led to a Coq formalization of the
- Feit-Thompson Theorem -- the first major step in the
- classification of finite simple groups.
-
- By the way, in case you're wondering about the name, here's what
- the official Coq web site says: "Some French computer scientists
- have a tradition of naming their software as animal species: Caml,
- Elan, Foc or Phox are examples of this tacit convention. In French,
- “coq” means rooster, and it sounds like the initials of the
- Calculus of Constructions CoC on which it is based." The rooster
- is also the national symbol of France, and "Coq" are the first
- three letters of the name of Thierry Coquand, one of Coq's early
- developers. *)
-
-(** ** Functional Programming *)
-
-(** The term _functional programming_ refers both to a collection of
- programming idioms that can be used in almost any programming
- language and to a particular family of programming languages that are
- designed to emphasize these idioms, including Haskell, OCaml,
- Standard ML, F##, Scala, Scheme, Racket, Common Lisp, Clojure,
- Erlang, and Coq.
-
- Functional programming has been developed by researchers over many
- decades -- indeed, its roots go back to Church's lambda-calculus,
- developed in the 1930s before the era of the computer began! But
- in the past two decades it has enjoyed a surge of interest among
- industrial engineers and language designers, playing a key role in
- high-value systems at companies like Jane St. Capital, Microsoft,
- Facebook, and Ericsson.
-
- The most basic tenet of functional programming is that, as much as
- possible, computation should be _pure_: the only effect of running
- a computation should be to produce a result; the computation
- should be free from _side effects_ such as I/O, assignments to
- mutable variables, or redirecting pointers. For example, whereas
- an _imperative_ sorting function might take a list of numbers and
- rearrange the pointers to put the list in order, a pure sorting
- function would take the original list and return a _new_ list
- containing the same numbers in sorted order.
-
- One significant benefit of this style of programming is that it
- makes programs easier to understand and reason about. If every
- operation on a data structure yields a new data structure, leaving
- the old one intact, then there is no need to worry about where
- else in the program the structure is being shared, whether a
- change by one part of the program might break an invariant that
- another part of the program thinks is being enforced. These
- considerations are particularly critical in concurrent programs,
- where any mutable state that is shared between threads is a
- potential source of pernicious bugs. Indeed, a large part of the
- recent interest in functional programming in industry is due to its
- simple behavior in the presence of concurrency.
-
- Another reason for the current excitement about functional
- programming is related to this one: functional programs are often
- much easier to parallelize than their imperative counterparts. If
- running a computation has no effect other than producing a result,
- then it can be run anywhere. If a data structure is never
- modified in place, it can be copied freely, across cores or across
- the network. Indeed, the MapReduce idiom that lies at the heart
- of massively distributed query processors like Hadoop and is used
- at Google to index the entire web is an instance of functional
- programming.
-
- For purposes of this course, functional programming has one other
- significant attraction: it serves as a bridge between logic and
- computer science. Indeed, Coq itself can be seen as a combination
- of a small but extremely expressive functional programming
- language, together with a set of tools for stating and proving
- logical assertions. However, when we come to look more closely,
- we will find that these two sides of Coq are actually aspects of
- the very same underlying machinery -- i.e., _proofs are programs_. *)
-
-(** ** Program Verification *)
-
-(** The first third of the book is devoted to developing the
- conceptual framework of logic and functional programming and to
- gaining enough fluency with the essentials of Coq to use it for
- modeling and reasoning about nontrivial artifacts. From this
- point on, we will increasingly turn our attention to two broad
- topics of critical importance to the enterprise of building
- reliable software (and hardware!): techniques for proving specific
- properties of particular _programs_ and for proving general
- properties of whole programming _languages_.
-
- For both of these, the first thing we need is a way of
- representing programs as mathematical objects (so we can talk
- about them precisely) and of describing their behavior in terms of
- mathematical functions or relations. Our tools for these tasks
- will be _abstract syntax_ and _operational semantics_, a method of
- specifying the behavior of programs by writing abstract
- interpreters. At the beginning, we will work with operational
- semantics in the so-called "big-step" style, which leads to
- somewhat simpler and more readable definitions, in those cases
- where it is applicable. Later on, we will switch to a more
- detailed "small-step" style, which helps make some useful
- distinctions between different sorts of "nonterminating" program
- behaviors and which can be applied to a broader range of language
- features, including concurrency.
-
- The first programming language we consider in detail is Imp, a
- tiny toy language capturing the most fundamental features of
- conventional imperative languages: variables, assignment,
- conditionals, and loops. We study two different ways of reasoning
- about the properties of Imp programs.
-
- First, we consider what it means to say that two Imp programs are
- _equivalent_ in the sense that they give the same behaviors for
- all initial memories. This notion of equivalence then becomes a
- criterion for judging the correctness of _metaprograms_ --
- programs that manipulate other programs, such as compilers and
- optimizers. We build a simple optimizer for Imp and prove that it
- is correct.
-
- Second, we develop a methodology for proving that Imp programs
- satisfy some formal specification of their behavior. We introduce
- the notion of _Hoare triples_ -- Imp programs annotated with pre-
- and post-conditions describing what should be true about the
- memory in which they are started and what they promise to make
- true about the memory in which they terminate -- and the reasoning
- principles of _Hoare Logic_, a "domain-specific logic" specialized
- for convenient compositional reasoning about imperative programs,
- with concepts like "loop invariant" built in.
-
- This part of the course will give you a taste of the key ideas and
- mathematical tools used for a wide variety of real-world software
- and hardware verification tasks.
-
-*)
-
-(** ** Type Systems *)
-
-(** Our final major topic, covering the last third of the course, is
- _type systems_, a powerful set of tools for establishing
- properties of _all_ programs in a given language.
-
- Type systems are the best established and most popular example of
- a highly successful class of formal verification techniques known
- as _lightweight formal methods_. These are reasoning techniques
- of modest power -- modest enough that automatic checkers can be
- built into compilers, linkers, or program analyzers and thus be
- applied even by programmers unfamiliar with the underlying
- theories. (Other examples of lightweight formal methods include
- hardware and software model checkers and run-time property
- monitoring, a collection of techniques that allow a system to
- detect, dynamically, when one of its components is not behaving
- according to specification).
-
- In a sense, this topic brings us full circle: the language whose
- properties we study in this part, called the _simply typed
- lambda-calculus_, is essentially a simplified model of the core of
- Coq itself!
-
-*)
-
-(* ###################################################################### *)
-(** * Practicalities *)
-
-(* ###################################################################### *)
-(** ** System Requirements *)
-
-(** Coq runs on Windows, Linux, and OS X. You will need:
-
- - A current installation of Coq, available from the Coq home
- page. Everything should work with version 8.4.
-
- - An IDE for interacting with Coq. Currently, there are two
- choices:
-
- - Proof General is an Emacs-based IDE. It tends to be
- preferred by users who are already comfortable with
- Emacs. It requires a separate installation (google
- "Proof General").
-
- - CoqIDE is a simpler stand-alone IDE. It is distributed
- with Coq, but on some platforms compiling it involves
- installing additional packages for GUI libraries and
- such. *)
-
-(* ###################################################################### *)
-(** ** Exercises *)
-
-(** Each chapter includes numerous exercises. Each is marked with a
- "star rating," which can be interpreted as follows:
-
- - One star: easy exercises that underscore points in the text
- and that, for most readers, should take only a minute or two.
- Get in the habit of working these as you reach them.
-
- - Two stars: straightforward exercises (five or ten minutes).
-
- - Three stars: exercises requiring a bit of thought (ten
- minutes to half an hour).
-
- - Four and five stars: more difficult exercises (half an hour
- and up).
-
- Also, some exercises are marked "advanced", and some are marked
- "optional." Doing just the non-optional, non-advanced exercises
- should provide good coverage of the core material. "Advanced"
- exercises are for readers who want an extra challenge (and, in
- return, a deeper contact with the material). "Optional" exercises
- provide a bit of extra practice with key concepts and introduce
- secondary themes that may be of interest to some readers. *)
-
-(* ###################################################################### *)
-(** ** Chapter Dependencies *)
-
-(** A diagram of the dependencies between chapters and some suggested
- paths through the material can be found in the file [deps.html]. *)
-
-(* ###################################################################### *)
-(** ** Downloading the Coq Files *)
-
-(** A tar file containing the full sources for the "release version"
- of these notes (as a collection of Coq scripts and HTML files) is
- available here:
-<<
- http://www.cis.upenn.edu/~bcpierce/sf
->>
- If you are using the notes as part of a class, you may be given
- access to a locally extended version of the files, which you
- should use instead of the release version.
-*)
-
-(* ###################################################################### *)
-(** * Note for Instructors *)
-
-(** If you intend to use these materials in your own course, you will
- undoubtedly find things you'd like to change, improve, or add.
- Your contributions are welcome!
-
- Please send an email to Benjamin Pierce, and we'll set you up with
- read/write access to our subversion repository and developers'
- mailing list; in the repository you'll find a [README] with further
- instructions. *)
-
-(* ###################################################################### *)
-(** * Translations *)
-
-(** Thanks to the efforts of a team of volunteer translators, _Software
- Foundations_ can now be enjoyed in Japanese:
-
- - http://proofcafe.org/sf
-*)
-
-(* $Date: 2014-06-05 07:22:21 -0400 (Thu, 05 Jun 2014) $ *)
-
diff --git a/ProofObjects.html b/ProofObjects.html
deleted file mode 100644
index 4242a19..0000000
--- a/ProofObjects.html
+++ /dev/null
@@ -1,906 +0,0 @@
-
-
-
-
-
-ProofObjects: Working with Explicit Evidence in Coq
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ProofObjectsWorking with Explicit Evidence in Coq
-
-
-
-
-
-
-
-
-
-
-RequireExportMoreLogic.
-
-
-
-
-
-
-
- We have seen that Coq has mechanisms both for programming,
- using inductive data types (like nat or list) and functions
- over these types, and for proving properties of these programs,
- using inductive propositions (like ev or eq), implication, and
- universal quantification. So far, we have treated these mechanisms
- as if they were quite separate, and for many purposes this is
- a good way to think. But we have also seen hints that Coq's programming and
- proving facilities are closely related. For example, the
- keyword Inductive is used to declare both data types and
- propositions, and → is used both to describe the type of
- functions on data and logical implication. This is not just a
- syntactic accident! In fact, programs and proofs in Coq are almost
- the same thing. In this chapter we will study how this works.
-
-
-
- We have already seen the fundamental idea: provability in Coq is
- represented by concrete evidence. When we construct the proof
- of a basic proposition, we are actually building a tree of evidence,
- which can be thought of as a data structure. If the proposition
- is an implication like A→B, then its proof will be an
- evidence transformer: a recipe for converting evidence for
- A into evidence for B. So at a fundamental level, proofs are simply
- programs that manipulate evidence.
-
-
-
- Q. If evidence is data, what are propositions themselves?
-
-
-
- A. They are types!
-
-
-
- Look again at the formal definition of the beautiful property.
-
-
- The trick is to introduce an alternative pronunciation of ":".
- Instead of "has type," we can also say "is a proof of." For
- example, the second line in the definition of beautiful declares
- that b_0:beautiful0. Instead of "b_0 has type
- beautiful0," we can say that "b_0 is a proof of beautiful0."
- Similarly for b_3 and b_5.
-
-
-
-
-
-
- This pun between types and propositions (between : as "has type"
- and : as "is a proof of" or "is evidence for") is called the
- Curry-Howard correspondence. It proposes a deep connection
- between the world of logic and the world of computation.
-
- propositions ~ types
- proofs ~ data values
-
- Many useful insights follow from this connection. To begin with, it
- gives us a natural interpretation of the type of b_sum constructor:
-
-
-
-
-Checkb_sum.
-(* ===> b_sum : forall n m,
- beautiful n ->
- beautiful m ->
- beautiful (n+m) *)
-
-
-
-This can be read "b_sum is a constructor that takes four
- arguments — two numbers, n and m, and two pieces of evidence,
- for the propositions beautifuln and beautifulm, respectively —
- and yields evidence for the proposition beautiful(n+m)."
-
-
- Now let's look again at a previous proof involving beautiful.
-
-The expression b_sum35b_3b_5 can be thought of as
- instantiating the parameterized constructor b_sum with the
- specific arguments 35 and the corresponding proof objects for
- its premises beautiful3 and beautiful5 (Coq is smart enough
- to figure out that 3+5=8). Alternatively, we can think of b_sum
- as a primitive "evidence constructor" that, when applied to two
- particular numbers, wants to be further applied to evidence that
- those two numbers are beautiful; its type,
-
-
-
-
- ∀nm, beautifuln→beautifulm→beautiful (n+m),
-
-
-
- expresses this functionality, in the same way that the polymorphic
- type ∀X,listX in the previous chapter expressed the fact
- that the constructor nil can be thought of as a function from
- types to empty lists with elements of that type.
-
-
- This gives us an alternative way to write the proof that 8 is
- beautiful:
-
-Notice that we're using apply here in a new way: instead of just
- supplying the name of a hypothesis or previously proved theorem
- whose type matches the current goal, we are supplying an
- expression that directly builds evidence with the required
- type.
-
-
-
-
-
-
-
-
Proof Scripts and Proof Objects
-
-
-
- These proof objects lie at the core of how Coq operates.
-
-
-
- When Coq is following a proof script, what is happening internally
- is that it is gradually constructing a proof object — a term
- whose type is the proposition being proved. The tactics between
- the Proof command and the Qed instruct Coq how to build up a
- term of the required type. To see this process in action, let's
- use the ShowProof command to display the current state of the
- proof tree at various points in the following tactic proof.
-
-At any given moment, Coq has constructed a term with some
- "holes" (indicated by ?1, ?2, and so on), and it knows what
- type of evidence is needed at each hole.
-
-
-
-
-
- Each of the holes corresponds to a subgoal, and the proof is
- finished when there are no more subgoals. At this point, the
- Theorem command gives a name to the evidence we've built and
- stores it in the global context.
-
-
- Tactic proofs are useful and convenient, but they are not
- essential: in principle, we can always construct the required
- evidence by hand, as shown above. Then we can use Definition
- (rather than Theorem) to give a global name directly to a
- piece of evidence.
-
- Give a tactic proof and a proof object showing that 6 is beautiful.
-
-
-
-
-Theoremsix_is_beautiful :
- beautiful 6.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Definitionsix_is_beautiful' : beautiful 6 :=
- (* FILL IN HERE *)admit.
-
-
-
-☐
-
-
-
Exercise: 1 star (nine_is_beautiful)
- Give a tactic proof and a proof object showing that 9 is beautiful.
-
-
-
-
-Theoremnine_is_beautiful :
- beautiful 9.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Definitionnine_is_beautiful' : beautiful 9 :=
- (* FILL IN HERE *)admit.
-
-
-
-☐
-
-
-
-
-
-
-
-
Quantification, Implications and Functions
-
-
-
- In Coq's computational universe (where we've mostly been living
- until this chapter), there are two sorts of values with arrows in
- their types: constructors introduced by Inductive-ly defined
- data types, and functions.
-
-
-
- Similarly, in Coq's logical universe, there are two ways of giving
- evidence for an implication: constructors introduced by
- Inductive-ly defined propositions, and... functions!
-
-
-What is the proof object corresponding to b_plus3?
-
-
-
- We're looking for an expression whose type is ∀n,
- beautifuln→beautiful(3+n) — that is, a function that
- takes two arguments (one number and a piece of evidence) and
- returns a piece of evidence! Here it is:
-
-When we view the proposition being proved by b_plus3 as a function type,
- one aspect of it may seem a little unusual. The second argument's
- type, beautifuln, mentions the value of the first argument, n.
- While such dependent types are not commonly found in programming
- languages, even functional ones like ML or Haskell, they can
- be useful there too.
-
-
-
- Notice that both implication (→) and quantification (∀)
- correspond to functions on evidence. In fact, they are really the
- same thing: → is just a shorthand for a degenerate use of
- ∀ where there is no dependency, i.e., no need to give a name
- to the type on the LHS of the arrow.
-
-A proof term inhabiting this proposition would be a function
- with two arguments: a number n and some evidence E that n is
- beautiful. But the name E for this evidence is not used in the
- rest of the statement of funny_prop1, so it's a bit silly to
- bother making up a name for it. We could write it like this
- instead, using the dummy identifier _ in place of a real
- name:
-
- The Case tactics were commented out in the proof of
- and_example to avoid cluttering the proof object. What would
- you guess the proof object will look like if we uncomment them?
- Try it and see. ☐
-
-Once again, we have commented out the Case tactics to make the
- proof object for this theorem easier to understand. It is still
- a little complicated, but after performing some simple reduction
- steps, we can see that all that is really happening is taking apart
- a record containing evidence for P and Q and rebuilding it in the
- opposite order:
-
-
-
-
-Printand_commut.
-(* ===>
- and_commut =
- fun (P Q : Prop) (H : P /\ Q) =>
- (fun H0 : Q /\ P => H0)
- match H with
- | conj HP HQ => (fun (HP0 : P) (HQ0 : Q) => conj Q P HQ0 HP0) HP HQ
- end
- : forall P Q : Prop, P /\ Q -> Q /\ P *)
-
-
-
-
-
-After simplifying some direct application of fun expressions to arguments,
-we get:
-
-
-
-
-(* ===>
- and_commut =
- fun (P Q : Prop) (H : P /\ Q) =>
- match H with
- | conj HP HQ => conj Q P HQ HP
- end
- : forall P Q : Prop, P /\ Q -> Q /\ P *)
-
-
-
-
-
-
Exercise: 2 stars, optional (conj_fact)
- Construct a proof object demonstrating the following proposition.
-
-
-
-
-Definitionconj_fact : ∀PQR, P∧Q→Q∧R→P∧R :=
- (* FILL IN HERE *)admit.
-
-
- We have seen that the families of propositions beautiful and
- gorgeous actually characterize the same set of numbers.
- Prove that beautifuln↔gorgeousn for all n. Just for
- fun, write your proof as an explicit proof object, rather than
- using tactics. (Hint: if you make use of previously defined
- theorems, you should only need a single line!)
-
-
-
-
-Definitionbeautiful_iff_gorgeous :
- ∀n, beautifuln↔gorgeousn :=
- (* FILL IN HERE *)admit.
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (or_commut'')
- Try to write down an explicit proof object for or_commut (without
- using Print to peek at the ones we already defined!).
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
- Recall that we model an existential for a property as a pair consisting of
-a witness value and a proof that the witness obeys that property.
-We can choose to construct the proof explicitly.
-
-
-
-For example, consider this existentially quantified proposition:
-
- Complete the definition of the following proof object:
-
-
-
-
-Definitionp : ex_ (funn ⇒ beautiful (Sn)) :=
-(* FILL IN HERE *)admit.
-
-
-
-☐
-
-
-
-
-
-
-
-
Giving Explicit Arguments to Lemmas and Hypotheses
-
-
-
- Even when we are using tactic-based proof, it can be very useful to
-understand the underlying functional nature of implications and quantification.
-
-
-
-For example, it is often convenient to apply or rewrite
-using a lemma or hypothesis with one or more quantifiers or
-assumptions already instantiated in order to direct what
-happens. For example:
-
-
-
-
-Checkplus_comm.
-(* ==>
- plus_comm
- : forall n m : nat, n + m = m + n *)
-
-
-Lemmaplus_comm_r : ∀abc, c + (b + a) = c + (a + b).
-Proof.
- introsabc.
- (* rewrite plus_comm. *)
- (* rewrites in the first possible spot; not what we want *)
- rewrite (plus_commba). (* directs rewriting to the right spot *)
- reflexivity. Qed.
-
-
-
-
-
-In this case, giving just one argument would be sufficient.
-
-
-
-
-Lemmaplus_comm_r' : ∀abc, c + (b + a) = c + (a + b).
-Proof.
- introsabc.
- rewrite (plus_commb).
- reflexivity. Qed.
-
-
-
-
-
-Arguments must be given in order, but wildcards (_)
-may be used to skip arguments that Coq can infer.
-
-
-
-
-Lemmaplus_comm_r'' : ∀abc, c + (b + a) = c + (a + b).
-Proof.
- introsabc.
- rewrite (plus_comm_a).
- reflexivity. Qed.
-
-
-
-
-
-The author of a lemma can choose to declare easily inferable arguments
-to be implicit, just as with functions and constructors.
-
-
-
- The with clauses we've already seen is really just a way of
- specifying selected arguments by name rather than position:
-
-
-
-
-Lemmaplus_comm_r''' : ∀abc, c + (b + a) = c + (a + b).
-Proof.
- introsabc.
- rewriteplus_commwith (n := b).
- reflexivity. Qed.
-
-
-
-
-
-
Exercise: 2 stars (trans_eq_example_redux)
- Redo the proof of the following theorem (from MoreCoq.v) using
-an apply of trans_eq but not using a with clause.
-
-
-
-
-Exampletrans_eq_example' : ∀(abcdef : nat),
- [a;b] = [c;d] →
- [c;d] = [e;f] →
- [a;b] = [e;f].
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Programming with Tactics (Optional)
-
-
-
- If we can build proofs with explicit terms rather than
-tactics, you may be wondering if we can build programs using
-tactics rather than explicit terms. Sure!
-
-Notice that we terminate the Definition with a . rather than with
-:= followed by a term. This tells Coq to enter proof scripting mode
-to build an object of type nat→nat. Also, we terminate the proof
-with Defined rather than Qed; this makes the definition transparent
-so that it can be used in computation like a normally-defined function.
-
-
-
-This feature is mainly useful for writing functions with dependent types,
-which we won't explore much further in this book.
-But it does illustrate the uniformity and orthogonality of the basic ideas in Coq.
-
-
-
-
\ No newline at end of file
diff --git a/ProofObjects.v b/ProofObjects.v
deleted file mode 100644
index e6f1793..0000000
--- a/ProofObjects.v
+++ /dev/null
@@ -1,537 +0,0 @@
-(** * ProofObjects: Working with Explicit Evidence in Coq *)
-
-Require Export MoreLogic.
-
-(* ##################################################### *)
-
-(** We have seen that Coq has mechanisms both for _programming_,
- using inductive data types (like [nat] or [list]) and functions
- over these types, and for _proving_ properties of these programs,
- using inductive propositions (like [ev] or [eq]), implication, and
- universal quantification. So far, we have treated these mechanisms
- as if they were quite separate, and for many purposes this is
- a good way to think. But we have also seen hints that Coq's programming and
- proving facilities are closely related. For example, the
- keyword [Inductive] is used to declare both data types and
- propositions, and [->] is used both to describe the type of
- functions on data and logical implication. This is not just a
- syntactic accident! In fact, programs and proofs in Coq are almost
- the same thing. In this chapter we will study how this works.
-
- We have already seen the fundamental idea: provability in Coq is
- represented by concrete _evidence_. When we construct the proof
- of a basic proposition, we are actually building a tree of evidence,
- which can be thought of as a data structure. If the proposition
- is an implication like [A -> B], then its proof will be an
- evidence _transformer_: a recipe for converting evidence for
- A into evidence for B. So at a fundamental level, proofs are simply
- programs that manipulate evidence.
-*)
-(**
- Q. If evidence is data, what are propositions themselves?
-
- A. They are types!
-
- Look again at the formal definition of the [beautiful] property. *)
-
-Print beautiful.
-(* ==>
- Inductive beautiful : nat -> Prop :=
- b_0 : beautiful 0
- | b_3 : beautiful 3
- | b_5 : beautiful 5
- | b_sum : forall n m : nat, beautiful n -> beautiful m -> beautiful (n + m)
-*)
-
-(** *** *)
-
-(** The trick is to introduce an alternative pronunciation of "[:]".
- Instead of "has type," we can also say "is a proof of." For
- example, the second line in the definition of [beautiful] declares
- that [b_0 : beautiful 0]. Instead of "[b_0] has type
- [beautiful 0]," we can say that "[b_0] is a proof of [beautiful 0]."
- Similarly for [b_3] and [b_5]. *)
-
-(** *** *)
-
-(** This pun between types and propositions (between [:] as "has type"
- and [:] as "is a proof of" or "is evidence for") is called the
- _Curry-Howard correspondence_. It proposes a deep connection
- between the world of logic and the world of computation.
-<<
- propositions ~ types
- proofs ~ data values
->>
- Many useful insights follow from this connection. To begin with, it
- gives us a natural interpretation of the type of [b_sum] constructor: *)
-
-Check b_sum.
-(* ===> b_sum : forall n m,
- beautiful n ->
- beautiful m ->
- beautiful (n+m) *)
-(** This can be read "[b_sum] is a constructor that takes four
- arguments -- two numbers, [n] and [m], and two pieces of evidence,
- for the propositions [beautiful n] and [beautiful m], respectively --
- and yields evidence for the proposition [beautiful (n+m)]." *)
-
-(** Now let's look again at a previous proof involving [beautiful]. *)
-
-Theorem eight_is_beautiful: beautiful 8.
-Proof.
- apply b_sum with (n := 3) (m := 5).
- apply b_3.
- apply b_5. Qed.
-
-(** Just as with ordinary data values and functions, we can use the [Print]
-command to see the _proof object_ that results from this proof script. *)
-
-Print eight_is_beautiful.
-(* ===> eight_is_beautiful = b_sum 3 5 b_3 b_5
- : beautiful 8 *)
-
-(** In view of this, we might wonder whether we can write such
- an expression ourselves. Indeed, we can: *)
-
-Check (b_sum 3 5 b_3 b_5).
-(* ===> beautiful (3 + 5) *)
-
-(** The expression [b_sum 3 5 b_3 b_5] can be thought of as
- instantiating the parameterized constructor [b_sum] with the
- specific arguments [3] [5] and the corresponding proof objects for
- its premises [beautiful 3] and [beautiful 5] (Coq is smart enough
- to figure out that 3+5=8). Alternatively, we can think of [b_sum]
- as a primitive "evidence constructor" that, when applied to two
- particular numbers, wants to be further applied to evidence that
- those two numbers are beautiful; its type,
- forall n m, beautiful n -> beautiful m -> beautiful (n+m),
- expresses this functionality, in the same way that the polymorphic
- type [forall X, list X] in the previous chapter expressed the fact
- that the constructor [nil] can be thought of as a function from
- types to empty lists with elements of that type. *)
-
-(** This gives us an alternative way to write the proof that [8] is
- beautiful: *)
-
-Theorem eight_is_beautiful': beautiful 8.
-Proof.
- apply (b_sum 3 5 b_3 b_5).
-Qed.
-
-(** Notice that we're using [apply] here in a new way: instead of just
- supplying the _name_ of a hypothesis or previously proved theorem
- whose type matches the current goal, we are supplying an
- _expression_ that directly builds evidence with the required
- type. *)
-
-
-(* ##################################################### *)
-(** ** Proof Scripts and Proof Objects *)
-
-(** These proof objects lie at the core of how Coq operates.
-
- When Coq is following a proof script, what is happening internally
- is that it is gradually constructing a proof object -- a term
- whose type is the proposition being proved. The tactics between
- the [Proof] command and the [Qed] instruct Coq how to build up a
- term of the required type. To see this process in action, let's
- use the [Show Proof] command to display the current state of the
- proof tree at various points in the following tactic proof. *)
-
-Theorem eight_is_beautiful'': beautiful 8.
-Proof.
- Show Proof.
- apply b_sum with (n:=3) (m:=5).
- Show Proof.
- apply b_3.
- Show Proof.
- apply b_5.
- Show Proof.
-Qed.
-
-(** At any given moment, Coq has constructed a term with some
- "holes" (indicated by [?1], [?2], and so on), and it knows what
- type of evidence is needed at each hole. *)
-
-(**
- Each of the holes corresponds to a subgoal, and the proof is
- finished when there are no more subgoals. At this point, the
- [Theorem] command gives a name to the evidence we've built and
- stores it in the global context. *)
-
-(** Tactic proofs are useful and convenient, but they are not
- essential: in principle, we can always construct the required
- evidence by hand, as shown above. Then we can use [Definition]
- (rather than [Theorem]) to give a global name directly to a
- piece of evidence. *)
-
-Definition eight_is_beautiful''' : beautiful 8 :=
- b_sum 3 5 b_3 b_5.
-
-(** All these different ways of building the proof lead to exactly the
- same evidence being saved in the global environment. *)
-
-Print eight_is_beautiful.
-(* ===> eight_is_beautiful = b_sum 3 5 b_3 b_5 : beautiful 8 *)
-Print eight_is_beautiful'.
-(* ===> eight_is_beautiful' = b_sum 3 5 b_3 b_5 : beautiful 8 *)
-Print eight_is_beautiful''.
-(* ===> eight_is_beautiful'' = b_sum 3 5 b_3 b_5 : beautiful 8 *)
-Print eight_is_beautiful'''.
-(* ===> eight_is_beautiful''' = b_sum 3 5 b_3 b_5 : beautiful 8 *)
-
-(** **** Exercise: 1 star (six_is_beautiful) *)
-(** Give a tactic proof and a proof object showing that [6] is [beautiful]. *)
-
-Theorem six_is_beautiful :
- beautiful 6.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Definition six_is_beautiful' : beautiful 6 :=
- (* FILL IN HERE *) admit.
-(** [] *)
-
-(** **** Exercise: 1 star (nine_is_beautiful) *)
-(** Give a tactic proof and a proof object showing that [9] is [beautiful]. *)
-
-Theorem nine_is_beautiful :
- beautiful 9.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Definition nine_is_beautiful' : beautiful 9 :=
- (* FILL IN HERE *) admit.
-(** [] *)
-
-(* ##################################################### *)
-(** ** Quantification, Implications and Functions *)
-
-(** In Coq's computational universe (where we've mostly been living
- until this chapter), there are two sorts of values with arrows in
- their types: _constructors_ introduced by [Inductive]-ly defined
- data types, and _functions_.
-
- Similarly, in Coq's logical universe, there are two ways of giving
- evidence for an implication: constructors introduced by
- [Inductive]-ly defined propositions, and... functions!
-
- For example, consider this statement: *)
-
-Theorem b_plus3: forall n, beautiful n -> beautiful (3+n).
-Proof.
- intros n H.
- apply b_sum.
- apply b_3.
- apply H.
-Qed.
-
-(** What is the proof object corresponding to [b_plus3]?
-
- We're looking for an expression whose _type_ is [forall n,
- beautiful n -> beautiful (3+n)] -- that is, a _function_ that
- takes two arguments (one number and a piece of evidence) and
- returns a piece of evidence! Here it is: *)
-
-Definition b_plus3' : forall n, beautiful n -> beautiful (3+n) :=
- fun (n : nat) => fun (H : beautiful n) =>
- b_sum 3 n b_3 H.
-
-Check b_plus3'.
-(* ===> b_plus3' : forall n : nat, beautiful n -> beautiful (3+n) *)
-
-(** Recall that [fun n => blah] means "the function that, given [n],
- yields [blah]." Another equivalent way to write this definition is: *)
-
-Definition b_plus3'' (n : nat) (H : beautiful n) : beautiful (3+n) :=
- b_sum 3 n b_3 H.
-
-Check b_plus3''.
-(* ===> b_plus3'' : forall n, beautiful n -> beautiful (3+n) *)
-
-(** When we view the proposition being proved by [b_plus3] as a function type,
- one aspect of it may seem a little unusual. The second argument's
- type, [beautiful n], mentions the _value_ of the first argument, [n].
- While such _dependent types_ are not commonly found in programming
- languages, even functional ones like ML or Haskell, they can
- be useful there too.
-
- Notice that both implication ([->]) and quantification ([forall])
- correspond to functions on evidence. In fact, they are really the
- same thing: [->] is just a shorthand for a degenerate use of
- [forall] where there is no dependency, i.e., no need to give a name
- to the type on the LHS of the arrow. *)
-
-(** For example, consider this proposition: *)
-
-Definition beautiful_plus3 : Prop :=
- forall n, forall (E : beautiful n), beautiful (n+3).
-
-(** A proof term inhabiting this proposition would be a function
- with two arguments: a number [n] and some evidence [E] that [n] is
- beautiful. But the name [E] for this evidence is not used in the
- rest of the statement of [funny_prop1], so it's a bit silly to
- bother making up a name for it. We could write it like this
- instead, using the dummy identifier [_] in place of a real
- name: *)
-
-Definition beautiful_plus3' : Prop :=
- forall n, forall (_ : beautiful n), beautiful (n+3).
-
-(** Or, equivalently, we can write it in more familiar notation: *)
-
-Definition beatiful_plus3'' : Prop :=
- forall n, beautiful n -> beautiful (n+3).
-
-(** In general, "[P -> Q]" is just syntactic sugar for
- "[forall (_:P), Q]". *)
-
-
-(** **** Exercise: 2 stars b_times2 *)
-
-(** Give a proof object corresponding to the theorem [b_times2] from Prop.v *)
-
-Definition b_times2': forall n, beautiful n -> beautiful (2*n) :=
- (* FILL IN HERE *) admit.
-(** [] *)
-
-
-
-(** **** Exercise: 2 stars, optional (gorgeous_plus13_po) *)
-(** Give a proof object corresponding to the theorem [gorgeous_plus13] from Prop.v *)
-
-Definition gorgeous_plus13_po: forall n, gorgeous n -> gorgeous (13+n):=
- (* FILL IN HERE *) admit.
-(** [] *)
-
-
-
-
-(** It is particularly revealing to look at proof objects involving the
-logical connectives that we defined with inductive propositions in Logic.v. *)
-
-Theorem and_example :
- (beautiful 0) /\ (beautiful 3).
-Proof.
- apply conj.
- (* Case "left". *) apply b_0.
- (* Case "right". *) apply b_3. Qed.
-
-(** Let's take a look at the proof object for the above theorem. *)
-
-Print and_example.
-(* ===> conj (beautiful 0) (beautiful 3) b_0 b_3
- : beautiful 0 /\ beautiful 3 *)
-
-(** Note that the proof is of the form
- conj (beautiful 0) (beautiful 3)
- (...pf of beautiful 3...) (...pf of beautiful 3...)
- as you'd expect, given the type of [conj]. *)
-
-(** **** Exercise: 1 star, optional (case_proof_objects) *)
-(** The [Case] tactics were commented out in the proof of
- [and_example] to avoid cluttering the proof object. What would
- you guess the proof object will look like if we uncomment them?
- Try it and see. *)
-(** [] *)
-
-Theorem and_commut : forall P Q : Prop,
- P /\ Q -> Q /\ P.
-Proof.
- intros P Q H.
- inversion H as [HP HQ].
- split.
- (* Case "left". *) apply HQ.
- (* Case "right". *) apply HP. Qed.
-
-(** Once again, we have commented out the [Case] tactics to make the
- proof object for this theorem easier to understand. It is still
- a little complicated, but after performing some simple reduction
- steps, we can see that all that is really happening is taking apart
- a record containing evidence for [P] and [Q] and rebuilding it in the
- opposite order: *)
-
-Print and_commut.
-(* ===>
- and_commut =
- fun (P Q : Prop) (H : P /\ Q) =>
- (fun H0 : Q /\ P => H0)
- match H with
- | conj HP HQ => (fun (HP0 : P) (HQ0 : Q) => conj Q P HQ0 HP0) HP HQ
- end
- : forall P Q : Prop, P /\ Q -> Q /\ P *)
-
-(** After simplifying some direct application of [fun] expressions to arguments,
-we get: *)
-
-(* ===>
- and_commut =
- fun (P Q : Prop) (H : P /\ Q) =>
- match H with
- | conj HP HQ => conj Q P HQ HP
- end
- : forall P Q : Prop, P /\ Q -> Q /\ P *)
-
-
-
-(** **** Exercise: 2 stars, optional (conj_fact) *)
-(** Construct a proof object demonstrating the following proposition. *)
-
-Definition conj_fact : forall P Q R, P /\ Q -> Q /\ R -> P /\ R :=
- (* FILL IN HERE *) admit.
-(** [] *)
-
-
-(** **** Exercise: 2 stars, advanced, optional (beautiful_iff_gorgeous) *)
-
-(** We have seen that the families of propositions [beautiful] and
- [gorgeous] actually characterize the same set of numbers.
- Prove that [beautiful n <-> gorgeous n] for all [n]. Just for
- fun, write your proof as an explicit proof object, rather than
- using tactics. (_Hint_: if you make use of previously defined
- theorems, you should only need a single line!) *)
-
-Definition beautiful_iff_gorgeous :
- forall n, beautiful n <-> gorgeous n :=
- (* FILL IN HERE *) admit.
-(** [] *)
-
-
-(** **** Exercise: 2 stars, optional (or_commut'') *)
-(** Try to write down an explicit proof object for [or_commut] (without
- using [Print] to peek at the ones we already defined!). *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** Recall that we model an existential for a property as a pair consisting of
-a witness value and a proof that the witness obeys that property.
-We can choose to construct the proof explicitly.
-
-For example, consider this existentially quantified proposition: *)
-Check ex.
-
-Definition some_nat_is_even : Prop :=
- ex _ ev.
-
-(** To prove this proposition, we need to choose a particular number
- as witness -- say, 4 -- and give some evidence that that number is
- even. *)
-
-Definition snie : some_nat_is_even :=
- ex_intro _ ev 4 (ev_SS 2 (ev_SS 0 ev_0)).
-
-
-(** **** Exercise: 2 stars, optional (ex_beautiful_Sn) *)
-(** Complete the definition of the following proof object: *)
-
-Definition p : ex _ (fun n => beautiful (S n)) :=
-(* FILL IN HERE *) admit.
-(** [] *)
-
-
-
-(* ##################################################### *)
-(** ** Giving Explicit Arguments to Lemmas and Hypotheses *)
-
-(** Even when we are using tactic-based proof, it can be very useful to
-understand the underlying functional nature of implications and quantification.
-
-For example, it is often convenient to [apply] or [rewrite]
-using a lemma or hypothesis with one or more quantifiers or
-assumptions already instantiated in order to direct what
-happens. For example: *)
-
-Check plus_comm.
-(* ==>
- plus_comm
- : forall n m : nat, n + m = m + n *)
-
-Lemma plus_comm_r : forall a b c, c + (b + a) = c + (a + b).
-Proof.
- intros a b c.
- (* rewrite plus_comm. *)
- (* rewrites in the first possible spot; not what we want *)
- rewrite (plus_comm b a). (* directs rewriting to the right spot *)
- reflexivity. Qed.
-
-
-(** In this case, giving just one argument would be sufficient. *)
-
-Lemma plus_comm_r' : forall a b c, c + (b + a) = c + (a + b).
-Proof.
- intros a b c.
- rewrite (plus_comm b).
- reflexivity. Qed.
-
-(** Arguments must be given in order, but wildcards (_)
-may be used to skip arguments that Coq can infer. *)
-
-Lemma plus_comm_r'' : forall a b c, c + (b + a) = c + (a + b).
-Proof.
- intros a b c.
- rewrite (plus_comm _ a).
- reflexivity. Qed.
-
-(** The author of a lemma can choose to declare easily inferable arguments
-to be implicit, just as with functions and constructors.
-
- The [with] clauses we've already seen is really just a way of
- specifying selected arguments by name rather than position: *)
-
-Lemma plus_comm_r''' : forall a b c, c + (b + a) = c + (a + b).
-Proof.
- intros a b c.
- rewrite plus_comm with (n := b).
- reflexivity. Qed.
-
-
-(** **** Exercise: 2 stars (trans_eq_example_redux) *)
-(** Redo the proof of the following theorem (from MoreCoq.v) using
-an [apply] of [trans_eq] but _not_ using a [with] clause. *)
-
-Example trans_eq_example' : forall (a b c d e f : nat),
- [a;b] = [c;d] ->
- [c;d] = [e;f] ->
- [a;b] = [e;f].
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-(* ##################################################### *)
-(** ** Programming with Tactics (Optional) *)
-
-(** If we can build proofs with explicit terms rather than
-tactics, you may be wondering if we can build programs using
-tactics rather than explicit terms. Sure! *)
-
-Definition add1 : nat -> nat.
-intro n.
-Show Proof.
-apply S.
-Show Proof.
-apply n. Defined.
-
-Print add1.
-(* ==>
- add1 = fun n : nat => S n
- : nat -> nat
-*)
-
-Eval compute in add1 2.
-(* ==> 3 : nat *)
-
-(** Notice that we terminate the [Definition] with a [.] rather than with
-[:=] followed by a term. This tells Coq to enter proof scripting mode
-to build an object of type [nat -> nat]. Also, we terminate the proof
-with [Defined] rather than [Qed]; this makes the definition _transparent_
-so that it can be used in computation like a normally-defined function.
-
-This feature is mainly useful for writing functions with dependent types,
-which we won't explore much further in this book.
-But it does illustrate the uniformity and orthogonality of the basic ideas in Coq. *)
-
-(* $Date: 2014-06-05 07:22:21 -0400 (Thu, 05 Jun 2014) $ *)
-
diff --git a/Prop.html b/Prop.html
deleted file mode 100644
index 4d1eabf..0000000
--- a/Prop.html
+++ /dev/null
@@ -1,1754 +0,0 @@
-
-
-
-
-
-Prop: Propositions and Evidence
-
-
-
-
-
-
-
-
-
-
-
-
-
-
PropPropositions and Evidence
-
-
-
-
-
-
-
-
-
-
-RequireExportLogic.
-
-
-
-
-
-
From Boolean Functions to Propositions
-
-
-
- In chapter Basics we defined a functionevenb that tests a
- number for evenness, yielding true if so. We can use this
- function to define the proposition that some number n is
- even:
-
-That is, we can define "n is even" to mean "the function evenb
- returns true when applied to n."
-
-
-
- Note that here we have given a name
- to a proposition using a Definition, just as we have
- given names to expressions of other sorts. This isn't a fundamentally
- new kind of proposition; it is still just an equality.
-
-
- Another alternative is to define the concept of evenness
- directly. Instead of going via the evenb function ("a number is
- even if a certain computation yields true"), we can say what the
- concept of evenness means by giving two different ways of
- presenting evidence that a number is even.
-
-This definition says that there are two ways to give
- evidence that a number m is even. First, 0 is even, and
- ev_0 is evidence for this. Second, if m=S(Sn) for some
- n and we can give evidence e that n is even, then m is
- also even, and ev_SSne is the evidence.
-
-
-
Exercise: 1 star (double_even)
-
-
-
-
-
-Theoremdouble_even : ∀n,
- ev (doublen).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Discussion: Computational vs. Inductive Definitions
-
-
-
- We have seen that the proposition "n is even" can be
- phrased in two different ways — indirectly, via a boolean testing
- function evenb, or directly, by inductively describing what
- constitutes evidence for evenness. These two ways of defining
- evenness are about equally easy to state and work with. Which we
- choose is basically a question of taste.
-
-
-
- However, for many other properties of interest, the direct
- inductive definition is preferable, since writing a testing
- function may be awkward or even impossible.
-
-
-
- One such property is beautiful. This is a perfectly sensible
- definition of a set of numbers, but we cannot translate its
- definition directly into a Coq Fixpoint (or into a recursive
- function in any other common programming language). We might be
- able to find a clever way of testing this property using a
- Fixpoint (indeed, it is not too hard to find one in this case),
- but in general this could require arbitrarily deep thinking. In
- fact, if the property we are interested in is uncomputable, then
- we cannot define it as a Fixpoint no matter how hard we try,
- because Coq requires that all Fixpoints correspond to
- terminating computations.
-
-
-
- On the other hand, writing an inductive definition of what it
- means to give evidence for the property beautiful is
- straightforward.
-
-
-
Exercise: 1 star (ev__even)
- Here is a proof that the inductive definition of evenness implies
- the computational one.
-
-Could this proof also be carried out by induction on n instead
- of E? If not, why not?
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
- The induction principle for inductively defined propositions does
- not follow quite the same form as that of inductively defined
- sets. For now, you can take the intuitive view that induction on
- evidence evn is similar to induction on n, but restricts our
- attention to only those numbers for which evidence evn could be
- generated. We'll look at the induction principle of ev in more
- depth below, to explain what's really going on.
-
-
-
Exercise: 1 star (l_fails)
- The following proof attempt will not succeed.
-
-
- Intuitively, we expect the proof to fail because not every
- number is even. However, what exactly causes the proof to fail?
-
-
-
-(* FILL IN HERE *)
- ☐
-
-
-
Exercise: 2 stars (ev_sum)
- Here's another exercise requiring induction.
-
-
-
-
-Theoremev_sum : ∀nm,
- evn→evm→ev (n+m).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Inductively Defined Propositions
-
-
-
- As a running example, let's
- define a simple property of natural numbers — we'll call it
- "beautiful."
-
-
- Informally, a number is beautiful if it is 0, 3, 5, or the
- sum of two beautiful numbers.
-
-
-
- More pedantically, we can define beautiful numbers by giving four
- rules:
-
-
-
-
-
Rule b_0: The number 0 is beautiful.
-
-
-
Rule b_3: The number 3 is beautiful.
-
-
-
Rule b_5: The number 5 is beautiful.
-
-
-
Rule b_sum: If n and m are both beautiful, then so is
- their sum.
-
-
-
Inference Rules
- We will see many definitions like this one during the rest
- of the course, and for purposes of informal discussions, it is
- helpful to have a lightweight notation that makes them easy to
- read and write. Inference rules are one such notation:
-
-
-
-
-
-
- (b_0)
-
-
-
-
-
-
beautiful 0
-
-
-
-
-
-
- (b_3)
-
-
-
-
-
-
beautiful 3
-
-
-
-
-
-
- (b_5)
-
-
-
-
-
-
beautiful 5
-
-
-
-
-
beautiful n beautiful m
-
- (b_sum)
-
-
-
-
-
-
beautiful (n+m)
-
-
-
-
-
-
- Each of the textual rules above is reformatted here as an
- inference rule; the intended reading is that, if the premises
- above the line all hold, then the conclusion below the line
- follows. For example, the rule b_sum says that, if n and m
- are both beautiful numbers, then it follows that n+m is
- beautiful too. If a rule has no premises above the line, then
- its conclusion holds unconditionally.
-
-
-
- These rules define the property beautiful. That is, if we
- want to convince someone that some particular number is beautiful,
- our argument must be based on these rules. For a simple example,
- suppose we claim that the number 5 is beautiful. To support
- this claim, we just need to point out that rule b_5 says so.
- Or, if we want to claim that 8 is beautiful, we can support our
- claim by first observing that 3 and 5 are both beautiful (by
- rules b_3 and b_5) and then pointing out that their sum, 8,
- is therefore beautiful by rule b_sum. This argument can be
- expressed graphically with the following proof tree:
-
-The first line declares that beautiful is a proposition — or,
- more formally, a family of propositions "indexed by" natural
- numbers. (That is, for each number n, the claim that "n is
- beautiful" is a proposition.) Such a family of propositions is
- often called a property of numbers. Each of the remaining lines
- embodies one of the rules for beautiful numbers.
-
-
-
-
- The rules introduced this way have the same status as proven
- theorems; that is, they are true axiomatically.
- So we can use Coq's apply tactic with the rule names to prove
- that particular numbers are beautiful.
-
-
-
-
-Theoremthree_is_beautiful: beautiful 3.
-Proof.
- (* This simply follows from the rule b_3. *)
- applyb_3.
-Qed.
-
-
-Theoremeight_is_beautiful: beautiful 8.
-Proof.
- (* First we use the rule b_sum, telling Coq how to
- instantiate n and m. *)
- applyb_sumwith (n:=3) (m:=5).
- (* To solve the subgoals generated by b_sum, we must provide
- evidence of beautiful3 and beautiful5. Fortunately we
- have rules for both. *)
- applyb_3.
- applyb_5.
-Qed.
-
-
-
-
-
-
- As you would expect, we can also prove theorems that have
-hypotheses about beautiful.
-
-Theoremb_times2: ∀n, beautifuln→beautiful (2×n).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (b_timesm)
-
-
-
-Theoremb_timesm: ∀nm, beautifuln→beautiful (m×n).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Induction Over Evidence
-
-
-
- Besides constructing evidence that numbers are beautiful, we can
- also reason about such evidence.
-
-
- The fact that we introduced beautiful with an Inductive
- declaration tells Coq not only that the constructors b_0, b_3,
- b_5 and b_sum are ways to build evidence, but also that these
- four constructors are the only ways to build evidence that
- numbers are beautiful.
-
-
- In other words, if someone gives us evidence E for the assertion
- beautifuln, then we know that E must have one of four shapes:
-
-
-
-
-
E is b_0 (and n is O),
-
-
-
E is b_3 (and n is 3),
-
-
-
E is b_5 (and n is 5), or
-
-
-
E is b_sumn1n2E1E2 (and n is n1+n2, where E1 is
- evidence that n1 is beautiful and E2 is evidence that n2
- is beautiful).
-
-
-
-
-
-
- This permits us to analyze any hypothesis of the form beautiful
- n to see how it was constructed, using the tactics we already
- know. In particular, we can use the induction tactic that we
- have already seen for reasoning about inductively defined data
- to reason about inductively defined evidence.
-
-
-
- To illustrate this, let's define another property of numbers:
-
- Write out the definition of gorgeous numbers using inference rule
- notation.
-
-
-
-(* FILL IN HERE *)
-☐
-
-
-
-
Exercise: 1 star (gorgeous_plus13)
-
-
-
-Theoremgorgeous_plus13: ∀n,
- gorgeousn→gorgeous (13+n).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
- It seems intuitively obvious that, although gorgeous and
- beautiful are presented using slightly different rules, they are
- actually the same property in the sense that they are true of the
- same numbers. Indeed, we can prove this.
-
-Notice that the argument proceeds by induction on the evidenceH!
-
-
- Let's see what happens if we try to prove this by induction on n
- instead of induction on the evidence H.
-
-
-
-
-Theoremgorgeous__beautiful_FAILED : ∀n,
- gorgeousn→beautifuln.
-Proof.
- intros. inductionnas [| n'].
- Case "n = 0". applyb_0.
- Case "n = S n'". (* We are stuck! *)
-Abort.
-
-
-
-
-
-The problem here is that doing induction on n doesn't yield a
- useful induction hypothesis. Knowing how the property we are
- interested in behaves on the predecessor of n doesn't help us
- prove that it holds for n. Instead, we would like to be able to
- have induction hypotheses that mention other numbers, such as n-
- 3 and n-5. This is given precisely by the shape of the
- constructors for gorgeous.
-
-
-
Exercise: 2 stars (gorgeous_sum)
-
-
-
-Theoremgorgeous_sum : ∀nm,
- gorgeousn→gorgeousm→gorgeous (n + m).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (beautiful__gorgeous)
-
-
-
-Theorembeautiful__gorgeous : ∀n, beautifuln→gorgeousn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (g_times2)
- Prove the g_times2 theorem below without using gorgeous__beautiful.
- You might find the following helper lemma useful.
-
-
-
-
-Lemmahelper_g_times2 : ∀xyz, x + (z + y)= z + x + y.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremg_times2: ∀n, gorgeousn→gorgeous (2×n).
-Proof.
- introsnH. simpl.
- inductionH.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Inversion on Evidence
-
-
-
- Another situation where we want to analyze evidence for evenness
- is when proving that, if n is even, then pred(predn) is
- too. In this case, we don't need to do an inductive proof. The
- right tactic turns out to be inversion.
-
-
- These uses of inversion may seem a bit mysterious at first.
- Until now, we've only used inversion on equality
- propositions, to utilize injectivity of constructors or to
- discriminate between different constructors. But we see here
- that inversion can also be applied to analyzing evidence
- for inductively defined propositions.
-
-
-
- (You might also expect that destruct would be a more suitable
- tactic to use here. Indeed, it is possible to use destruct, but
- it often throws away useful information, and the eqn: qualifier
- doesn't help much in this case.)
-
-
-
- Here's how inversion works in general. Suppose the name
- I refers to an assumption P in the current context, where
- P has been defined by an Inductive declaration. Then,
- for each of the constructors of P, inversionI generates
- a subgoal in which I has been replaced by the exact,
- specific conditions under which this constructor could have
- been used to prove P. Some of these subgoals will be
- self-contradictory; inversion throws these away. The ones
- that are left represent the cases that must be proved to
- establish the original goal.
-
-
-
- In this particular case, the inversion analyzed the construction
- ev(S(Sn)), determined that this could only have been
- constructed using ev_SS, and generated a new subgoal with the
- arguments of that constructor as new hypotheses. (It also
- produced an auxiliary equality, which happens to be useless here.)
- We'll begin exploring this more general behavior of inversion in
- what follows.
-
-
-
Exercise: 1 star (inversion_practice)
-
-
-
-TheoremSSSSev__even : ∀n,
- ev (S (S (S (Sn)))) →evn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-The inversion tactic can also be used to derive goals by showing
- the absurdity of a hypothesis.
-
-
-
-
-Theoremeven5_nonsense :
- ev 5 → 2 + 2 = 9.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (ev_ev__ev)
- Finding the appropriate thing to do induction on is a
- bit tricky here:
-
-
-
-
-Theoremev_ev__ev : ∀nm,
- ev (n+m) →evn→evm.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (ev_plus_plus)
- Here's an exercise that just requires applying existing lemmas. No
- induction or even case analysis is needed, but some of the rewriting
- may be tedious.
-
-
-
-
-Theoremev_plus_plus : ∀nmp,
- ev (n+m) →ev (n+p) →ev (m+p).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 4 stars (palindromes)
- A palindrome is a sequence that reads the same backwards as
- forwards.
-
-
-
-
-
Define an inductive proposition pal on listX that
- captures what it means to be a palindrome. (Hint: You'll need
- three cases. Your definition should be based on the structure
- of the list; just having a single constructor
-
-
-
-
-c : ∀l, l = revl→pall
-
-
-
- may seem obvious, but will not work very well.)
-
-
-
-
-
-
Prove that
-
-
-
-
- ∀l, pal (l ++ revl).
-
-
-
-
-
-
Prove that
-
-
-
-
- ∀l, pall→l = revl.
-
-
-
-
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 5 stars, optional (palindrome_converse)
- Using your definition of pal from the previous exercise, prove
- that
-
-
-
-
- ∀l, l = revl→pall.
-
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 4 stars, advanced (subsequence)
- A list is a subsequence of another list if all of the elements
- in the first list occur in the same order in the second list,
- possibly with some extra elements in between. For example,
-
-
- but it is not a subsequence of any of the lists
-
-
-
-
- [1,2]
- [1,3]
- [5,6,2,1,7,3,8]
-
-
-
-
-
-
-
-
Define an inductive proposition subseq on listnat that
- captures what it means to be a subsequence. (Hint: You'll need
- three cases.)
-
-
-
-
-
-
Prove that subsequence is reflexive, that is, any list is a
- subsequence of itself.
-
-
-
-
-
-
Prove that for any lists l1, l2, and l3, if l1 is a
- subsequence of l2, then l1 is also a subsequence of l2++
- l3.
-
-
-
-
-
-
(Optional, harder) Prove that subsequence is transitive — that
- is, if l1 is a subsequence of l2 and l2 is a subsequence
- of l3, then l1 is a subsequence of l3. Hint: choose your
- induction carefully!
-
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (R_provability)
- Suppose we give Coq the following definition:
-
-
- Which of the following propositions are provable?
-
-
-
-
-
R2[1,0]
-
-
-
R1[1,2,1,0]
-
-
-
R6[3,2,1,0]
-
-
-
-
-
-
- ☐
-
-
-
-
-
-
-
-
Relations
-
-
-
- A proposition parameterized by a number (such as ev or
- beautiful) can be thought of as a property — i.e., it defines
- a subset of nat, namely those numbers for which the proposition
- is provable. In the same way, a two-argument proposition can be
- thought of as a relation — i.e., it defines a set of pairs for
- which the proposition is provable.
-
-
-
-
-
-
-
-
-
-One useful example is the "less than or equal to"
- relation on numbers.
-
-
- The following definition should be fairly intuitive. It
- says that there are two ways to give evidence that one number is
- less than or equal to another: either observe that they are the
- same number, or give evidence that the first is less than or equal
- to the predecessor of the second.
-
-Proofs of facts about ≤ using the constructors le_n and
- le_S follow the same patterns as proofs about properties, like
- ev in chapter Prop. We can apply the constructors to prove ≤
- goals (e.g., to show that 3≤3 or 3≤6), and we can use
- tactics like inversion to extract information from ≤
- hypotheses in the context (e.g., to prove that (2≤1)→2+2=5.)
-
-
-
- Here are some sanity checks on the definition. (Notice that,
- although these are the same kind of simple "unit tests" as we gave
- for the testing functions we wrote in the first few lectures, we
- must construct their proofs explicitly — simpl and
- reflexivity don't do the job, because the proofs aren't just a
- matter of simplifying computations.)
-
-
-
-
-Theoremtest_le1 :
- 3 ≤ 3.
-Proof.
- (* WORKED IN CLASS *)
- applyle_n. Qed.
-
-
-Theoremtest_le2 :
- 3 ≤ 6.
-Proof.
- (* WORKED IN CLASS *)
- applyle_S. applyle_S. applyle_S. applyle_n. Qed.
-
-
-Theoremtest_le3 :
- (2 ≤ 1) → 2 + 2 = 5.
-Proof.
- (* WORKED IN CLASS *)
- introsH. inversionH. inversionH2. Qed.
-
-
-
-
-
-
- The "strictly less than" relation n<m can now be defined
- in terms of le.
-
-
-
-
-Definitionlt (nm:nat) := le (Sn) m.
-
-
-Notation "m < n" := (ltmn).
-
-
-
-
-
-Here are a few more simple relations on numbers:
-
- Define an inductive binary relation total_relation that holds
- between every pair of natural numbers.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 2 stars (empty_relation)
- Define an inductive binary relation empty_relation (on numbers)
- that never holds.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (le_exercises)
- Here are a number of facts about the ≤ and < relations that
- we are going to need later in the course. The proofs make good
- practice exercises.
-
-
-
-
-Lemmale_trans : ∀mno, m ≤ n→n ≤ o→m ≤ o.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-TheoremO_le_n : ∀n,
- 0 ≤ n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremn_le_m__Sn_le_Sm : ∀nm,
- n ≤ m→Sn ≤ Sm.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-TheoremSn_le_Sm__n_le_m : ∀nm,
- Sn ≤ Sm→n ≤ m.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremle_plus_l : ∀ab,
- a ≤ a + b.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremplus_lt : ∀n1n2m,
- n1 + n2 < m→
- n1 < m∧n2 < m.
-Proof.
- unfoldlt.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremlt_S : ∀nm,
- n < m→
- n < Sm.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremble_nat_true : ∀nm,
- ble_natnm = true→n ≤ m.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremle_ble_nat : ∀nm,
- n ≤ m→
- ble_natnm = true.
-Proof.
- (* Hint: This may be easiest to prove by induction on m. *)
- (* FILL IN HERE *)Admitted.
-
-
-Theoremble_nat_true_trans : ∀nmo,
- ble_natnm = true→ble_natmo = true→ble_natno = true.
-Proof.
- (* Hint: This theorem can be easily proved without using induction. *)
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-
Exercise: 2 stars, optional (ble_nat_false)
-
-
-
-Theoremble_nat_false : ∀nm,
- ble_natnm = false→ ~(n ≤ m).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars (R_provability)
-
-
-
-ModuleR.
-
-
-
-We can define three-place relations, four-place relations,
- etc., in just the same way as binary relations. For example,
- consider the following three-place relation on numbers:
-
Which of the following propositions are provable?
-
-
R112
-
-
-
R226
-
-
-
-
-
-
-
-
-
-
-
-
If we dropped constructor c5 from the definition of R,
- would the set of provable propositions change? Briefly (1
- sentence) explain your answer.
-
-
-
-
-
-
If we dropped constructor c4 from the definition of R,
- would the set of provable propositions change? Briefly (1
- sentence) explain your answer.
-
-
-
-
-
-
-(* FILL IN HERE *)
-☐
-
-
-
-
Exercise: 3 stars, optional (R_fact)
- Relation R actually encodes a familiar function. State and prove two
- theorems that formally connects the relation and the function.
- That is, if Rmno is true, what can we say about m,
- n, and o, and vice versa?
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-EndR.
-
-
-
-
-
-
Programming with Propositions Revisited
-
-
-
- As we have seen, a proposition is a statement expressing a factual claim,
- like "two plus two equals four." In Coq, propositions are written
- as expressions of type Prop. .
-
- Both provable and unprovable claims are perfectly good
- propositions. Simply being a proposition is one thing; being
- provable is something else!
-
-But they can be used in many other ways. For example, we have also seen that
- we can give a name to a proposition using a Definition, just as we have
- given names to expressions of other sorts.
-
- The type of even, i.e., nat→Prop, can be pronounced in
- three equivalent ways: (1) "even is a function from numbers to
- propositions," (2) "even is a family of propositions, indexed
- by a number n," or (3) "even is a property of numbers."
-
-
- Propositions — including parameterized propositions — are
- first-class citizens in Coq. For example, we can define functions
- from numbers to propositions...
-
- Here are two more examples of passing parameterized propositions
- as arguments to a function.
-
-
-
- The first function, true_for_all_numbers, takes a proposition
- P as argument and builds the proposition that P is true for
- all natural numbers.
-
-The second, preserved_by_S, takes P and builds the proposition
- that, if P is true for some natural number n', then it is also
- true by the successor of n' — i.e. that P is preserved by
- successor:
-
- Complete the definition of the combine_odd_even function
- below. It takes as arguments two properties of numbers Podd and
- Peven. As its result, it should return a new property P such
- that Pn is equivalent to Poddn when n is odd, and
- equivalent to Pevenn otherwise.
-
-
-
-
-Definitioncombine_odd_even (PoddPeven : nat→Prop) : nat→Prop :=
- (* FILL IN HERE *)admit.
-
-
-
-
-
-To test your definition, see whether you can prove the following
- facts:
-
-
-
-
-Theoremcombine_odd_even_intro :
- ∀(PoddPeven : nat→Prop) (n : nat),
- (oddbn = true→Poddn) →
- (oddbn = false→Pevenn) →
- combine_odd_evenPoddPevenn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremcombine_odd_even_elim_odd :
- ∀(PoddPeven : nat→Prop) (n : nat),
- combine_odd_evenPoddPevenn→
- oddbn = true→
- Poddn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-Theoremcombine_odd_even_elim_even :
- ∀(PoddPeven : nat→Prop) (n : nat),
- combine_odd_evenPoddPevenn→
- oddbn = false→
- Pevenn.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-☐
-
-
-
-
-
-
-
-One more quick digression, for adventurous souls: if we can define
- parameterized propositions using Definition, then can we also
- define them using Fixpoint? Of course we can! However, this
- kind of "recursive parameterization" doesn't correspond to
- anything very familiar from everyday mathematics. The following
- exercise gives a slightly contrived example.
-
- Define a recursive function
- true_upto_n__true_everywhere that makes
- true_upto_n_example work.
-
-
-
-
-(*
-Fixpoint true_upto_n__true_everywhere
-(* FILL IN HERE *)
-
-Example true_upto_n_example :
- (true_upto_n__true_everywhere 3 (fun n => even n))
- = (even 3 -> even 2 -> even 1 -> forall m : nat, even m).
-Proof. reflexivity. Qed.
-*)
-
-
-
-
\ No newline at end of file
diff --git a/Prop.v b/Prop.v
deleted file mode 100644
index 493ae12..0000000
--- a/Prop.v
+++ /dev/null
@@ -1,1012 +0,0 @@
-(** * Prop: Propositions and Evidence *)
-
-Require Export Logic.
-
-
-
-(* ####################################################### *)
-(** ** From Boolean Functions to Propositions *)
-
-(** In chapter [Basics] we defined a _function_ [evenb] that tests a
- number for evenness, yielding [true] if so. We can use this
- function to define the _proposition_ that some number [n] is
- even: *)
-
-Definition even (n:nat) : Prop :=
- evenb n = true.
-
-(** That is, we can define "[n] is even" to mean "the function [evenb]
- returns [true] when applied to [n]."
-
- Note that here we have given a name
- to a proposition using a [Definition], just as we have
- given names to expressions of other sorts. This isn't a fundamentally
- new kind of proposition; it is still just an equality. *)
-
-(** Another alternative is to define the concept of evenness
- directly. Instead of going via the [evenb] function ("a number is
- even if a certain computation yields [true]"), we can say what the
- concept of evenness means by giving two different ways of
- presenting _evidence_ that a number is even. *)
-
-(** ** Inductively Defined Propositions *)
-
-Inductive ev : nat -> Prop :=
- | ev_0 : ev O
- | ev_SS : forall n:nat, ev n -> ev (S (S n)).
-
-(** This definition says that there are two ways to give
- evidence that a number [m] is even. First, [0] is even, and
- [ev_0] is evidence for this. Second, if [m = S (S n)] for some
- [n] and we can give evidence [e] that [n] is even, then [m] is
- also even, and [ev_SS n e] is the evidence. *)
-
-
-(** **** Exercise: 1 star (double_even) *)
-
-Theorem double_even : forall n,
- ev (double n).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(** *** Discussion: Computational vs. Inductive Definitions *)
-
-(** We have seen that the proposition "[n] is even" can be
- phrased in two different ways -- indirectly, via a boolean testing
- function [evenb], or directly, by inductively describing what
- constitutes evidence for evenness. These two ways of defining
- evenness are about equally easy to state and work with. Which we
- choose is basically a question of taste.
-
- However, for many other properties of interest, the direct
- inductive definition is preferable, since writing a testing
- function may be awkward or even impossible.
-
- One such property is [beautiful]. This is a perfectly sensible
- definition of a set of numbers, but we cannot translate its
- definition directly into a Coq Fixpoint (or into a recursive
- function in any other common programming language). We might be
- able to find a clever way of testing this property using a
- [Fixpoint] (indeed, it is not too hard to find one in this case),
- but in general this could require arbitrarily deep thinking. In
- fact, if the property we are interested in is uncomputable, then
- we cannot define it as a [Fixpoint] no matter how hard we try,
- because Coq requires that all [Fixpoint]s correspond to
- terminating computations.
-
- On the other hand, writing an inductive definition of what it
- means to give evidence for the property [beautiful] is
- straightforward. *)
-
-
-
-(** **** Exercise: 1 star (ev__even) *)
-(** Here is a proof that the inductive definition of evenness implies
- the computational one. *)
-
-Theorem ev__even : forall n,
- ev n -> even n.
-Proof.
- intros n E. induction E as [| n' E'].
- Case "E = ev_0".
- unfold even. reflexivity.
- Case "E = ev_SS n' E'".
- unfold even. apply IHE'.
-Qed.
-
-(** Could this proof also be carried out by induction on [n] instead
- of [E]? If not, why not? *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** The induction principle for inductively defined propositions does
- not follow quite the same form as that of inductively defined
- sets. For now, you can take the intuitive view that induction on
- evidence [ev n] is similar to induction on [n], but restricts our
- attention to only those numbers for which evidence [ev n] could be
- generated. We'll look at the induction principle of [ev] in more
- depth below, to explain what's really going on. *)
-
-(** **** Exercise: 1 star (l_fails) *)
-(** The following proof attempt will not succeed.
- Theorem l : forall n,
- ev n.
- Proof.
- intros n. induction n.
- Case "O". simpl. apply ev_0.
- Case "S".
- ...
- Intuitively, we expect the proof to fail because not every
- number is even. However, what exactly causes the proof to fail?
-
-(* FILL IN HERE *)
-*)
-(** [] *)
-
-(** **** Exercise: 2 stars (ev_sum) *)
-(** Here's another exercise requiring induction. *)
-
-Theorem ev_sum : forall n m,
- ev n -> ev m -> ev (n+m).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ##################################################### *)
-(** * Inductively Defined Propositions *)
-
-(** As a running example, let's
- define a simple property of natural numbers -- we'll call it
- "[beautiful]." *)
-
-(** Informally, a number is [beautiful] if it is [0], [3], [5], or the
- sum of two [beautiful] numbers.
-
- More pedantically, we can define [beautiful] numbers by giving four
- rules:
-
- - Rule [b_0]: The number [0] is [beautiful].
- - Rule [b_3]: The number [3] is [beautiful].
- - Rule [b_5]: The number [5] is [beautiful].
- - Rule [b_sum]: If [n] and [m] are both [beautiful], then so is
- their sum. *)
-(** ** Inference Rules *)
-(** We will see many definitions like this one during the rest
- of the course, and for purposes of informal discussions, it is
- helpful to have a lightweight notation that makes them easy to
- read and write. _Inference rules_ are one such notation: *)
-(**
- ----------- (b_0)
- beautiful 0
-
- ------------ (b_3)
- beautiful 3
-
- ------------ (b_5)
- beautiful 5
-
- beautiful n beautiful m
- --------------------------- (b_sum)
- beautiful (n+m)
-*)
-
-(** *** *)
-(** Each of the textual rules above is reformatted here as an
- inference rule; the intended reading is that, if the _premises_
- above the line all hold, then the _conclusion_ below the line
- follows. For example, the rule [b_sum] says that, if [n] and [m]
- are both [beautiful] numbers, then it follows that [n+m] is
- [beautiful] too. If a rule has no premises above the line, then
- its conclusion holds unconditionally.
-
- These rules _define_ the property [beautiful]. That is, if we
- want to convince someone that some particular number is [beautiful],
- our argument must be based on these rules. For a simple example,
- suppose we claim that the number [5] is [beautiful]. To support
- this claim, we just need to point out that rule [b_5] says so.
- Or, if we want to claim that [8] is [beautiful], we can support our
- claim by first observing that [3] and [5] are both [beautiful] (by
- rules [b_3] and [b_5]) and then pointing out that their sum, [8],
- is therefore [beautiful] by rule [b_sum]. This argument can be
- expressed graphically with the following _proof tree_: *)
-(**
- ----------- (b_3) ----------- (b_5)
- beautiful 3 beautiful 5
- ------------------------------- (b_sum)
- beautiful 8
-*)
-(** *** *)
-(**
- Of course, there are other ways of using these rules to argue that
- [8] is [beautiful], for instance:
- ----------- (b_5) ----------- (b_3)
- beautiful 5 beautiful 3
- ------------------------------- (b_sum)
- beautiful 8
-*)
-
-(** **** Exercise: 1 star (varieties_of_beauty) *)
-(** How many different ways are there to show that [8] is [beautiful]? *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** *** *)
-(** In Coq, we can express the definition of [beautiful] as
- follows: *)
-
-Inductive beautiful : nat -> Prop :=
- b_0 : beautiful 0
-| b_3 : beautiful 3
-| b_5 : beautiful 5
-| b_sum : forall n m, beautiful n -> beautiful m -> beautiful (n+m).
-
-
-(** The first line declares that [beautiful] is a proposition -- or,
- more formally, a family of propositions "indexed by" natural
- numbers. (That is, for each number [n], the claim that "[n] is
- [beautiful]" is a proposition.) Such a family of propositions is
- often called a _property_ of numbers. Each of the remaining lines
- embodies one of the rules for [beautiful] numbers.
-*)
-(** *** *)
-(**
- The rules introduced this way have the same status as proven
- theorems; that is, they are true axiomatically.
- So we can use Coq's [apply] tactic with the rule names to prove
- that particular numbers are [beautiful]. *)
-
-Theorem three_is_beautiful: beautiful 3.
-Proof.
- (* This simply follows from the rule [b_3]. *)
- apply b_3.
-Qed.
-
-Theorem eight_is_beautiful: beautiful 8.
-Proof.
- (* First we use the rule [b_sum], telling Coq how to
- instantiate [n] and [m]. *)
- apply b_sum with (n:=3) (m:=5).
- (* To solve the subgoals generated by [b_sum], we must provide
- evidence of [beautiful 3] and [beautiful 5]. Fortunately we
- have rules for both. *)
- apply b_3.
- apply b_5.
-Qed.
-
-(** *** *)
-(** As you would expect, we can also prove theorems that have
-hypotheses about [beautiful]. *)
-
-Theorem beautiful_plus_eight: forall n, beautiful n -> beautiful (8+n).
-Proof.
- intros n B.
- apply b_sum with (n:=8) (m:=n).
- apply eight_is_beautiful.
- apply B.
-Qed.
-
-(** **** Exercise: 2 stars (b_times2) *)
-Theorem b_times2: forall n, beautiful n -> beautiful (2*n).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars (b_timesm) *)
-Theorem b_timesm: forall n m, beautiful n -> beautiful (m*n).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ####################################################### *)
-(** ** Induction Over Evidence *)
-
-(** Besides _constructing_ evidence that numbers are beautiful, we can
- also _reason about_ such evidence. *)
-
-(** The fact that we introduced [beautiful] with an [Inductive]
- declaration tells Coq not only that the constructors [b_0], [b_3],
- [b_5] and [b_sum] are ways to build evidence, but also that these
- four constructors are the _only_ ways to build evidence that
- numbers are beautiful. *)
-
-(** In other words, if someone gives us evidence [E] for the assertion
- [beautiful n], then we know that [E] must have one of four shapes:
-
- - [E] is [b_0] (and [n] is [O]),
- - [E] is [b_3] (and [n] is [3]),
- - [E] is [b_5] (and [n] is [5]), or
- - [E] is [b_sum n1 n2 E1 E2] (and [n] is [n1+n2], where [E1] is
- evidence that [n1] is beautiful and [E2] is evidence that [n2]
- is beautiful). *)
-
-(** *** *)
-(** This permits us to _analyze_ any hypothesis of the form [beautiful
- n] to see how it was constructed, using the tactics we already
- know. In particular, we can use the [induction] tactic that we
- have already seen for reasoning about inductively defined _data_
- to reason about inductively defined _evidence_.
-
- To illustrate this, let's define another property of numbers: *)
-
-Inductive gorgeous : nat -> Prop :=
- g_0 : gorgeous 0
-| g_plus3 : forall n, gorgeous n -> gorgeous (3+n)
-| g_plus5 : forall n, gorgeous n -> gorgeous (5+n).
-
-(** **** Exercise: 1 star (gorgeous_tree) *)
-(** Write out the definition of [gorgeous] numbers using inference rule
- notation.
-
-(* FILL IN HERE *)
-[]
-*)
-
-
-(** **** Exercise: 1 star (gorgeous_plus13) *)
-Theorem gorgeous_plus13: forall n,
- gorgeous n -> gorgeous (13+n).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** *** *)
-(** It seems intuitively obvious that, although [gorgeous] and
- [beautiful] are presented using slightly different rules, they are
- actually the same property in the sense that they are true of the
- same numbers. Indeed, we can prove this. *)
-
-Theorem gorgeous__beautiful : forall n,
- gorgeous n -> beautiful n.
-Proof.
- intros n H.
- induction H as [|n'|n'].
- Case "g_0".
- apply b_0.
- Case "g_plus3".
- apply b_sum. apply b_3.
- apply IHgorgeous.
- Case "g_plus5".
- apply b_sum. apply b_5. apply IHgorgeous.
-Qed.
-
-(** Notice that the argument proceeds by induction on the _evidence_ [H]! *)
-
-(** Let's see what happens if we try to prove this by induction on [n]
- instead of induction on the evidence [H]. *)
-
-Theorem gorgeous__beautiful_FAILED : forall n,
- gorgeous n -> beautiful n.
-Proof.
- intros. induction n as [| n'].
- Case "n = 0". apply b_0.
- Case "n = S n'". (* We are stuck! *)
-Abort.
-
-(** The problem here is that doing induction on [n] doesn't yield a
- useful induction hypothesis. Knowing how the property we are
- interested in behaves on the predecessor of [n] doesn't help us
- prove that it holds for [n]. Instead, we would like to be able to
- have induction hypotheses that mention other numbers, such as [n -
- 3] and [n - 5]. This is given precisely by the shape of the
- constructors for [gorgeous]. *)
-
-
-
-
-(** **** Exercise: 2 stars (gorgeous_sum) *)
-Theorem gorgeous_sum : forall n m,
- gorgeous n -> gorgeous m -> gorgeous (n + m).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (beautiful__gorgeous) *)
-Theorem beautiful__gorgeous : forall n, beautiful n -> gorgeous n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (g_times2) *)
-(** Prove the [g_times2] theorem below without using [gorgeous__beautiful].
- You might find the following helper lemma useful. *)
-
-Lemma helper_g_times2 : forall x y z, x + (z + y)= z + x + y.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem g_times2: forall n, gorgeous n -> gorgeous (2*n).
-Proof.
- intros n H. simpl.
- induction H.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-
-(* ####################################################### *)
-(** ** [Inversion] on Evidence *)
-
-(** Another situation where we want to analyze evidence for evenness
- is when proving that, if [n] is even, then [pred (pred n)] is
- too. In this case, we don't need to do an inductive proof. The
- right tactic turns out to be [inversion]. *)
-
-Theorem ev_minus2: forall n,
- ev n -> ev (pred (pred n)).
-Proof.
- intros n E.
- inversion E as [| n' E'].
- Case "E = ev_0". simpl. apply ev_0.
- Case "E = ev_SS n' E'". simpl. apply E'. Qed.
-
-(** **** Exercise: 1 star, optional (ev_minus2_n) *)
-(** What happens if we try to use [destruct] on [n] instead of [inversion] on [E]? *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** *** *)
-(** Another example, in which [inversion] helps narrow down to
-the relevant cases. *)
-
-Theorem SSev__even : forall n,
- ev (S (S n)) -> ev n.
-Proof.
- intros n E.
- inversion E as [| n' E'].
- apply E'. Qed.
-
-(** ** [inversion] revisited *)
-
-(** These uses of [inversion] may seem a bit mysterious at first.
- Until now, we've only used [inversion] on equality
- propositions, to utilize injectivity of constructors or to
- discriminate between different constructors. But we see here
- that [inversion] can also be applied to analyzing evidence
- for inductively defined propositions.
-
- (You might also expect that [destruct] would be a more suitable
- tactic to use here. Indeed, it is possible to use [destruct], but
- it often throws away useful information, and the [eqn:] qualifier
- doesn't help much in this case.)
-
- Here's how [inversion] works in general. Suppose the name
- [I] refers to an assumption [P] in the current context, where
- [P] has been defined by an [Inductive] declaration. Then,
- for each of the constructors of [P], [inversion I] generates
- a subgoal in which [I] has been replaced by the exact,
- specific conditions under which this constructor could have
- been used to prove [P]. Some of these subgoals will be
- self-contradictory; [inversion] throws these away. The ones
- that are left represent the cases that must be proved to
- establish the original goal.
-
- In this particular case, the [inversion] analyzed the construction
- [ev (S (S n))], determined that this could only have been
- constructed using [ev_SS], and generated a new subgoal with the
- arguments of that constructor as new hypotheses. (It also
- produced an auxiliary equality, which happens to be useless here.)
- We'll begin exploring this more general behavior of inversion in
- what follows. *)
-
-
-(** **** Exercise: 1 star (inversion_practice) *)
-Theorem SSSSev__even : forall n,
- ev (S (S (S (S n)))) -> ev n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** The [inversion] tactic can also be used to derive goals by showing
- the absurdity of a hypothesis. *)
-
-Theorem even5_nonsense :
- ev 5 -> 2 + 2 = 9.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (ev_ev__ev) *)
-(** Finding the appropriate thing to do induction on is a
- bit tricky here: *)
-
-Theorem ev_ev__ev : forall n m,
- ev (n+m) -> ev n -> ev m.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (ev_plus_plus) *)
-(** Here's an exercise that just requires applying existing lemmas. No
- induction or even case analysis is needed, but some of the rewriting
- may be tedious. *)
-
-Theorem ev_plus_plus : forall n m p,
- ev (n+m) -> ev (n+p) -> ev (m+p).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-
-
-(* ####################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 4 stars (palindromes) *)
-(** A palindrome is a sequence that reads the same backwards as
- forwards.
-
- - Define an inductive proposition [pal] on [list X] that
- captures what it means to be a palindrome. (Hint: You'll need
- three cases. Your definition should be based on the structure
- of the list; just having a single constructor
- c : forall l, l = rev l -> pal l
- may seem obvious, but will not work very well.)
-
- - Prove that
- forall l, pal (l ++ rev l).
- - Prove that
- forall l, pal l -> l = rev l.
-*)
-
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 5 stars, optional (palindrome_converse) *)
-(** Using your definition of [pal] from the previous exercise, prove
- that
- forall l, l = rev l -> pal l.
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 4 stars, advanced (subsequence) *)
-(** A list is a _subsequence_ of another list if all of the elements
- in the first list occur in the same order in the second list,
- possibly with some extra elements in between. For example,
- [1,2,3]
- is a subsequence of each of the lists
- [1,2,3]
- [1,1,1,2,2,3]
- [1,2,7,3]
- [5,6,1,9,9,2,7,3,8]
- but it is _not_ a subsequence of any of the lists
- [1,2]
- [1,3]
- [5,6,2,1,7,3,8]
-
- - Define an inductive proposition [subseq] on [list nat] that
- captures what it means to be a subsequence. (Hint: You'll need
- three cases.)
-
- - Prove that subsequence is reflexive, that is, any list is a
- subsequence of itself.
-
- - Prove that for any lists [l1], [l2], and [l3], if [l1] is a
- subsequence of [l2], then [l1] is also a subsequence of [l2 ++
- l3].
-
- - (Optional, harder) Prove that subsequence is transitive -- that
- is, if [l1] is a subsequence of [l2] and [l2] is a subsequence
- of [l3], then [l1] is a subsequence of [l3]. Hint: choose your
- induction carefully!
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (R_provability) *)
-(** Suppose we give Coq the following definition:
- Inductive R : nat -> list nat -> Prop :=
- | c1 : R 0 []
- | c2 : forall n l, R n l -> R (S n) (n :: l)
- | c3 : forall n l, R (S n) l -> R n l.
- Which of the following propositions are provable?
-
- - [R 2 [1,0]]
- - [R 1 [1,2,1,0]]
- - [R 6 [3,2,1,0]]
-*)
-
-(** [] *)
-
-
-
-(* ####################################################### *)
-(** * Relations *)
-
-(** A proposition parameterized by a number (such as [ev] or
- [beautiful]) can be thought of as a _property_ -- i.e., it defines
- a subset of [nat], namely those numbers for which the proposition
- is provable. In the same way, a two-argument proposition can be
- thought of as a _relation_ -- i.e., it defines a set of pairs for
- which the proposition is provable. *)
-
-Module LeModule.
-
-
-(** One useful example is the "less than or equal to"
- relation on numbers. *)
-
-(** The following definition should be fairly intuitive. It
- says that there are two ways to give evidence that one number is
- less than or equal to another: either observe that they are the
- same number, or give evidence that the first is less than or equal
- to the predecessor of the second. *)
-
-Inductive le : nat -> nat -> Prop :=
- | le_n : forall n, le n n
- | le_S : forall n m, (le n m) -> (le n (S m)).
-
-Notation "m <= n" := (le m n).
-
-
-(** Proofs of facts about [<=] using the constructors [le_n] and
- [le_S] follow the same patterns as proofs about properties, like
- [ev] in chapter [Prop]. We can [apply] the constructors to prove [<=]
- goals (e.g., to show that [3<=3] or [3<=6]), and we can use
- tactics like [inversion] to extract information from [<=]
- hypotheses in the context (e.g., to prove that [(2 <= 1) -> 2+2=5].) *)
-
-(** *** *)
-(** Here are some sanity checks on the definition. (Notice that,
- although these are the same kind of simple "unit tests" as we gave
- for the testing functions we wrote in the first few lectures, we
- must construct their proofs explicitly -- [simpl] and
- [reflexivity] don't do the job, because the proofs aren't just a
- matter of simplifying computations.) *)
-
-Theorem test_le1 :
- 3 <= 3.
-Proof.
- (* WORKED IN CLASS *)
- apply le_n. Qed.
-
-Theorem test_le2 :
- 3 <= 6.
-Proof.
- (* WORKED IN CLASS *)
- apply le_S. apply le_S. apply le_S. apply le_n. Qed.
-
-Theorem test_le3 :
- (2 <= 1) -> 2 + 2 = 5.
-Proof.
- (* WORKED IN CLASS *)
- intros H. inversion H. inversion H2. Qed.
-
-(** *** *)
-(** The "strictly less than" relation [n < m] can now be defined
- in terms of [le]. *)
-
-End LeModule.
-
-Definition lt (n m:nat) := le (S n) m.
-
-Notation "m < n" := (lt m n).
-
-(** Here are a few more simple relations on numbers: *)
-
-Inductive square_of : nat -> nat -> Prop :=
- sq : forall n:nat, square_of n (n * n).
-
-Inductive next_nat (n:nat) : nat -> Prop :=
- | nn : next_nat n (S n).
-
-Inductive next_even (n:nat) : nat -> Prop :=
- | ne_1 : ev (S n) -> next_even n (S n)
- | ne_2 : ev (S (S n)) -> next_even n (S (S n)).
-
-(** **** Exercise: 2 stars (total_relation) *)
-(** Define an inductive binary relation [total_relation] that holds
- between every pair of natural numbers. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 2 stars (empty_relation) *)
-(** Define an inductive binary relation [empty_relation] (on numbers)
- that never holds. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (le_exercises) *)
-(** Here are a number of facts about the [<=] and [<] relations that
- we are going to need later in the course. The proofs make good
- practice exercises. *)
-
-Lemma le_trans : forall m n o, m <= n -> n <= o -> m <= o.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem O_le_n : forall n,
- 0 <= n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem n_le_m__Sn_le_Sm : forall n m,
- n <= m -> S n <= S m.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-Theorem Sn_le_Sm__n_le_m : forall n m,
- S n <= S m -> n <= m.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-
-Theorem le_plus_l : forall a b,
- a <= a + b.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem plus_lt : forall n1 n2 m,
- n1 + n2 < m ->
- n1 < m /\ n2 < m.
-Proof.
- unfold lt.
- (* FILL IN HERE *) Admitted.
-
-Theorem lt_S : forall n m,
- n < m ->
- n < S m.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem ble_nat_true : forall n m,
- ble_nat n m = true -> n <= m.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem le_ble_nat : forall n m,
- n <= m ->
- ble_nat n m = true.
-Proof.
- (* Hint: This may be easiest to prove by induction on [m]. *)
- (* FILL IN HERE *) Admitted.
-
-Theorem ble_nat_true_trans : forall n m o,
- ble_nat n m = true -> ble_nat m o = true -> ble_nat n o = true.
-Proof.
- (* Hint: This theorem can be easily proved without using [induction]. *)
- (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 2 stars, optional (ble_nat_false) *)
-Theorem ble_nat_false : forall n m,
- ble_nat n m = false -> ~(n <= m).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(** **** Exercise: 3 stars (R_provability) *)
-Module R.
-(** We can define three-place relations, four-place relations,
- etc., in just the same way as binary relations. For example,
- consider the following three-place relation on numbers: *)
-
-Inductive R : nat -> nat -> nat -> Prop :=
- | c1 : R 0 0 0
- | c2 : forall m n o, R m n o -> R (S m) n (S o)
- | c3 : forall m n o, R m n o -> R m (S n) (S o)
- | c4 : forall m n o, R (S m) (S n) (S (S o)) -> R m n o
- | c5 : forall m n o, R m n o -> R n m o.
-
-(** - Which of the following propositions are provable?
- - [R 1 1 2]
- - [R 2 2 6]
-
- - If we dropped constructor [c5] from the definition of [R],
- would the set of provable propositions change? Briefly (1
- sentence) explain your answer.
-
- - If we dropped constructor [c4] from the definition of [R],
- would the set of provable propositions change? Briefly (1
- sentence) explain your answer.
-
-(* FILL IN HERE *)
-[]
-*)
-
-(** **** Exercise: 3 stars, optional (R_fact) *)
-(** Relation [R] actually encodes a familiar function. State and prove two
- theorems that formally connects the relation and the function.
- That is, if [R m n o] is true, what can we say about [m],
- [n], and [o], and vice versa?
-*)
-
-(* FILL IN HERE *)
-(** [] *)
-
-End R.
-
-
-(* ##################################################### *)
-(** * Programming with Propositions Revisited *)
-
-(** As we have seen, a _proposition_ is a statement expressing a factual claim,
- like "two plus two equals four." In Coq, propositions are written
- as expressions of type [Prop]. . *)
-
-Check (2 + 2 = 4).
-(* ===> 2 + 2 = 4 : Prop *)
-
-Check (ble_nat 3 2 = false).
-(* ===> ble_nat 3 2 = false : Prop *)
-
-Check (beautiful 8).
-(* ===> beautiful 8 : Prop *)
-
-(** *** *)
-(** Both provable and unprovable claims are perfectly good
- propositions. Simply _being_ a proposition is one thing; being
- _provable_ is something else! *)
-
-Check (2 + 2 = 5).
-(* ===> 2 + 2 = 5 : Prop *)
-
-Check (beautiful 4).
-(* ===> beautiful 4 : Prop *)
-
-(** Both [2 + 2 = 4] and [2 + 2 = 5] are legal expressions
- of type [Prop]. *)
-
-(** *** *)
-(** We've mainly seen one place that propositions can appear in Coq: in
- [Theorem] (and [Lemma] and [Example]) declarations. *)
-
-Theorem plus_2_2_is_4 :
- 2 + 2 = 4.
-Proof. reflexivity. Qed.
-
-(** But they can be used in many other ways. For example, we have also seen that
- we can give a name to a proposition using a [Definition], just as we have
- given names to expressions of other sorts. *)
-
-Definition plus_fact : Prop := 2 + 2 = 4.
-Check plus_fact.
-(* ===> plus_fact : Prop *)
-
-(** We can later use this name in any situation where a proposition is
- expected -- for example, as the claim in a [Theorem] declaration. *)
-
-Theorem plus_fact_is_true :
- plus_fact.
-Proof. reflexivity. Qed.
-
-(** *** *)
-(** We've seen several ways of constructing propositions.
-
- - We can define a new proposition primitively using [Inductive].
-
- - Given two expressions [e1] and [e2] of the same type, we can
- form the proposition [e1 = e2], which states that their
- values are equal.
-
- - We can combine propositions using implication and
- quantification. *)
-(** *** *)
-(** We have also seen _parameterized propositions_, such as [even] and
- [beautiful]. *)
-
-Check (even 4).
-(* ===> even 4 : Prop *)
-Check (even 3).
-(* ===> even 3 : Prop *)
-Check even.
-(* ===> even : nat -> Prop *)
-
-(** *** *)
-(** The type of [even], i.e., [nat->Prop], can be pronounced in
- three equivalent ways: (1) "[even] is a _function_ from numbers to
- propositions," (2) "[even] is a _family_ of propositions, indexed
- by a number [n]," or (3) "[even] is a _property_ of numbers." *)
-
-(** Propositions -- including parameterized propositions -- are
- first-class citizens in Coq. For example, we can define functions
- from numbers to propositions... *)
-
-Definition between (n m o: nat) : Prop :=
- andb (ble_nat n o) (ble_nat o m) = true.
-
-(** ... and then partially apply them: *)
-
-Definition teen : nat->Prop := between 13 19.
-
-(** We can even pass propositions -- including parameterized
- propositions -- as arguments to functions: *)
-
-Definition true_for_zero (P:nat->Prop) : Prop :=
- P 0.
-
-(** *** *)
-(** Here are two more examples of passing parameterized propositions
- as arguments to a function.
-
- The first function, [true_for_all_numbers], takes a proposition
- [P] as argument and builds the proposition that [P] is true for
- all natural numbers. *)
-
-Definition true_for_all_numbers (P:nat->Prop) : Prop :=
- forall n, P n.
-
-(** The second, [preserved_by_S], takes [P] and builds the proposition
- that, if [P] is true for some natural number [n'], then it is also
- true by the successor of [n'] -- i.e. that [P] is _preserved by
- successor_: *)
-
-Definition preserved_by_S (P:nat->Prop) : Prop :=
- forall n', P n' -> P (S n').
-
-(** *** *)
-(** Finally, we can put these ingredients together to define
-a proposition stating that induction is valid for natural numbers: *)
-
-Definition natural_number_induction_valid : Prop :=
- forall (P:nat->Prop),
- true_for_zero P ->
- preserved_by_S P ->
- true_for_all_numbers P.
-
-
-
-
-
-(** **** Exercise: 3 stars (combine_odd_even) *)
-(** Complete the definition of the [combine_odd_even] function
- below. It takes as arguments two properties of numbers [Podd] and
- [Peven]. As its result, it should return a new property [P] such
- that [P n] is equivalent to [Podd n] when [n] is odd, and
- equivalent to [Peven n] otherwise. *)
-
-Definition combine_odd_even (Podd Peven : nat -> Prop) : nat -> Prop :=
- (* FILL IN HERE *) admit.
-
-(** To test your definition, see whether you can prove the following
- facts: *)
-
-Theorem combine_odd_even_intro :
- forall (Podd Peven : nat -> Prop) (n : nat),
- (oddb n = true -> Podd n) ->
- (oddb n = false -> Peven n) ->
- combine_odd_even Podd Peven n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem combine_odd_even_elim_odd :
- forall (Podd Peven : nat -> Prop) (n : nat),
- combine_odd_even Podd Peven n ->
- oddb n = true ->
- Podd n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Theorem combine_odd_even_elim_even :
- forall (Podd Peven : nat -> Prop) (n : nat),
- combine_odd_even Podd Peven n ->
- oddb n = false ->
- Peven n.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** [] *)
-
-(* ##################################################### *)
-(** One more quick digression, for adventurous souls: if we can define
- parameterized propositions using [Definition], then can we also
- define them using [Fixpoint]? Of course we can! However, this
- kind of "recursive parameterization" doesn't correspond to
- anything very familiar from everyday mathematics. The following
- exercise gives a slightly contrived example. *)
-
-(** **** Exercise: 4 stars, optional (true_upto_n__true_everywhere) *)
-(** Define a recursive function
- [true_upto_n__true_everywhere] that makes
- [true_upto_n_example] work. *)
-
-(*
-Fixpoint true_upto_n__true_everywhere
-(* FILL IN HERE *)
-
-Example true_upto_n_example :
- (true_upto_n__true_everywhere 3 (fun n => even n))
- = (even 3 -> even 2 -> even 1 -> forall m : nat, even m).
-Proof. reflexivity. Qed.
-*)
-(** [] *)
-
-
-(* $Date: 2014-06-05 07:22:21 -0400 (Thu, 05 Jun 2014) $ *)
-
-
diff --git a/Review1.html b/Review1.html
deleted file mode 100644
index 28ed967..0000000
--- a/Review1.html
+++ /dev/null
@@ -1,232 +0,0 @@
-
-
-
-
-
-Review1: Review Session for First Midterm
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Review1Review Session for First Midterm
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
General Notes
-
-
-
-
Standard vs. Advanced Exams
-
-
-
-
-
-
-
-
Unlike the homework assignments, we will make up two completely
- separate versions of the exam — a "standard exam" and an
- "advanced exam." They will share some problems, but there will
- be problems on each that are not on the other.
-
-
-
- You can choose to take whichever one you want at the beginning
- of the exam period.
-
-
-
-
-
-
-
Grading
-
-
-
-
-
-
-
-
Meaning of grades:
-
-
A = mastery of all or almost all of the material
-
-
-
B = good understanding of most of the material, perhaps with
- a few gaps
-
-
-
C = some understanding of most of the material, with
- substantial gaps
-
-
-
D = major gaps
-
-
-
F = didn't show up / try
-
-
-
-
-
-
-
-
-
-
-
-
There is no pre-determined curve. We'd be perfectly delighted
- to give everyone an A (for the exam, and for the course).
-
-
Except: A+ grades will be given only for completing the
- advanced track.
-
-
-
-
-
-
-
-
-
Standard and advanced exams will be graded relative to different
- expectations (i.e., "the material" is different)
-
-
-
-
-
-
-
Hints
-
-
-
-
-
-
-
-
On each version of the exam, will be at least one problem taken
- more or less verbatim from a homework assignment.
-
-
-
-
-
-
On the advanced version, there will be an informal proof.
-
-
-
-
-
-
-
-
-
-
-
-
Expressions and Their Types
-
-
-
- Thinking about well-typed expressions and their types is a great
- way of reviewing many aspects of how Coq works...
-
-
-
-
-
-
- (Discussion of Coq's view of the universe...)
-
-
-
-
-
-
-
-
Inductive Definitions
-
-
-
-
-
-
-
-
-
Tactics
-
-
-
-
-
-
-
-
-
Proof Objects
-
-
-
-
-
-
-
-
-
Functional Programming
-
-
-
-
-
-
-
-
-
Judging Propositions
-
-
-
-
-
-
-
-
-
More Type Checking
-
-
-
- Good luck on the exam!
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Review1.v b/Review1.v
deleted file mode 100644
index 411971d..0000000
--- a/Review1.v
+++ /dev/null
@@ -1,121 +0,0 @@
-(** * Review1: Review Session for First Midterm *)
-
-Require Export MoreInd.
-
-(* ###################################################################### *)
-(** * General Notes *)
-
-(** *** Standard vs. Advanced Exams *)
-
-(** - Unlike the homework assignments, we will make up two completely
- separate versions of the exam -- a "standard exam" and an
- "advanced exam." They will share some problems, but there will
- be problems on each that are not on the other.
-
- You can choose to take whichever one you want at the beginning
- of the exam period.
-*)
-
-(** *** Grading *)
-
-(** - Meaning of grades:
- - A = mastery of all or almost all of the material
- - B = good understanding of most of the material, perhaps with
- a few gaps
- - C = some understanding of most of the material, with
- substantial gaps
- - D = major gaps
- - F = didn't show up / try
-
- - There is no pre-determined curve. We'd be perfectly delighted
- to give everyone an A (for the exam, and for the course).
- - Except: A+ grades will be given only for completing the
- advanced track.
-
- - Standard and advanced exams will be graded relative to different
- expectations (i.e., "the material" is different)
-*)
-
-(** *** Hints *)
-
-(**
- - On each version of the exam, will be at least one problem taken
- more or less verbatim from a homework assignment.
-
- - On the advanced version, there will be an informal proof.
-*)
-
-
-(* ###################################################################### *)
-(** * Expressions and Their Types *)
-
-(** Thinking about well-typed expressions and their types is a great
- way of reviewing many aspects of how Coq works...
-
-*)
-
-
-
-
-
-
-
-
-
-
-
-
-(** (Discussion of Coq's view of the universe...) #
On each version of the exam, there will be at least one problem
- taken more or less verbatim from a homework assignment.
-
-
-
-
-
-
Both versions will include one or more decorated programs.
-
-
-
-
-
-
On the advanced version, there will be an informal proof.
-
-
-
-
-
-
This set of review questions is biased toward ones that can be
- discussed in class / using clickers, so it doesn't fully
- represent the range of questions that might show up on the exam.
-
-
-
- Make sure to have a look at some prior exams to get a sense of
- some other sorts of questions you might see.
-
-
-
-
-
-
-
-
-
-
-
-
Definitions
-
-
-
-
-
-
-
-
-
IMP Program Equivalence
-
-
-
-
Hoare triples
-
-
-
-
Decorated programs
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Review2.v b/Review2.v
deleted file mode 100644
index f513c16..0000000
--- a/Review2.v
+++ /dev/null
@@ -1,69 +0,0 @@
-(** * Review2: Review Session for Second Midterm *)
-
-Require Export Hoare2.
-
-(* ###################################################################### *)
-(** * General Notes *)
-
-(** *** Hints *)
-
-(**
- - On each version of the exam, there will be at least one problem
- taken more or less verbatim from a homework assignment.
-
- - Both versions will include one or more decorated programs.
-
- - On the advanced version, there will be an informal proof.
-
- - This set of review questions is biased toward ones that can be
- discussed in class / using clickers, so it doesn't fully
- represent the range of questions that might show up on the exam.
-
- Make sure to have a look at some prior exams to get a sense of
- some other sorts of questions you might see.
-*)
-
-
-(* ###################################################################### *)
-(** * Definitions *)
-
-
-
-(* ###################################################################### *)
-(** * IMP Program Equivalence *)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-(** * Hoare triples *)
-
-
-
-
-
-
-
-
-(** * Decorated programs *)
-
-
-
-
-
-
-
-
-(* $Date: 2013-11-20 13:03:49 -0500 (Wed, 20 Nov 2013) $ *)
diff --git a/SfLib.html b/SfLib.html
deleted file mode 100644
index 044898d..0000000
--- a/SfLib.html
+++ /dev/null
@@ -1,382 +0,0 @@
-
-
-
-
-
-SfLib: Software Foundations Library
-
-
-
-
-
-
-
-Here we collect together several useful definitions and theorems
- from Basics.v, List.v, Poly.v, Ind.v, and Logic.v that are not
- already in the Coq standard library. From now on we can Import
- or Export this file, instead of cluttering our environment with
- all the examples and false starts in those files.
-
-
-
From the Coq Standard Library
-
-
-
-
-
-RequireOmega. (* needed for using the omega tactic *)
-RequireExportBool.
-RequireExportList.
-ExportListNotations.
-RequireExportArith.
-RequireExportArith.EqNat. (* Contains beq_nat, among other things *)
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SfLib.v b/SfLib.v
deleted file mode 100644
index 5178d5b..0000000
--- a/SfLib.v
+++ /dev/null
@@ -1,250 +0,0 @@
-(** * SfLib: Software Foundations Library *)
-
-(* $Date: 2013-07-17 16:19:11 -0400 (Wed, 17 Jul 2013) $ *)
-
-(** Here we collect together several useful definitions and theorems
- from Basics.v, List.v, Poly.v, Ind.v, and Logic.v that are not
- already in the Coq standard library. From now on we can [Import]
- or [Export] this file, instead of cluttering our environment with
- all the examples and false starts in those files. *)
-
-(** * From the Coq Standard Library *)
-
-Require Omega. (* needed for using the [omega] tactic *)
-Require Export Bool.
-Require Export List.
-Export ListNotations.
-Require Export Arith.
-Require Export Arith.EqNat. (* Contains [beq_nat], among other things *)
-
-(** * From Basics.v *)
-
-Definition admit {T: Type} : T. Admitted.
-
-Require String. Open Scope string_scope.
-
-Ltac move_to_top x :=
- match reverse goal with
- | H : _ |- _ => try move x after H
- end.
-
-Tactic Notation "assert_eq" ident(x) constr(v) :=
- let H := fresh in
- assert (x = v) as H by reflexivity;
- clear H.
-
-Tactic Notation "Case_aux" ident(x) constr(name) :=
- first [
- set (x := name); move_to_top x
- | assert_eq x name; move_to_top x
- | fail 1 "because we are working on a different case" ].
-
-Tactic Notation "Case" constr(name) := Case_aux Case name.
-Tactic Notation "SCase" constr(name) := Case_aux SCase name.
-Tactic Notation "SSCase" constr(name) := Case_aux SSCase name.
-Tactic Notation "SSSCase" constr(name) := Case_aux SSSCase name.
-Tactic Notation "SSSSCase" constr(name) := Case_aux SSSSCase name.
-Tactic Notation "SSSSSCase" constr(name) := Case_aux SSSSSCase name.
-Tactic Notation "SSSSSSCase" constr(name) := Case_aux SSSSSSCase name.
-Tactic Notation "SSSSSSSCase" constr(name) := Case_aux SSSSSSSCase name.
-
-Fixpoint ble_nat (n m : nat) : bool :=
- match n with
- | O => true
- | S n' =>
- match m with
- | O => false
- | S m' => ble_nat n' m'
- end
- end.
-
-Theorem andb_true_elim1 : forall b c,
- andb b c = true -> b = true.
-Proof.
- intros b c H.
- destruct b.
- Case "b = true".
- reflexivity.
- Case "b = false".
- rewrite <- H. reflexivity. Qed.
-
-Theorem andb_true_elim2 : forall b c,
- andb b c = true -> c = true.
-Proof.
-(* An exercise in Basics.v *)
-Admitted.
-
-Theorem beq_nat_sym : forall (n m : nat),
- beq_nat n m = beq_nat m n.
-(* An exercise in Lists.v *)
-Admitted.
-
-(** * From Props.v *)
-
-Inductive ev : nat -> Prop :=
- | ev_0 : ev O
- | ev_SS : forall n:nat, ev n -> ev (S (S n)).
-
-(** * From Logic.v *)
-
-Theorem andb_true : forall b c,
- andb b c = true -> b = true /\ c = true.
-Proof.
- intros b c H.
- destruct b.
- destruct c.
- apply conj. reflexivity. reflexivity.
- inversion H.
- inversion H. Qed.
-
-Theorem false_beq_nat: forall n n' : nat,
- n <> n' ->
- beq_nat n n' = false.
-Proof.
-(* An exercise in Logic.v *)
-Admitted.
-
-Theorem ex_falso_quodlibet : forall (P:Prop),
- False -> P.
-Proof.
- intros P contra.
- inversion contra. Qed.
-
-Theorem ev_not_ev_S : forall n,
- ev n -> ~ ev (S n).
-Proof.
-(* An exercise in Logic.v *)
-Admitted.
-
-Theorem ble_nat_true : forall n m,
- ble_nat n m = true -> n <= m.
-(* An exercise in Logic.v *)
-Admitted.
-
-Theorem ble_nat_false : forall n m,
- ble_nat n m = false -> ~(n <= m).
-(* An exercise in Logic.v *)
-Admitted.
-
-Inductive appears_in (n : nat) : list nat -> Prop :=
-| ai_here : forall l, appears_in n (n::l)
-| ai_later : forall m l, appears_in n l -> appears_in n (m::l).
-
-Inductive next_nat (n:nat) : nat -> Prop :=
- | nn : next_nat n (S n).
-
-Inductive total_relation : nat -> nat -> Prop :=
- tot : forall n m : nat, total_relation n m.
-
-Inductive empty_relation : nat -> nat -> Prop := .
-
-(** * From Later Files *)
-
-Definition relation (X:Type) := X -> X -> Prop.
-
-Definition deterministic {X: Type} (R: relation X) :=
- forall x y1 y2 : X, R x y1 -> R x y2 -> y1 = y2.
-
-Inductive multi (X:Type) (R: relation X)
- : X -> X -> Prop :=
- | multi_refl : forall (x : X),
- multi X R x x
- | multi_step : forall (x y z : X),
- R x y ->
- multi X R y z ->
- multi X R x z.
-Implicit Arguments multi [[X]].
-
-Tactic Notation "multi_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "multi_refl" | Case_aux c "multi_step" ].
-
-Theorem multi_R : forall (X:Type) (R:relation X) (x y : X),
- R x y -> multi R x y.
-Proof.
- intros X R x y r.
- apply multi_step with y. apply r. apply multi_refl. Qed.
-
-Theorem multi_trans :
- forall (X:Type) (R: relation X) (x y z : X),
- multi R x y ->
- multi R y z ->
- multi R x z.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** Identifiers and polymorphic partial maps. *)
-
-Inductive id : Type :=
- Id : nat -> id.
-
-Theorem eq_id_dec : forall id1 id2 : id, {id1 = id2} + {id1 <> id2}.
-Proof.
- intros id1 id2.
- destruct id1 as [n1]. destruct id2 as [n2].
- destruct (eq_nat_dec n1 n2) as [Heq | Hneq].
- Case "n1 = n2".
- left. rewrite Heq. reflexivity.
- Case "n1 <> n2".
- right. intros contra. inversion contra. apply Hneq. apply H0.
-Defined.
-
-Lemma eq_id : forall (T:Type) x (p q:T),
- (if eq_id_dec x x then p else q) = p.
-Proof.
- intros.
- destruct (eq_id_dec x x); try reflexivity.
- apply ex_falso_quodlibet; auto.
-Qed.
-
-Lemma neq_id : forall (T:Type) x y (p q:T), x <> y ->
- (if eq_id_dec x y then p else q) = q.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-Definition partial_map (A:Type) := id -> option A.
-
-Definition empty {A:Type} : partial_map A := (fun _ => None).
-
-Notation "'\empty'" := empty.
-
-Definition extend {A:Type} (Gamma : partial_map A) (x:id) (T : A) :=
- fun x' => if eq_id_dec x x' then Some T else Gamma x'.
-
-Lemma extend_eq : forall A (ctxt: partial_map A) x T,
- (extend ctxt x T) x = Some T.
-Proof.
- intros. unfold extend. rewrite eq_id; auto.
-Qed.
-
-Lemma extend_neq : forall A (ctxt: partial_map A) x1 T x2,
- x2 <> x1 ->
- (extend ctxt x2 T) x1 = ctxt x1.
-Proof.
- intros. unfold extend. rewrite neq_id; auto.
-Qed.
-
-Lemma extend_shadow : forall A (ctxt: partial_map A) t1 t2 x1 x2,
- extend (extend ctxt x2 t1) x2 t2 x1 = extend ctxt x2 t2 x1.
-Proof with auto.
- intros. unfold extend. destruct (eq_id_dec x2 x1)...
-Qed.
-
-(** -------------------- *)
-
-(** * Some useful tactics *)
-
-Tactic Notation "solve_by_inversion_step" tactic(t) :=
- match goal with
- | H : _ |- _ => solve [ inversion H; subst; t ]
- end
- || fail "because the goal is not solvable by inversion.".
-
-Tactic Notation "solve" "by" "inversion" "1" :=
- solve_by_inversion_step idtac.
-Tactic Notation "solve" "by" "inversion" "2" :=
- solve_by_inversion_step (solve by inversion 1).
-Tactic Notation "solve" "by" "inversion" "3" :=
- solve_by_inversion_step (solve by inversion 2).
-Tactic Notation "solve" "by" "inversion" :=
- solve by inversion 1.
diff --git a/Smallstep.html b/Smallstep.html
deleted file mode 100644
index 99f4bad..0000000
--- a/Smallstep.html
+++ /dev/null
@@ -1,2712 +0,0 @@
-
-
-
-
-
-Smallstep: Small-step Operational Semantics
-
-
-
-
-
-
-
-
-
-
-
-
-
-
SmallstepSmall-step Operational Semantics
-
-
-
-
-
-
-
-
-
-
-RequireExportImp.
-
-
-
-
-
-The evaluators we have seen so far (e.g., the ones for
- aexps, bexps, and commands) have been formulated in a
- "big-step" style — they specify how a given expression can be
- evaluated to its final value (or a command plus a store to a final
- store) "all in one big step."
-
-
-
- This style is simple and natural for many purposes — indeed,
- Gilles Kahn, who popularized its use, called it natural
- semantics. But there are some things it does not do well. In
- particular, it does not give us a natural way of talking about
- concurrent programming languages, where the "semantics" of a
- program — i.e., the essence of how it behaves — is not just
- which input states get mapped to which output states, but also
- includes the intermediate states that it passes through along the
- way, since these states can also be observed by concurrently
- executing code.
-
-
-
- Another shortcoming of the big-step style is more technical, but
- critical in some situations. To see the issue, suppose we wanted
- to define a variant of Imp where variables could hold either
- numbers or lists of numbers (see the HoareList chapter for
- details). In the syntax of this extended language, it will be
- possible to write strange expressions like 2+nil, and our
- semantics for arithmetic expressions will then need to say
- something about how such expressions behave. One
- possibility (explored in the HoareList chapter) is to maintain
- the convention that every arithmetic expressions evaluates to some
- number by choosing some way of viewing a list as a number — e.g.,
- by specifying that a list should be interpreted as 0 when it
- occurs in a context expecting a number. But this is really a bit
- of a hack.
-
-
-
- A much more natural approach is simply to say that the behavior of
- an expression like 2+nil is undefined — it doesn't evaluate
- to any result at all. And we can easily do this: we just have to
- formulate aeval and beval as Inductive propositions rather
- than Fixpoints, so that we can make them partial functions instead
- of total ones.
-
-
-
- However, now we encounter a serious deficiency. In this language,
- a command might fail to map a given starting state to any ending
- state for two quite different reasons: either because the
- execution gets into an infinite loop or because, at some point,
- the program tries to do an operation that makes no sense, such as
- adding a number to a list, and none of the evaluation rules can be
- applied.
-
-
-
- These two outcomes — nontermination vs. getting stuck in an
- erroneous configuration — are quite different. In particular, we
- want to allow the first (permitting the possibility of infinite
- loops is the price we pay for the convenience of programming with
- general looping constructs like while) but prevent the
- second (which is just wrong), for example by adding some form of
- typechecking to the language. Indeed, this will be a major
- topic for the rest of the course. As a first step, we need a
- different way of presenting the semantics that allows us to
- distinguish nontermination from erroneous "stuck states."
-
-
-
- So, for lots of reasons, we'd like to have a finer-grained way of
- defining and reasoning about program behaviors. This is the topic
- of the present chapter. We replace the "big-step" eval relation
- with a "small-step" relation that specifies, for a given program,
- how the "atomic steps" of computation are performed.
-
-
-
-
-
-
-
-
A Toy Language
-
-
-
- To save space in the discussion, let's go back to an
- incredibly simple language containing just constants and
- addition. (We use single letters — C and P — for the
- constructor names, for brevity.) At the end of the chapter, we'll
- see how to apply the same techniques to the full Imp language.
-
-Now, here is the same evaluator, written in exactly the same
- style, but formulated as an inductively defined relation. Again,
- we use the notation t⇓n for "t evaluates to n."
-
-
-
-
-
-
- (E_Const)
-
-
-
-
-
-
C n ⇓ n
-
-
-
-
-
t1⇓ n1
-
-
-
-
t2⇓ n2
-
- (E_Plus)
-
-
-
-
-
-
P t1 t2⇓ C (n1 + n2)
-
-
-
-
-
-
-
-Reserved Notation " t '⇓' n " (atlevel 50, leftassociativity).
-
-
-Inductiveeval : tm→nat→Prop :=
-
We are defining just a single reduction step, in which
- one P node is replaced by its value.
-
-
-
-
-
-
Each step finds the leftmostP node that is ready to
- go (both of its operands are constants) and rewrites it in
- place. The first rule tells how to rewrite this P node
- itself; the other two rules tell how to find it.
-
-
-
-
-
-
A term that is just a constant cannot take a step.
-
-
-
-
-
- Let's pause and check a couple of examples of reasoning with
- the step relation...
-
-
- If t1 can take a step to t1', then Pt1t2 steps
- to Pt1't2:
-
- Right-hand sides of sums can take a step only when the
- left-hand side is finished: if t2 can take a step to t2',
- then P(Cn)t2 steps to P(Cn)
- t2':
-
-
-
-
-Exampletest_step_2 :
- P
- (C 0)
- (P
- (C 2)
- (P (C 0) (C 3)))
- ⇒
- P
- (C 0)
- (P
- (C 2)
- (C (0 + 3))).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Relations
-
-
-
- We will be using several different step relations, so it is
- helpful to generalize a bit...
-
-
- A (binary) relation on a set X is a family of propositions
- parameterized by two elements of X — i.e., a proposition about
- pairs of elements of X.
-
-Our main examples of such relations in this chapter will be
- the single-step and multi-step reduction relations on terms, ⇒
- and ⇒*, but there are many other examples — some that come to
- mind are the "equals," "less than," "less than or equal to," and
- "is the square of" relations on numbers, and the "prefix of"
- relation on lists and strings.
-
-
- One simple property of the ⇒ relation is that, like the
- evaluation relation for our language of Imp programs, it is
- deterministic.
-
-
-
- Theorem: For each t, there is at most one t' such that t
- steps to t' (t⇒t' is provable). Formally, this is the
- same as saying that ⇒ is deterministic.
-
-
- Proof sketch: We show that if x steps to both y1 and y2
- then y1 and y2 are equal, by induction on a derivation of
- stepxy1. There are several cases to consider, depending on
- the last rule used in this derivation and in the given derivation
- of stepxy2.
-
-
-
-
-
If both are ST_PlusConstConst, the result is immediate.
-
-
-
-
-
-
The cases when both derivations end with ST_Plus1 or
- ST_Plus2 follow by the induction hypothesis.
-
-
-
-
-
-
It cannot happen that one is ST_PlusConstConst and the other
- is ST_Plus1 or ST_Plus2, since this would imply that x has
- the form Pt1t2 where both t1 and t2 are
- constants (by ST_PlusConstConst) and one of t1 or t2 has
- the form P....
-
-
-
-
-
-
Similarly, it cannot happen that one is ST_Plus1 and the other
- is ST_Plus2, since this would imply that x has the form
- Pt1t2 where t1 has both the form Pt1t2 and
- the form Cn. ☐
-
-Having introduced the idea of values, we can use it in the
- definition of the ⇒ relation to write ST_Plus2 rule in a
- slightly more elegant way:
-
-
-
-
-
-
-
-
-
- (ST_PlusConstConst)
-
-
-
-
-
-
P (C n1) (C n2) ⇒ C (n1 + n2)
-
-
-
-
-
t1⇒ t1'
-
- (ST_Plus1)
-
-
-
-
-
-
P t1 t2⇒ P t1' t2
-
-
-
-
-
value v1
-
-
-
-
t2⇒ t2'
-
- (ST_Plus2)
-
-
-
-
-
-
P v1 t2⇒ P v1 t2'
-
-
-
Again, the variable names here carry important information:
- by convention, v1 ranges only over values, while t1 and t2
- range over arbitrary terms. (Given this convention, the explicit
- value hypothesis is arguably redundant. We'll keep it for now,
- to maintain a close correspondence between the informal and Coq
- versions of the rules, but later on we'll drop it in informal
- rules, for the sake of brevity.)
-
- As a sanity check on this change, let's re-verify determinism
-
-
-
- Proof sketch: We must show that if x steps to both y1 and y2
- then y1 and y2 are equal. Consider the final rules used in
- the derivations of stepxy1 and stepxy2.
-
-
-
-
-
If both are ST_PlusConstConst, the result is immediate.
-
-
-
-
-
-
It cannot happen that one is ST_PlusConstConst and the other
- is ST_Plus1 or ST_Plus2, since this would imply that x has
- the form Pt1t2 where both t1 and t2 are
- constants (by ST_PlusConstConst) AND one of t1 or t2 has
- the form P....
-
-
-
-
-
-
Similarly, it cannot happen that one is ST_Plus1 and the other
- is ST_Plus2, since this would imply that x has the form
- Pt1t2 where t1 both has the form Pt1t2 and
- is a value (hence has the form Cn).
-
-
-
-
-
-
The cases when both derivations end with ST_Plus1 or
- ST_Plus2 follow by the induction hypothesis. ☐
-
-
-
-
-
- Most of this proof is the same as the one above. But to get
- maximum benefit from the exercise you should try to write it from
- scratch and just use the earlier one if you get stuck.
-
-
-
-
-Theoremstep_deterministic :
- deterministicstep.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Strong Progress and Normal Forms
-
-
-
- The definition of single-step reduction for our toy language is
- fairly simple, but for a larger language it would be pretty easy
- to forget one of the rules and create a situation where some term
- cannot take a step even though it has not been completely reduced
- to a value. The following theorem shows that we did not, in fact,
- make such a mistake here.
-
-
- Theorem (Strong Progress): If t is a term, then either t
- is a value, or there exists a term t' such that t⇒t'.
-
-
- Proof: By induction on t.
-
-
-
-
-
Suppose t=Cn. Then t is a value.
-
-
-
-
-
-
Suppose t=Pt1t2, where (by the IH) t1 is either a
- value or can step to some t1', and where t2 is either a
- value or can step to some t2'. We must show Pt1t2 is
- either a value or steps to some t'.
-
-
-
-
-
If t1 and t2 are both values, then t can take a step, by
- ST_PlusConstConst.
-
-
-
-
-
-
If t1 is a value and t2 can take a step, then so can t,
- by ST_Plus2.
-
-
-
-
-
-
If t1 can take a step, then so can t, by ST_Plus1. ☐
-
-This important property is called strong progress, because
- every term either is a value or can "make progress" by stepping to
- some other term. (The qualifier "strong" distinguishes it from a
- more refined version that we'll see in later chapters, called
- simply "progress.")
-
-
- The idea of "making progress" can be extended to tell us something
- interesting about values: in this language values are exactly
- the terms that cannot make progress in this sense.
-
-
-
- To state this observation formally, let's begin by giving a name
- to terms that cannot make progress. We'll call them normal
- forms.
-
-This definition actually specifies what it is to be a normal form
- for an arbitrary relation R over an arbitrary set X, not
- just for the particular single-step reduction relation over terms
- that we are interested in at the moment. We'll re-use the same
- terminology for talking about other relations later in the
- course.
-
-
- We can use this terminology to generalize the observation we made
- in the strong progress theorem: in this language, normal forms and
- values are actually the same thing.
-
-
- Because value is a syntactic concept — it is defined by looking
- at the form of a term — while normal_form is a semantic one —
- it is defined by looking at how the term steps. It is not obvious
- that these concepts should coincide!
-
-
-
- Indeed, we could easily have written the definitions so that they
- would not coincide...
-
-
-
-
-
-
-
-
-
-We might, for example, mistakenly define value so that it
- includes some terms that are not finished reducing.
-
-
-
-
-ModuleTemp1.
-(* Open an inner module so we can redefine value and step. *)
-
-
-Inductivevalue : tm→Prop :=
-| v_const : ∀n, value (Cn)
-| v_funny : ∀t1n2, (* <---- *)
- value (Pt1 (Cn2)).
-
-
-Reserved Notation " t '⇒' t' " (atlevel 40).
-
-
-Inductivestep : tm→tm→Prop :=
- | ST_PlusConstConst : ∀n1n2,
- P (Cn1) (Cn2) ⇒C (n1 + n2)
- | ST_Plus1 : ∀t1t1't2,
- t1⇒t1'→
- Pt1t2⇒Pt1't2
- | ST_Plus2 : ∀v1t2t2',
- valuev1→
- t2⇒t2'→
- Pv1t2⇒Pv1t2'
-
- where " t '⇒' t' " := (steptt').
-
-
-
-Finally, we might define value and step so that there is some
- term that is not a value but that cannot take a step in the step
- relation. Such terms are said to be stuck. In this case this is
- caused by a mistake in the semantics, but we will also see
- situations where, even in a correct language definition, it makes
- sense to allow some terms to be stuck.
-
-Lemmavalue_not_same_as_normal_form :
- ∃t, ¬ valuet∧normal_formstept.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-EndTemp3.
-
-
-
-
-
-
Additional Exercises
-
-
-
-
-
-ModuleTemp4.
-
-
-
-
-
-Here is another very simple language whose terms, instead of being
- just plus and numbers, are just the booleans true and false and a
- conditional expression...
-
- Which of the following propositions are provable? (This is just a
- thought exercise, but for an extra challenge feel free to prove
- your answers in Coq.)
-
-
-
-
-Definitionbool_step_prop1 :=
- tfalse⇒tfalse.
-
-
-(* FILL IN HERE *)
-
-
-Definitionbool_step_prop2 :=
- tif
- ttrue
- (tifttruettruettrue)
- (tiftfalsetfalsetfalse)
- ⇒
- ttrue.
-
-
-(* FILL IN HERE *)
-
-
-Definitionbool_step_prop3 :=
- tif
- (tifttruettruettrue)
- (tifttruettruettrue)
- tfalse
- ⇒
- tif
- ttrue
- (tifttruettruettrue)
- tfalse.
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 3 stars, optional (progress_bool)
- Just as we proved a progress theorem for plus expressions, we can
- do so for boolean expressions, as well.
-
-
-
-
-Theoremstrong_progress : ∀t,
- valuet∨ (∃t', t⇒t').
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (step_deterministic)
-
-
-
-Theoremstep_deterministic :
- deterministicstep.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-ModuleTemp5.
-
-
-
-
-
-
Exercise: 2 stars (smallstep_bool_shortcut)
- Suppose we want to add a "short circuit" to the step relation for
- boolean expressions, so that it can recognize when the then and
- else branches of a conditional are the same value (either
- ttrue or tfalse) and reduce the whole conditional to this
- value in a single step, even if the guard has not yet been reduced
- to a value. For example, we would like this proposition to be
- provable:
-
-
- It can be shown that the determinism and strong progress theorems
- for the step relation in the lecture notes also hold for the
- definition of step given above. After we add the clause
- ST_ShortCircuit...
-
-
-
-
-
Is the step relation still deterministic? Write yes or no and
- briefly (1 sentence) explain your answer.
-
-
-
- Optional: prove your answer correct in Coq.
-
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-
-
-
Does a strong progress theorem hold? Write yes or no and
- briefly (1 sentence) explain your answer.
-
-
-
- Optional: prove your answer correct in Coq.
-
-
-
-
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-
-
-
-
-
In general, is there any way we could cause strong progress to
- fail if we took away one or more constructors from the original
- step relation? Write yes or no and briefly (1 sentence) explain
- your answer.
-
-
-
-
-
-
-(* FILL IN HERE *)
- ☐
-
-
-
-
-EndTemp5.
-EndTemp4.
-
-
-
-
-
-
Multi-Step Reduction
-
-
-
- Until now, we've been working with the single-step reduction
- relation ⇒, which formalizes the individual steps of an
- abstract machine for executing programs.
-
-
-
- We can also use this machine to reduce programs to completion —
- to find out what final result they yield. This can be formalized
- as follows:
-
-
-
-
-
First, we define a multi-step reduction relation⇒*, which
- relates terms t and t' if t can reach t' by any number
- of single reduction steps (including zero steps!).
-
-
-
-
-
-
Then we define a "result" of a term t as a normal form that
- t can reach by multi-step reduction.
-
-
-
-
-
-
-
-
-
-
-
-
-Since we'll want to reuse the idea of multi-step reduction many
- times in this and future chapters, let's take a little extra
- trouble here and define it generically.
-
-
-
- Given a relation R, we define a relation multiR, called the
- multi-step closure of R as follows:
-
-We write ⇒* for the multistep relation — i.e., the
- relation that relates two terms t and t' if we can get from
- t to t' using the step relation zero or more times.
-
-The relation multiR has several crucial properties.
-
-
-
- First, it is obviously reflexive (that is, ∀x,multiRx
- x). In the case of the ⇒* (i.e. multistep) relation, the
- intuition is that a term can execute to itself by taking zero
- steps of execution.
-
-
-
- Second, it contains R — that is, single-step executions are a
- particular case of multi-step executions. (It is this fact that
- justifies the word "closure" in the term "multi-step closure of
- R.")
-
-We have already seen that, for our language, single-step reduction is
- deterministic — i.e., a given term can take a single step in
- at most one way. It follows from this that, if t can reach
- a normal form, then this normal form is unique. In other words, we
- can actually pronounce normal_formtt' as "t' is the
- normal form of t."
-
-
-
Exercise: 3 stars, optional (normal_forms_unique)
-
-
-
-Theoremnormal_forms_unique:
- deterministicnormal_form_of.
-Proof.
- unfolddeterministic. unfoldnormal_form_of. introsxy1y2P1P2.
- inversionP1as [P11P12]; clearP1. inversionP2as [P21P22]; clearP2.
- generalizedependenty2.
- (* We recommend using this initial setup as-is! *)
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- Indeed, something stronger is true for this language (though not
- for all languages): the reduction of any term t will
- eventually reach a normal form — i.e., normal_form_of is a
- total function. Formally, we say the step relation is
- normalizing.
-
-To prove that step is normalizing, we need a couple of lemmas.
-
-
-
- First, we observe that, if t reduces to t' in many steps, then
- the same sequence of reduction steps within t is also possible
- when t appears as the left-hand child of a P node, and
- similarly when t appears as the right-hand child of a P
- node whose left-hand child is a value.
-
-
- Theorem: The step function is normalizing — i.e., for every
- t there exists some t' such that t steps to t' and t' is
- a normal form.
-
-
-
- Proof sketch: By induction on terms. There are two cases to
- consider:
-
-
-
-
-
t=Cn for some n. Here t doesn't take a step,
- and we have t'=t. We can derive the left-hand side by
- reflexivity and the right-hand side by observing (a) that values
- are normal forms (by nf_same_as_value) and (b) that t is a
- value (by v_const).
-
-
-
-
-
-
t=Pt1t2 for some t1 and t2. By the IH, t1 and
- t2 have normal forms t1' and t2'. Recall that normal
- forms are values (by nf_same_as_value); we know that t1'=
- Cn1 and t2'=Cn2, for some n1 and n2.
- We can combine the ⇒* derivations for t1 and t2 to prove
- that Pt1t2 reduces in many steps to C(n1+n2).
-
-
-
- It is clear that our choice of t'=C(n1+n2) is a
- value, which is in turn a normal form. ☐
-
-
- Having defined the operational semantics of our tiny programming
- language in two different styles, it makes sense to ask whether
- these definitions actually define the same thing! They do, though
- it takes a little work to show it. (The details are left as an
- exercise).
-
-
-
Exercise: 3 stars (eval__multistep)
-
-
-
-Theoremeval__multistep : ∀tn,
- t⇓n→t⇒*Cn.
-
-
-
-
-
-The key idea behind the proof comes from the following picture:
-
-
-
-
- Pt1t2⇒ (byST_Plus1)
- Pt1't2⇒ (byST_Plus1)
- Pt1''t2⇒ (byST_Plus1)
- ...
- P (Cn1) t2⇒ (byST_Plus2)
- P (Cn1) t2'⇒ (byST_Plus2)
- P (Cn1) t2''⇒ (byST_Plus2)
- ...
- P (Cn1) (Cn2) ⇒ (byST_PlusConstConst)
- C (n1 + n2)
-
-
-
- That is, the multistep reduction of a term of the form Pt1t2
- proceeds in three phases:
-
-
-
-
-
First, we use ST_Plus1 some number of times to reduce t1
- to a normal form, which must (by nf_same_as_value) be a
- term of the form Cn1 for some n1.
-
-
-
Next, we use ST_Plus2 some number of times to reduce t2
- to a normal form, which must again be a term of the form C
- n2 for some n2.
-
-
-
Finally, we use ST_PlusConstConst one time to reduce P(C
- n1)(Cn2) to C(n1+n2).
-
-
-
-
-
- To formalize this intuition, you'll need to use the congruence
- lemmas from above (you might want to review them now, so that
- you'll be able to recognize when they are useful), plus some basic
- properties of ⇒*: that it is reflexive, transitive, and
- includes ⇒.
-
-
-
-
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 3 stars, advanced (eval__multistep_inf)
- Write a detailed informal version of the proof of eval__multistep.
-
-
-
-(* FILL IN HERE *)
-☐
- For the other direction, we need one lemma, which establishes a
- relation between single-step reduction and big-step evaluation.
-
-
-
Exercise: 3 stars (step__eval)
-
-
-
-Lemmastep__eval : ∀tt'n,
- t⇒t'→
- t'⇓n→
- t⇓n.
-Proof.
- introstt'nHs. generalizedependentn.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- The fact that small-step reduction implies big-step is now
- straightforward to prove, once it is stated correctly.
-
-
-
- The proof proceeds by induction on the multi-step reduction
- sequence that is buried in the hypothesis normal_form_oftt'. Make sure you understand the statement before you start to
- work on the proof.
-
-
-
Exercise: 3 stars (multistep__eval)
-
-
-
-Theoremmultistep__eval : ∀tt',
- normal_form_oftt'→∃n, t' = Cn∧t⇓n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 3 stars, optional (interp_tm)
- Remember that we also defined big-step evaluation of tms as a
- function evalF. Prove that it is equivalent to the existing
- semantics.
-
-
-
- Hint: we just proved that eval and multistep are
- equivalent, so logically it doesn't matter which you choose.
- One will be easier than the other, though!
-
-
-
-
-TheoremevalF_eval : ∀tn,
- evalFt = n↔t⇓n.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 4 stars (combined_properties)
- We've considered the arithmetic and conditional expressions
- separately. This exercise explores how the two interact.
-
-Earlier, we separately proved for both plus- and if-expressions...
-
-
-
-
-
that the step relation was deterministic, and
-
-
-
-
-
-
a strong progress lemma, stating that every term is either a
- value or can take a step.
-
-
-
- Prove or disprove these two properties for the combined language.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-EndCombined.
-
-
-
-
-
-
Small-Step Imp
-
-
-
- For a more serious example, here is the small-step version of the
- Imp operational semantics.
-
-
- The small-step evaluation relations for arithmetic and boolean
- expressions are straightforward extensions of the tiny language
- we've been working up to now. To make them easier to read, we
- introduce the symbolic notations ⇒a and ⇒b, respectively,
- for the arithmetic and boolean step relations.
-
-We are not actually going to bother to define boolean
- values, since they aren't needed in the definition of ⇒b
- below (why?), though they might be if our language were a bit
- larger (why?).
-
-The semantics of commands is the interesting part. We need two
- small tricks to make it work:
-
-
-
-
-
We use SKIP as a "command value" — i.e., a command that
- has reached a normal form.
-
-
-
-
-
An assignment command reduces to SKIP (and an updated
- state).
-
-
-
-
-
-
The sequencing command waits until its left-hand
- subcommand has reduced to SKIP, then throws it away so
- that reduction can continue with the right-hand
- subcommand.
-
-
-
-
-
-
-
-
-
We reduce a WHILE command by transforming it into a
- conditional followed by the same WHILE.
-
-
-
-
-
- (There are other ways of achieving the effect of the latter
- trick, but they all share the feature that the original WHILE
- command needs to be saved somewhere while a single copy of the loop
- body is being evaluated.)
-
-
-
-
-Reserved Notation " t '/' st '⇒' t' '/' st' "
- (atlevel 40, statlevel 39, t'atlevel 39).
-
-
-Inductivecstep : (com × state) → (com × state) →Prop :=
- | CS_AssStep : ∀stiaa',
- a / st⇒aa'→
- (i ::= a) / st⇒ (i ::= a') / st
- | CS_Ass : ∀stin,
- (i ::= (ANumn)) / st⇒SKIP / (updatestin)
- | CS_SeqStep : ∀stc1c1'st'c2,
- c1 / st⇒c1' / st'→
- (c1 ;; c2) / st⇒ (c1' ;; c2) / st'
- | CS_SeqFinish : ∀stc2,
- (SKIP ;; c2) / st⇒c2 / st
- | CS_IfTrue : ∀stc1c2,
- IFBBTrueTHENc1ELSEc2FI / st⇒c1 / st
- | CS_IfFalse : ∀stc1c2,
- IFBBFalseTHENc1ELSEc2FI / st⇒c2 / st
- | CS_IfStep : ∀stbb'c1c2,
- b / st⇒bb'→
- IFBbTHENc1ELSEc2FI / st⇒ (IFBb'THENc1ELSEc2FI) / st
- | CS_While : ∀stbc1,
- (WHILEbDOc1END) / st
- ⇒ (IFBbTHEN (c1;; (WHILEbDOc1END)) ELSESKIPFI) / st
-
- where " t '/' st '⇒' t' '/' st' " := (cstep (t,st) (t',st')).
-
-
-
-
-
-
Concurrent Imp
-
-
-
- Finally, to show the power of this definitional style, let's
- enrich Imp with a new form of command that runs two subcommands in
- parallel and terminates when both have terminated. To reflect the
- unpredictability of scheduling, the actions of the subcommands may
- be interleaved in any order, but they share the same memory and
- can communicate by reading and writing the same variables.
-
-Among the many interesting properties of this language is the fact
- that the following program can terminate with the variable X set
- to any value...
-
-
-
-
-Definitionpar_loop : com :=
- PAR
- Y ::= ANum 1
- WITH
- WHILEBEq (AIdY) (ANum 0) DO
- X ::= APlus (AIdX) (ANum 1)
- END
- END.
-
-
-
-
-
-In particular, it can terminate with X set to 0:
-
-
-
-Definitionstack_multistepst := multi (stack_stepst).
-
-
-
-
-
-
Exercise: 3 stars, advanced (compiler_is_correct)
- Remember the definition of compile for aexp given in the
- Imp chapter. We want now to prove compile correct with respect
- to the stack machine.
-
-
-
- State what it means for the compiler to be correct according to
- the stack machine small step semantics and then prove it.
-
-
-
-
-Definitioncompiler_is_correct_statement : Prop :=
-(* FILL IN HERE *)admit.
-
-
-Theoremcompiler_is_correct : compiler_is_correct_statement.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-
-
\ No newline at end of file
diff --git a/Smallstep.v b/Smallstep.v
deleted file mode 100644
index 490535d..0000000
--- a/Smallstep.v
+++ /dev/null
@@ -1,1661 +0,0 @@
-(** * Smallstep: Small-step Operational Semantics *)
-
-Require Export Imp.
-
-
-
-(** The evaluators we have seen so far (e.g., the ones for
- [aexp]s, [bexp]s, and commands) have been formulated in a
- "big-step" style -- they specify how a given expression can be
- evaluated to its final value (or a command plus a store to a final
- store) "all in one big step."
-
- This style is simple and natural for many purposes -- indeed,
- Gilles Kahn, who popularized its use, called it _natural
- semantics_. But there are some things it does not do well. In
- particular, it does not give us a natural way of talking about
- _concurrent_ programming languages, where the "semantics" of a
- program -- i.e., the essence of how it behaves -- is not just
- which input states get mapped to which output states, but also
- includes the intermediate states that it passes through along the
- way, since these states can also be observed by concurrently
- executing code.
-
- Another shortcoming of the big-step style is more technical, but
- critical in some situations. To see the issue, suppose we wanted
- to define a variant of Imp where variables could hold _either_
- numbers _or_ lists of numbers (see the [HoareList] chapter for
- details). In the syntax of this extended language, it will be
- possible to write strange expressions like [2 + nil], and our
- semantics for arithmetic expressions will then need to say
- something about how such expressions behave. One
- possibility (explored in the [HoareList] chapter) is to maintain
- the convention that every arithmetic expressions evaluates to some
- number by choosing some way of viewing a list as a number -- e.g.,
- by specifying that a list should be interpreted as [0] when it
- occurs in a context expecting a number. But this is really a bit
- of a hack.
-
- A much more natural approach is simply to say that the behavior of
- an expression like [2+nil] is _undefined_ -- it doesn't evaluate
- to any result at all. And we can easily do this: we just have to
- formulate [aeval] and [beval] as [Inductive] propositions rather
- than Fixpoints, so that we can make them partial functions instead
- of total ones.
-
- However, now we encounter a serious deficiency. In this language,
- a command might _fail_ to map a given starting state to any ending
- state for two quite different reasons: either because the
- execution gets into an infinite loop or because, at some point,
- the program tries to do an operation that makes no sense, such as
- adding a number to a list, and none of the evaluation rules can be
- applied.
-
- These two outcomes -- nontermination vs. getting stuck in an
- erroneous configuration -- are quite different. In particular, we
- want to allow the first (permitting the possibility of infinite
- loops is the price we pay for the convenience of programming with
- general looping constructs like [while]) but prevent the
- second (which is just wrong), for example by adding some form of
- _typechecking_ to the language. Indeed, this will be a major
- topic for the rest of the course. As a first step, we need a
- different way of presenting the semantics that allows us to
- distinguish nontermination from erroneous "stuck states."
-
- So, for lots of reasons, we'd like to have a finer-grained way of
- defining and reasoning about program behaviors. This is the topic
- of the present chapter. We replace the "big-step" [eval] relation
- with a "small-step" relation that specifies, for a given program,
- how the "atomic steps" of computation are performed. *)
-
-
-(* ########################################################### *)
-(** * A Toy Language *)
-
-(** To save space in the discussion, let's go back to an
- incredibly simple language containing just constants and
- addition. (We use single letters -- [C] and [P] -- for the
- constructor names, for brevity.) At the end of the chapter, we'll
- see how to apply the same techniques to the full Imp language. *)
-
-Inductive tm : Type :=
- | C : nat -> tm (* Constant *)
- | P : tm -> tm -> tm. (* Plus *)
-
-Tactic Notation "tm_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "C" | Case_aux c "P" ].
-
-(** Here is a standard evaluator for this language, written in the
- same (big-step) style as we've been using up to this point. *)
-
-Fixpoint evalF (t : tm) : nat :=
- match t with
- | C n => n
- | P a1 a2 => evalF a1 + evalF a2
- end.
-
-(** Now, here is the same evaluator, written in exactly the same
- style, but formulated as an inductively defined relation. Again,
- we use the notation [t || n] for "[t] evaluates to [n]." *)
-(**
- -------- (E_Const)
- C n || n
-
- t1 || n1
- t2 || n2
- ---------------------- (E_Plus)
- P t1 t2 || C (n1 + n2)
-*)
-
-Reserved Notation " t '||' n " (at level 50, left associativity).
-
-Inductive eval : tm -> nat -> Prop :=
- | E_Const : forall n,
- C n || n
- | E_Plus : forall t1 t2 n1 n2,
- t1 || n1 ->
- t2 || n2 ->
- P t1 t2 || (n1 + n2)
-
- where " t '||' n " := (eval t n).
-
-Tactic Notation "eval_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "E_Const" | Case_aux c "E_Plus" ].
-
-Module SimpleArith1.
-
-(** Now, here is a small-step version. *)
-(**
- ------------------------------- (ST_PlusConstConst)
- P (C n1) (C n2) ==> C (n1 + n2)
-
- t1 ==> t1'
- -------------------- (ST_Plus1)
- P t1 t2 ==> P t1' t2
-
- t2 ==> t2'
- --------------------------- (ST_Plus2)
- P (C n1) t2 ==> P (C n1) t2'
-*)
-
-
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_PlusConstConst : forall n1 n2,
- P (C n1) (C n2) ==> C (n1 + n2)
- | ST_Plus1 : forall t1 t1' t2,
- t1 ==> t1' ->
- P t1 t2 ==> P t1' t2
- | ST_Plus2 : forall n1 t2 t2',
- t2 ==> t2' ->
- P (C n1) t2 ==> P (C n1) t2'
-
- where " t '==>' t' " := (step t t').
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_PlusConstConst"
- | Case_aux c "ST_Plus1" | Case_aux c "ST_Plus2" ].
-
-(** Things to notice:
-
- - We are defining just a single reduction step, in which
- one [P] node is replaced by its value.
-
- - Each step finds the _leftmost_ [P] node that is ready to
- go (both of its operands are constants) and rewrites it in
- place. The first rule tells how to rewrite this [P] node
- itself; the other two rules tell how to find it.
-
- - A term that is just a constant cannot take a step. *)
-
-
-(** Let's pause and check a couple of examples of reasoning with
- the [step] relation... *)
-
-(** If [t1] can take a step to [t1'], then [P t1 t2] steps
- to [P t1' t2]: *)
-
-Example test_step_1 :
- P
- (P (C 0) (C 3))
- (P (C 2) (C 4))
- ==>
- P
- (C (0 + 3))
- (P (C 2) (C 4)).
-Proof.
- apply ST_Plus1. apply ST_PlusConstConst. Qed.
-
-(** **** Exercise: 1 star (test_step_2) *)
-(** Right-hand sides of sums can take a step only when the
- left-hand side is finished: if [t2] can take a step to [t2'],
- then [P (C n) t2] steps to [P (C n)
- t2']: *)
-
-Example test_step_2 :
- P
- (C 0)
- (P
- (C 2)
- (P (C 0) (C 3)))
- ==>
- P
- (C 0)
- (P
- (C 2)
- (C (0 + 3))).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-
-(* ########################################################### *)
-(** * Relations *)
-
-(** We will be using several different step relations, so it is
- helpful to generalize a bit... *)
-
-(** A (binary) _relation_ on a set [X] is a family of propositions
- parameterized by two elements of [X] -- i.e., a proposition about
- pairs of elements of [X]. *)
-
-Definition relation (X: Type) := X->X->Prop.
-
-
-(** Our main examples of such relations in this chapter will be
- the single-step and multi-step reduction relations on terms, [==>]
- and [==>*], but there are many other examples -- some that come to
- mind are the "equals," "less than," "less than or equal to," and
- "is the square of" relations on numbers, and the "prefix of"
- relation on lists and strings. *)
-
-
-(** One simple property of the [==>] relation is that, like the
- evaluation relation for our language of Imp programs, it is
- _deterministic_.
-
- _Theorem_: For each [t], there is at most one [t'] such that [t]
- steps to [t'] ([t ==> t'] is provable). Formally, this is the
- same as saying that [==>] is deterministic. *)
-
-(** _Proof sketch_: We show that if [x] steps to both [y1] and [y2]
- then [y1] and [y2] are equal, by induction on a derivation of
- [step x y1]. There are several cases to consider, depending on
- the last rule used in this derivation and in the given derivation
- of [step x y2].
-
- - If both are [ST_PlusConstConst], the result is immediate.
-
- - The cases when both derivations end with [ST_Plus1] or
- [ST_Plus2] follow by the induction hypothesis.
-
- - It cannot happen that one is [ST_PlusConstConst] and the other
- is [ST_Plus1] or [ST_Plus2], since this would imply that [x] has
- the form [P t1 t2] where both [t1] and [t2] are
- constants (by [ST_PlusConstConst]) _and_ one of [t1] or [t2] has
- the form [P ...].
-
- - Similarly, it cannot happen that one is [ST_Plus1] and the other
- is [ST_Plus2], since this would imply that [x] has the form
- [P t1 t2] where [t1] has both the form [P t1 t2] and
- the form [C n]. [] *)
-
-Definition deterministic {X: Type} (R: relation X) :=
- forall x y1 y2 : X, R x y1 -> R x y2 -> y1 = y2.
-
-Theorem step_deterministic:
- deterministic step.
-Proof.
- unfold deterministic. intros x y1 y2 Hy1 Hy2.
- generalize dependent y2.
- step_cases (induction Hy1) Case; intros y2 Hy2.
- Case "ST_PlusConstConst". step_cases (inversion Hy2) SCase.
- SCase "ST_PlusConstConst". reflexivity.
- SCase "ST_Plus1". inversion H2.
- SCase "ST_Plus2". inversion H2.
- Case "ST_Plus1". step_cases (inversion Hy2) SCase.
- SCase "ST_PlusConstConst". rewrite <- H0 in Hy1. inversion Hy1.
- SCase "ST_Plus1".
- rewrite <- (IHHy1 t1'0).
- reflexivity. assumption.
- SCase "ST_Plus2". rewrite <- H in Hy1. inversion Hy1.
- Case "ST_Plus2". step_cases (inversion Hy2) SCase.
- SCase "ST_PlusConstConst". rewrite <- H1 in Hy1. inversion Hy1.
- SCase "ST_Plus1". inversion H2.
- SCase "ST_Plus2".
- rewrite <- (IHHy1 t2'0).
- reflexivity. assumption. Qed.
-
-End SimpleArith1.
-
-(* ########################################################### *)
-(** ** Values *)
-
-(** Let's take a moment to slightly generalize the way we state the
- definition of single-step reduction. *)
-
-(** It is useful to think of the [==>] relation as defining an
- _abstract machine_:
-
- - At any moment, the _state_ of the machine is a term.
-
- - A _step_ of the machine is an atomic unit of computation --
- here, a single "add" operation.
-
- - The _halting states_ of the machine are ones where there is no
- more computation to be done.
-*)
-(**
- We can then execute a term [t] as follows:
-
- - Take [t] as the starting state of the machine.
-
- - Repeatedly use the [==>] relation to find a sequence of
- machine states, starting with [t], where each state steps to
- the next.
-
- - When no more reduction is possible, "read out" the final state
- of the machine as the result of execution. *)
-
-(** Intuitively, it is clear that the final states of the
- machine are always terms of the form [C n] for some [n].
- We call such terms _values_. *)
-
-Inductive value : tm -> Prop :=
- v_const : forall n, value (C n).
-
-(** Having introduced the idea of values, we can use it in the
- definition of the [==>] relation to write [ST_Plus2] rule in a
- slightly more elegant way: *)
-
-(**
- ------------------------------- (ST_PlusConstConst)
- P (C n1) (C n2) ==> C (n1 + n2)
-
- t1 ==> t1'
- -------------------- (ST_Plus1)
- P t1 t2 ==> P t1' t2
-
- value v1
- t2 ==> t2'
- -------------------- (ST_Plus2)
- P v1 t2 ==> P v1 t2'
-*)
-(** Again, the variable names here carry important information:
- by convention, [v1] ranges only over values, while [t1] and [t2]
- range over arbitrary terms. (Given this convention, the explicit
- [value] hypothesis is arguably redundant. We'll keep it for now,
- to maintain a close correspondence between the informal and Coq
- versions of the rules, but later on we'll drop it in informal
- rules, for the sake of brevity.) *)
-
-(** Here are the formal rules: *)
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_PlusConstConst : forall n1 n2,
- P (C n1) (C n2)
- ==> C (n1 + n2)
- | ST_Plus1 : forall t1 t1' t2,
- t1 ==> t1' ->
- P t1 t2 ==> P t1' t2
- | ST_Plus2 : forall v1 t2 t2',
- value v1 -> (* <----- n.b. *)
- t2 ==> t2' ->
- P v1 t2 ==> P v1 t2'
-
- where " t '==>' t' " := (step t t').
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_PlusConstConst"
- | Case_aux c "ST_Plus1" | Case_aux c "ST_Plus2" ].
-
-(** **** Exercise: 3 stars (redo_determinism) *)
-(** As a sanity check on this change, let's re-verify determinism
-
- Proof sketch: We must show that if [x] steps to both [y1] and [y2]
- then [y1] and [y2] are equal. Consider the final rules used in
- the derivations of [step x y1] and [step x y2].
-
- - If both are [ST_PlusConstConst], the result is immediate.
-
- - It cannot happen that one is [ST_PlusConstConst] and the other
- is [ST_Plus1] or [ST_Plus2], since this would imply that [x] has
- the form [P t1 t2] where both [t1] and [t2] are
- constants (by [ST_PlusConstConst]) AND one of [t1] or [t2] has
- the form [P ...].
-
- - Similarly, it cannot happen that one is [ST_Plus1] and the other
- is [ST_Plus2], since this would imply that [x] has the form
- [P t1 t2] where [t1] both has the form [P t1 t2] and
- is a value (hence has the form [C n]).
-
- - The cases when both derivations end with [ST_Plus1] or
- [ST_Plus2] follow by the induction hypothesis. [] *)
-
-(** Most of this proof is the same as the one above. But to get
- maximum benefit from the exercise you should try to write it from
- scratch and just use the earlier one if you get stuck. *)
-
-Theorem step_deterministic :
- deterministic step.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ########################################################### *)
-(** ** Strong Progress and Normal Forms *)
-
-(** The definition of single-step reduction for our toy language is
- fairly simple, but for a larger language it would be pretty easy
- to forget one of the rules and create a situation where some term
- cannot take a step even though it has not been completely reduced
- to a value. The following theorem shows that we did not, in fact,
- make such a mistake here. *)
-
-(** _Theorem_ (_Strong Progress_): If [t] is a term, then either [t]
- is a value, or there exists a term [t'] such that [t ==> t']. *)
-
-(** _Proof_: By induction on [t].
-
- - Suppose [t = C n]. Then [t] is a [value].
-
- - Suppose [t = P t1 t2], where (by the IH) [t1] is either a
- value or can step to some [t1'], and where [t2] is either a
- value or can step to some [t2']. We must show [P t1 t2] is
- either a value or steps to some [t'].
-
- - If [t1] and [t2] are both values, then [t] can take a step, by
- [ST_PlusConstConst].
-
- - If [t1] is a value and [t2] can take a step, then so can [t],
- by [ST_Plus2].
-
- - If [t1] can take a step, then so can [t], by [ST_Plus1]. [] *)
-
-Theorem strong_progress : forall t,
- value t \/ (exists t', t ==> t').
-Proof.
- tm_cases (induction t) Case.
- Case "C". left. apply v_const.
- Case "P". right. inversion IHt1.
- SCase "l". inversion IHt2.
- SSCase "l". inversion H. inversion H0.
- exists (C (n + n0)).
- apply ST_PlusConstConst.
- SSCase "r". inversion H0 as [t' H1].
- exists (P t1 t').
- apply ST_Plus2. apply H. apply H1.
- SCase "r". inversion H as [t' H0].
- exists (P t' t2).
- apply ST_Plus1. apply H0. Qed.
-
-(** This important property is called _strong progress_, because
- every term either is a value or can "make progress" by stepping to
- some other term. (The qualifier "strong" distinguishes it from a
- more refined version that we'll see in later chapters, called
- simply "progress.") *)
-
-(** The idea of "making progress" can be extended to tell us something
- interesting about [value]s: in this language [value]s are exactly
- the terms that _cannot_ make progress in this sense.
-
- To state this observation formally, let's begin by giving a name
- to terms that cannot make progress. We'll call them _normal
- forms_. *)
-
-Definition normal_form {X:Type} (R:relation X) (t:X) : Prop :=
- ~ exists t', R t t'.
-
-(** This definition actually specifies what it is to be a normal form
- for an _arbitrary_ relation [R] over an arbitrary set [X], not
- just for the particular single-step reduction relation over terms
- that we are interested in at the moment. We'll re-use the same
- terminology for talking about other relations later in the
- course. *)
-
-(** We can use this terminology to generalize the observation we made
- in the strong progress theorem: in this language, normal forms and
- values are actually the same thing. *)
-
-Lemma value_is_nf : forall v,
- value v -> normal_form step v.
-Proof.
- unfold normal_form. intros v H. inversion H.
- intros contra. inversion contra. inversion H1.
-Qed.
-
-Lemma nf_is_value : forall t,
- normal_form step t -> value t.
-Proof. (* a corollary of [strong_progress]... *)
- unfold normal_form. intros t H.
- assert (G : value t \/ exists t', t ==> t').
- SCase "Proof of assertion". apply strong_progress.
- inversion G.
- SCase "l". apply H0.
- SCase "r". apply ex_falso_quodlibet. apply H. assumption. Qed.
-
-Corollary nf_same_as_value : forall t,
- normal_form step t <-> value t.
-Proof.
- split. apply nf_is_value. apply value_is_nf. Qed.
-
-(** Why is this interesting?
-
- Because [value] is a syntactic concept -- it is defined by looking
- at the form of a term -- while [normal_form] is a semantic one --
- it is defined by looking at how the term steps. It is not obvious
- that these concepts should coincide!
-
- Indeed, we could easily have written the definitions so that they
- would not coincide... *)
-
-(* ##################################################### *)
-
-(** We might, for example, mistakenly define [value] so that it
- includes some terms that are not finished reducing. *)
-
-Module Temp1.
-(* Open an inner module so we can redefine value and step. *)
-
-Inductive value : tm -> Prop :=
-| v_const : forall n, value (C n)
-| v_funny : forall t1 n2, (* <---- *)
- value (P t1 (C n2)).
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_PlusConstConst : forall n1 n2,
- P (C n1) (C n2) ==> C (n1 + n2)
- | ST_Plus1 : forall t1 t1' t2,
- t1 ==> t1' ->
- P t1 t2 ==> P t1' t2
- | ST_Plus2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- P v1 t2 ==> P v1 t2'
-
- where " t '==>' t' " := (step t t').
-
-
-
-(** **** Exercise: 3 stars, advanced (value_not_same_as_normal_form) *)
-Lemma value_not_same_as_normal_form :
- exists v, value v /\ ~ normal_form step v.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-End Temp1.
-
-(* ##################################################### *)
-(** Alternatively, we might mistakenly define [step] so that it
- permits something designated as a value to reduce further. *)
-
-Module Temp2.
-
-Inductive value : tm -> Prop :=
-| v_const : forall n, value (C n).
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_Funny : forall n, (* <---- *)
- C n ==> P (C n) (C 0)
- | ST_PlusConstConst : forall n1 n2,
- P (C n1) (C n2) ==> C (n1 + n2)
- | ST_Plus1 : forall t1 t1' t2,
- t1 ==> t1' ->
- P t1 t2 ==> P t1' t2
- | ST_Plus2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- P v1 t2 ==> P v1 t2'
-
- where " t '==>' t' " := (step t t').
-
-
-(** **** Exercise: 2 stars, advanced (value_not_same_as_normal_form) *)
-Lemma value_not_same_as_normal_form :
- exists v, value v /\ ~ normal_form step v.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** [] *)
-End Temp2.
-
-(* ########################################################### *)
-(** Finally, we might define [value] and [step] so that there is some
- term that is not a value but that cannot take a step in the [step]
- relation. Such terms are said to be _stuck_. In this case this is
- caused by a mistake in the semantics, but we will also see
- situations where, even in a correct language definition, it makes
- sense to allow some terms to be stuck. *)
-
-Module Temp3.
-
-Inductive value : tm -> Prop :=
- | v_const : forall n, value (C n).
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_PlusConstConst : forall n1 n2,
- P (C n1) (C n2) ==> C (n1 + n2)
- | ST_Plus1 : forall t1 t1' t2,
- t1 ==> t1' ->
- P t1 t2 ==> P t1' t2
-
- where " t '==>' t' " := (step t t').
-
-(** (Note that [ST_Plus2] is missing.) *)
-
-(** **** Exercise: 3 stars, advanced (value_not_same_as_normal_form') *)
-Lemma value_not_same_as_normal_form :
- exists t, ~ value t /\ normal_form step t.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-End Temp3.
-
-(* ########################################################### *)
-(** *** Additional Exercises *)
-
-Module Temp4.
-
-(** Here is another very simple language whose terms, instead of being
- just plus and numbers, are just the booleans true and false and a
- conditional expression... *)
-
-Inductive tm : Type :=
- | ttrue : tm
- | tfalse : tm
- | tif : tm -> tm -> tm -> tm.
-
-Inductive value : tm -> Prop :=
- | v_true : value ttrue
- | v_false : value tfalse.
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_IfTrue : forall t1 t2,
- tif ttrue t1 t2 ==> t1
- | ST_IfFalse : forall t1 t2,
- tif tfalse t1 t2 ==> t2
- | ST_If : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- tif t1 t2 t3 ==> tif t1' t2 t3
-
- where " t '==>' t' " := (step t t').
-
-(** **** Exercise: 1 star (smallstep_bools) *)
-(** Which of the following propositions are provable? (This is just a
- thought exercise, but for an extra challenge feel free to prove
- your answers in Coq.) *)
-
-Definition bool_step_prop1 :=
- tfalse ==> tfalse.
-
-(* FILL IN HERE *)
-
-Definition bool_step_prop2 :=
- tif
- ttrue
- (tif ttrue ttrue ttrue)
- (tif tfalse tfalse tfalse)
- ==>
- ttrue.
-
-(* FILL IN HERE *)
-
-Definition bool_step_prop3 :=
- tif
- (tif ttrue ttrue ttrue)
- (tif ttrue ttrue ttrue)
- tfalse
- ==>
- tif
- ttrue
- (tif ttrue ttrue ttrue)
- tfalse.
-
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (progress_bool) *)
-(** Just as we proved a progress theorem for plus expressions, we can
- do so for boolean expressions, as well. *)
-
-Theorem strong_progress : forall t,
- value t \/ (exists t', t ==> t').
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (step_deterministic) *)
-Theorem step_deterministic :
- deterministic step.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-Module Temp5.
-
-(** **** Exercise: 2 stars (smallstep_bool_shortcut) *)
-(** Suppose we want to add a "short circuit" to the step relation for
- boolean expressions, so that it can recognize when the [then] and
- [else] branches of a conditional are the same value (either
- [ttrue] or [tfalse]) and reduce the whole conditional to this
- value in a single step, even if the guard has not yet been reduced
- to a value. For example, we would like this proposition to be
- provable:
- tif
- (tif ttrue ttrue ttrue)
- tfalse
- tfalse
- ==>
- tfalse.
-*)
-
-(** Write an extra clause for the step relation that achieves this
- effect and prove [bool_step_prop4]. *)
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_IfTrue : forall t1 t2,
- tif ttrue t1 t2 ==> t1
- | ST_IfFalse : forall t1 t2,
- tif tfalse t1 t2 ==> t2
- | ST_If : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- tif t1 t2 t3 ==> tif t1' t2 t3
-(* FILL IN HERE *)
-
- where " t '==>' t' " := (step t t').
-(** [] *)
-
-Definition bool_step_prop4 :=
- tif
- (tif ttrue ttrue ttrue)
- tfalse
- tfalse
- ==>
- tfalse.
-
-Example bool_step_prop4_holds :
- bool_step_prop4.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional (properties_of_altered_step) *)
-(** It can be shown that the determinism and strong progress theorems
- for the step relation in the lecture notes also hold for the
- definition of step given above. After we add the clause
- [ST_ShortCircuit]...
-
- - Is the [step] relation still deterministic? Write yes or no and
- briefly (1 sentence) explain your answer.
-
- Optional: prove your answer correct in Coq.
-*)
-
-(* FILL IN HERE *)
-(**
- - Does a strong progress theorem hold? Write yes or no and
- briefly (1 sentence) explain your answer.
-
- Optional: prove your answer correct in Coq.
-*)
-
-(* FILL IN HERE *)
-(**
- - In general, is there any way we could cause strong progress to
- fail if we took away one or more constructors from the original
- step relation? Write yes or no and briefly (1 sentence) explain
- your answer.
-
-(* FILL IN HERE *)
-*)
-(** [] *)
-
-End Temp5.
-End Temp4.
-
-(* ########################################################### *)
-(** * Multi-Step Reduction *)
-
-(** Until now, we've been working with the _single-step reduction_
- relation [==>], which formalizes the individual steps of an
- _abstract machine_ for executing programs.
-
- We can also use this machine to reduce programs to completion --
- to find out what final result they yield. This can be formalized
- as follows:
-
- - First, we define a _multi-step reduction relation_ [==>*], which
- relates terms [t] and [t'] if [t] can reach [t'] by any number
- of single reduction steps (including zero steps!).
-
- - Then we define a "result" of a term [t] as a normal form that
- [t] can reach by multi-step reduction. *)
-
-(* ########################################################### *)
-
-(** Since we'll want to reuse the idea of multi-step reduction many
- times in this and future chapters, let's take a little extra
- trouble here and define it generically.
-
- Given a relation [R], we define a relation [multi R], called the
- _multi-step closure of [R]_ as follows: *)
-
-Inductive multi {X:Type} (R: relation X) : relation X :=
- | multi_refl : forall (x : X), multi R x x
- | multi_step : forall (x y z : X),
- R x y ->
- multi R y z ->
- multi R x z.
-
-(** The effect of this definition is that [multi R] relates two
- elements [x] and [y] if either
-
- - [x = y], or else
- - there is some sequence [z1], [z2], ..., [zn]
- such that
- R x z1
- R z1 z2
- ...
- R zn y.
-
- Thus, if [R] describes a single-step of computation, [z1],
- ... [zn] is the sequence of intermediate steps of computation
- between [x] and [y].
-*)
-
-Tactic Notation "multi_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "multi_refl" | Case_aux c "multi_step" ].
-
-(** We write [==>*] for the [multi step] relation -- i.e., the
- relation that relates two terms [t] and [t'] if we can get from
- [t] to [t'] using the [step] relation zero or more times. *)
-
-Definition multistep := multi step.
-Notation " t '==>*' t' " := (multistep t t') (at level 40).
-
-(** The relation [multi R] has several crucial properties.
-
- First, it is obviously _reflexive_ (that is, [forall x, multi R x
- x]). In the case of the [==>*] (i.e. [multi step]) relation, the
- intuition is that a term can execute to itself by taking zero
- steps of execution.
-
- Second, it contains [R] -- that is, single-step executions are a
- particular case of multi-step executions. (It is this fact that
- justifies the word "closure" in the term "multi-step closure of
- [R].") *)
-
-Theorem multi_R : forall (X:Type) (R:relation X) (x y : X),
- R x y -> (multi R) x y.
-Proof.
- intros X R x y H.
- apply multi_step with y. apply H. apply multi_refl. Qed.
-
-(** Third, [multi R] is _transitive_. *)
-
-Theorem multi_trans :
- forall (X:Type) (R: relation X) (x y z : X),
- multi R x y ->
- multi R y z ->
- multi R x z.
-Proof.
- intros X R x y z G H.
- multi_cases (induction G) Case.
- Case "multi_refl". assumption.
- Case "multi_step".
- apply multi_step with y. assumption.
- apply IHG. assumption. Qed.
-
-(** That is, if [t1==>*t2] and [t2==>*t3], then [t1==>*t3]. *)
-
-(* ########################################################### *)
-(** ** Examples *)
-
-Lemma test_multistep_1:
- P
- (P (C 0) (C 3))
- (P (C 2) (C 4))
- ==>*
- C ((0 + 3) + (2 + 4)).
-Proof.
- apply multi_step with
- (P
- (C (0 + 3))
- (P (C 2) (C 4))).
- apply ST_Plus1. apply ST_PlusConstConst.
- apply multi_step with
- (P
- (C (0 + 3))
- (C (2 + 4))).
- apply ST_Plus2. apply v_const.
- apply ST_PlusConstConst.
- apply multi_R.
- apply ST_PlusConstConst. Qed.
-
-(** Here's an alternate proof that uses [eapply] to avoid explicitly
- constructing all the intermediate terms. *)
-
-Lemma test_multistep_1':
- P
- (P (C 0) (C 3))
- (P (C 2) (C 4))
- ==>*
- C ((0 + 3) + (2 + 4)).
-Proof.
- eapply multi_step. apply ST_Plus1. apply ST_PlusConstConst.
- eapply multi_step. apply ST_Plus2. apply v_const.
- apply ST_PlusConstConst.
- eapply multi_step. apply ST_PlusConstConst.
- apply multi_refl. Qed.
-
-(** **** Exercise: 1 star, optional (test_multistep_2) *)
-Lemma test_multistep_2:
- C 3 ==>* C 3.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (test_multistep_3) *)
-Lemma test_multistep_3:
- P (C 0) (C 3)
- ==>*
- P (C 0) (C 3).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (test_multistep_4) *)
-Lemma test_multistep_4:
- P
- (C 0)
- (P
- (C 2)
- (P (C 0) (C 3)))
- ==>*
- P
- (C 0)
- (C (2 + (0 + 3))).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ########################################################### *)
-(** ** Normal Forms Again *)
-
-(** If [t] reduces to [t'] in zero or more steps and [t'] is a
- normal form, we say that "[t'] is a normal form of [t]." *)
-
-Definition step_normal_form := normal_form step.
-
-Definition normal_form_of (t t' : tm) :=
- (t ==>* t' /\ step_normal_form t').
-
-(** We have already seen that, for our language, single-step reduction is
- deterministic -- i.e., a given term can take a single step in
- at most one way. It follows from this that, if [t] can reach
- a normal form, then this normal form is unique. In other words, we
- can actually pronounce [normal_form t t'] as "[t'] is _the_
- normal form of [t]." *)
-
-(** **** Exercise: 3 stars, optional (normal_forms_unique) *)
-Theorem normal_forms_unique:
- deterministic normal_form_of.
-Proof.
- unfold deterministic. unfold normal_form_of. intros x y1 y2 P1 P2.
- inversion P1 as [P11 P12]; clear P1. inversion P2 as [P21 P22]; clear P2.
- generalize dependent y2.
- (* We recommend using this initial setup as-is! *)
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Indeed, something stronger is true for this language (though not
- for all languages): the reduction of _any_ term [t] will
- eventually reach a normal form -- i.e., [normal_form_of] is a
- _total_ function. Formally, we say the [step] relation is
- _normalizing_. *)
-
-Definition normalizing {X:Type} (R:relation X) :=
- forall t, exists t',
- (multi R) t t' /\ normal_form R t'.
-
-(** To prove that [step] is normalizing, we need a couple of lemmas.
-
- First, we observe that, if [t] reduces to [t'] in many steps, then
- the same sequence of reduction steps within [t] is also possible
- when [t] appears as the left-hand child of a [P] node, and
- similarly when [t] appears as the right-hand child of a [P]
- node whose left-hand child is a value. *)
-
-Lemma multistep_congr_1 : forall t1 t1' t2,
- t1 ==>* t1' ->
- P t1 t2 ==>* P t1' t2.
-Proof.
- intros t1 t1' t2 H. multi_cases (induction H) Case.
- Case "multi_refl". apply multi_refl.
- Case "multi_step". apply multi_step with (P y t2).
- apply ST_Plus1. apply H.
- apply IHmulti. Qed.
-
-(** **** Exercise: 2 stars (multistep_congr_2) *)
-Lemma multistep_congr_2 : forall t1 t2 t2',
- value t1 ->
- t2 ==>* t2' ->
- P t1 t2 ==>* P t1 t2'.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** _Theorem_: The [step] function is normalizing -- i.e., for every
- [t] there exists some [t'] such that [t] steps to [t'] and [t'] is
- a normal form.
-
- _Proof sketch_: By induction on terms. There are two cases to
- consider:
-
- - [t = C n] for some [n]. Here [t] doesn't take a step,
- and we have [t' = t]. We can derive the left-hand side by
- reflexivity and the right-hand side by observing (a) that values
- are normal forms (by [nf_same_as_value]) and (b) that [t] is a
- value (by [v_const]).
-
- - [t = P t1 t2] for some [t1] and [t2]. By the IH, [t1] and
- [t2] have normal forms [t1'] and [t2']. Recall that normal
- forms are values (by [nf_same_as_value]); we know that [t1' =
- C n1] and [t2' = C n2], for some [n1] and [n2].
- We can combine the [==>*] derivations for [t1] and [t2] to prove
- that [P t1 t2] reduces in many steps to [C (n1 + n2)].
-
- It is clear that our choice of [t' = C (n1 + n2)] is a
- value, which is in turn a normal form. [] *)
-
-Theorem step_normalizing :
- normalizing step.
-Proof.
- unfold normalizing.
- tm_cases (induction t) Case.
- Case "C".
- exists (C n).
- split.
- SCase "l". apply multi_refl.
- SCase "r".
- (* We can use [rewrite] with "iff" statements, not
- just equalities: *)
- rewrite nf_same_as_value. apply v_const.
- Case "P".
- inversion IHt1 as [t1' H1]; clear IHt1. inversion IHt2 as [t2' H2]; clear IHt2.
- inversion H1 as [H11 H12]; clear H1. inversion H2 as [H21 H22]; clear H2.
- rewrite nf_same_as_value in H12. rewrite nf_same_as_value in H22.
- inversion H12 as [n1]. inversion H22 as [n2].
- rewrite <- H in H11.
- rewrite <- H0 in H21.
- exists (C (n1 + n2)).
- split.
- SCase "l".
- apply multi_trans with (P (C n1) t2).
- apply multistep_congr_1. apply H11.
- apply multi_trans with
- (P (C n1) (C n2)).
- apply multistep_congr_2. apply v_const. apply H21.
- apply multi_R. apply ST_PlusConstConst.
- SCase "r".
- rewrite nf_same_as_value. apply v_const. Qed.
-
-(* ########################################################### *)
-(** ** Equivalence of Big-Step and Small-Step Reduction *)
-
-(** Having defined the operational semantics of our tiny programming
- language in two different styles, it makes sense to ask whether
- these definitions actually define the same thing! They do, though
- it takes a little work to show it. (The details are left as an
- exercise). *)
-
-(** **** Exercise: 3 stars (eval__multistep) *)
-Theorem eval__multistep : forall t n,
- t || n -> t ==>* C n.
-
-(** The key idea behind the proof comes from the following picture:
- P t1 t2 ==> (by ST_Plus1)
- P t1' t2 ==> (by ST_Plus1)
- P t1'' t2 ==> (by ST_Plus1)
- ...
- P (C n1) t2 ==> (by ST_Plus2)
- P (C n1) t2' ==> (by ST_Plus2)
- P (C n1) t2'' ==> (by ST_Plus2)
- ...
- P (C n1) (C n2) ==> (by ST_PlusConstConst)
- C (n1 + n2)
- That is, the multistep reduction of a term of the form [P t1 t2]
- proceeds in three phases:
- - First, we use [ST_Plus1] some number of times to reduce [t1]
- to a normal form, which must (by [nf_same_as_value]) be a
- term of the form [C n1] for some [n1].
- - Next, we use [ST_Plus2] some number of times to reduce [t2]
- to a normal form, which must again be a term of the form [C
- n2] for some [n2].
- - Finally, we use [ST_PlusConstConst] one time to reduce [P (C
- n1) (C n2)] to [C (n1 + n2)]. *)
-
-(** To formalize this intuition, you'll need to use the congruence
- lemmas from above (you might want to review them now, so that
- you'll be able to recognize when they are useful), plus some basic
- properties of [==>*]: that it is reflexive, transitive, and
- includes [==>]. *)
-
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (eval__multistep_inf) *)
-(** Write a detailed informal version of the proof of [eval__multistep].
-
-(* FILL IN HERE *)
-[]
-*)
-(** For the other direction, we need one lemma, which establishes a
- relation between single-step reduction and big-step evaluation. *)
-
-(** **** Exercise: 3 stars (step__eval) *)
-Lemma step__eval : forall t t' n,
- t ==> t' ->
- t' || n ->
- t || n.
-Proof.
- intros t t' n Hs. generalize dependent n.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** The fact that small-step reduction implies big-step is now
- straightforward to prove, once it is stated correctly.
-
- The proof proceeds by induction on the multi-step reduction
- sequence that is buried in the hypothesis [normal_form_of t t']. *)
-(** Make sure you understand the statement before you start to
- work on the proof. *)
-
-(** **** Exercise: 3 stars (multistep__eval) *)
-Theorem multistep__eval : forall t t',
- normal_form_of t t' -> exists n, t' = C n /\ t || n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ########################################################### *)
-(** ** Additional Exercises *)
-
-(** **** Exercise: 3 stars, optional (interp_tm) *)
-(** Remember that we also defined big-step evaluation of [tm]s as a
- function [evalF]. Prove that it is equivalent to the existing
- semantics.
-
- Hint: we just proved that [eval] and [multistep] are
- equivalent, so logically it doesn't matter which you choose.
- One will be easier than the other, though! *)
-
-Theorem evalF_eval : forall t n,
- evalF t = n <-> t || n.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 4 stars (combined_properties) *)
-(** We've considered the arithmetic and conditional expressions
- separately. This exercise explores how the two interact. *)
-
-Module Combined.
-
-Inductive tm : Type :=
- | C : nat -> tm
- | P : tm -> tm -> tm
- | ttrue : tm
- | tfalse : tm
- | tif : tm -> tm -> tm -> tm.
-
-Tactic Notation "tm_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "C" | Case_aux c "P"
- | Case_aux c "ttrue" | Case_aux c "tfalse" | Case_aux c "tif" ].
-
-Inductive value : tm -> Prop :=
- | v_const : forall n, value (C n)
- | v_true : value ttrue
- | v_false : value tfalse.
-
-Reserved Notation " t '==>' t' " (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_PlusConstConst : forall n1 n2,
- P (C n1) (C n2) ==> C (n1 + n2)
- | ST_Plus1 : forall t1 t1' t2,
- t1 ==> t1' ->
- P t1 t2 ==> P t1' t2
- | ST_Plus2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- P v1 t2 ==> P v1 t2'
- | ST_IfTrue : forall t1 t2,
- tif ttrue t1 t2 ==> t1
- | ST_IfFalse : forall t1 t2,
- tif tfalse t1 t2 ==> t2
- | ST_If : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- tif t1 t2 t3 ==> tif t1' t2 t3
-
- where " t '==>' t' " := (step t t').
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_PlusConstConst"
- | Case_aux c "ST_Plus1" | Case_aux c "ST_Plus2"
- | Case_aux c "ST_IfTrue" | Case_aux c "ST_IfFalse" | Case_aux c "ST_If" ].
-
-(** Earlier, we separately proved for both plus- and if-expressions...
-
- - that the step relation was deterministic, and
-
- - a strong progress lemma, stating that every term is either a
- value or can take a step.
-
- Prove or disprove these two properties for the combined language. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-End Combined.
-
-
-(* ########################################################### *)
-(** * Small-Step Imp *)
-
-(** For a more serious example, here is the small-step version of the
- Imp operational semantics. *)
-
-(** The small-step evaluation relations for arithmetic and boolean
- expressions are straightforward extensions of the tiny language
- we've been working up to now. To make them easier to read, we
- introduce the symbolic notations [==>a] and [==>b], respectively,
- for the arithmetic and boolean step relations. *)
-
-Inductive aval : aexp -> Prop :=
- av_num : forall n, aval (ANum n).
-
-(** We are not actually going to bother to define boolean
- values, since they aren't needed in the definition of [==>b]
- below (why?), though they might be if our language were a bit
- larger (why?). *)
-
-Reserved Notation " t '/' st '==>a' t' " (at level 40, st at level 39).
-
-Inductive astep : state -> aexp -> aexp -> Prop :=
- | AS_Id : forall st i,
- AId i / st ==>a ANum (st i)
- | AS_Plus : forall st n1 n2,
- APlus (ANum n1) (ANum n2) / st ==>a ANum (n1 + n2)
- | AS_Plus1 : forall st a1 a1' a2,
- a1 / st ==>a a1' ->
- (APlus a1 a2) / st ==>a (APlus a1' a2)
- | AS_Plus2 : forall st v1 a2 a2',
- aval v1 ->
- a2 / st ==>a a2' ->
- (APlus v1 a2) / st ==>a (APlus v1 a2')
- | AS_Minus : forall st n1 n2,
- (AMinus (ANum n1) (ANum n2)) / st ==>a (ANum (minus n1 n2))
- | AS_Minus1 : forall st a1 a1' a2,
- a1 / st ==>a a1' ->
- (AMinus a1 a2) / st ==>a (AMinus a1' a2)
- | AS_Minus2 : forall st v1 a2 a2',
- aval v1 ->
- a2 / st ==>a a2' ->
- (AMinus v1 a2) / st ==>a (AMinus v1 a2')
- | AS_Mult : forall st n1 n2,
- (AMult (ANum n1) (ANum n2)) / st ==>a (ANum (mult n1 n2))
- | AS_Mult1 : forall st a1 a1' a2,
- a1 / st ==>a a1' ->
- (AMult (a1) (a2)) / st ==>a (AMult (a1') (a2))
- | AS_Mult2 : forall st v1 a2 a2',
- aval v1 ->
- a2 / st ==>a a2' ->
- (AMult v1 a2) / st ==>a (AMult v1 a2')
-
- where " t '/' st '==>a' t' " := (astep st t t').
-
- Reserved Notation " t '/' st '==>b' t' " (at level 40, st at level 39).
-
- Inductive bstep : state -> bexp -> bexp -> Prop :=
- | BS_Eq : forall st n1 n2,
- (BEq (ANum n1) (ANum n2)) / st ==>b
- (if (beq_nat n1 n2) then BTrue else BFalse)
- | BS_Eq1 : forall st a1 a1' a2,
- a1 / st ==>a a1' ->
- (BEq a1 a2) / st ==>b (BEq a1' a2)
- | BS_Eq2 : forall st v1 a2 a2',
- aval v1 ->
- a2 / st ==>a a2' ->
- (BEq v1 a2) / st ==>b (BEq v1 a2')
- | BS_LtEq : forall st n1 n2,
- (BLe (ANum n1) (ANum n2)) / st ==>b
- (if (ble_nat n1 n2) then BTrue else BFalse)
- | BS_LtEq1 : forall st a1 a1' a2,
- a1 / st ==>a a1' ->
- (BLe a1 a2) / st ==>b (BLe a1' a2)
- | BS_LtEq2 : forall st v1 a2 a2',
- aval v1 ->
- a2 / st ==>a a2' ->
- (BLe v1 a2) / st ==>b (BLe v1 (a2'))
- | BS_NotTrue : forall st,
- (BNot BTrue) / st ==>b BFalse
- | BS_NotFalse : forall st,
- (BNot BFalse) / st ==>b BTrue
- | BS_NotStep : forall st b1 b1',
- b1 / st ==>b b1' ->
- (BNot b1) / st ==>b (BNot b1')
- | BS_AndTrueTrue : forall st,
- (BAnd BTrue BTrue) / st ==>b BTrue
- | BS_AndTrueFalse : forall st,
- (BAnd BTrue BFalse) / st ==>b BFalse
- | BS_AndFalse : forall st b2,
- (BAnd BFalse b2) / st ==>b BFalse
- | BS_AndTrueStep : forall st b2 b2',
- b2 / st ==>b b2' ->
- (BAnd BTrue b2) / st ==>b (BAnd BTrue b2')
- | BS_AndStep : forall st b1 b1' b2,
- b1 / st ==>b b1' ->
- (BAnd b1 b2) / st ==>b (BAnd b1' b2)
-
- where " t '/' st '==>b' t' " := (bstep st t t').
-
-(** The semantics of commands is the interesting part. We need two
- small tricks to make it work:
-
- - We use [SKIP] as a "command value" -- i.e., a command that
- has reached a normal form.
-
- - An assignment command reduces to [SKIP] (and an updated
- state).
-
- - The sequencing command waits until its left-hand
- subcommand has reduced to [SKIP], then throws it away so
- that reduction can continue with the right-hand
- subcommand.
-
- - We reduce a [WHILE] command by transforming it into a
- conditional followed by the same [WHILE]. *)
-
-(** (There are other ways of achieving the effect of the latter
- trick, but they all share the feature that the original [WHILE]
- command needs to be saved somewhere while a single copy of the loop
- body is being evaluated.) *)
-
-Reserved Notation " t '/' st '==>' t' '/' st' "
- (at level 40, st at level 39, t' at level 39).
-
-Inductive cstep : (com * state) -> (com * state) -> Prop :=
- | CS_AssStep : forall st i a a',
- a / st ==>a a' ->
- (i ::= a) / st ==> (i ::= a') / st
- | CS_Ass : forall st i n,
- (i ::= (ANum n)) / st ==> SKIP / (update st i n)
- | CS_SeqStep : forall st c1 c1' st' c2,
- c1 / st ==> c1' / st' ->
- (c1 ;; c2) / st ==> (c1' ;; c2) / st'
- | CS_SeqFinish : forall st c2,
- (SKIP ;; c2) / st ==> c2 / st
- | CS_IfTrue : forall st c1 c2,
- IFB BTrue THEN c1 ELSE c2 FI / st ==> c1 / st
- | CS_IfFalse : forall st c1 c2,
- IFB BFalse THEN c1 ELSE c2 FI / st ==> c2 / st
- | CS_IfStep : forall st b b' c1 c2,
- b / st ==>b b' ->
- IFB b THEN c1 ELSE c2 FI / st ==> (IFB b' THEN c1 ELSE c2 FI) / st
- | CS_While : forall st b c1,
- (WHILE b DO c1 END) / st
- ==> (IFB b THEN (c1;; (WHILE b DO c1 END)) ELSE SKIP FI) / st
-
- where " t '/' st '==>' t' '/' st' " := (cstep (t,st) (t',st')).
-
-
-(* ########################################################### *)
-(** * Concurrent Imp *)
-
-(** Finally, to show the power of this definitional style, let's
- enrich Imp with a new form of command that runs two subcommands in
- parallel and terminates when both have terminated. To reflect the
- unpredictability of scheduling, the actions of the subcommands may
- be interleaved in any order, but they share the same memory and
- can communicate by reading and writing the same variables. *)
-
-Module CImp.
-
-Inductive com : Type :=
- | CSkip : com
- | CAss : id -> aexp -> com
- | CSeq : com -> com -> com
- | CIf : bexp -> com -> com -> com
- | CWhile : bexp -> com -> com
- (* New: *)
- | CPar : com -> com -> com.
-
-Tactic Notation "com_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "SKIP" | Case_aux c "::=" | Case_aux c ";"
- | Case_aux c "IFB" | Case_aux c "WHILE" | Case_aux c "PAR" ].
-
-Notation "'SKIP'" :=
- CSkip.
-Notation "x '::=' a" :=
- (CAss x a) (at level 60).
-Notation "c1 ;; c2" :=
- (CSeq c1 c2) (at level 80, right associativity).
-Notation "'WHILE' b 'DO' c 'END'" :=
- (CWhile b c) (at level 80, right associativity).
-Notation "'IFB' b 'THEN' c1 'ELSE' c2 'FI'" :=
- (CIf b c1 c2) (at level 80, right associativity).
-Notation "'PAR' c1 'WITH' c2 'END'" :=
- (CPar c1 c2) (at level 80, right associativity).
-
-Inductive cstep : (com * state) -> (com * state) -> Prop :=
- (* Old part *)
- | CS_AssStep : forall st i a a',
- a / st ==>a a' ->
- (i ::= a) / st ==> (i ::= a') / st
- | CS_Ass : forall st i n,
- (i ::= (ANum n)) / st ==> SKIP / (update st i n)
- | CS_SeqStep : forall st c1 c1' st' c2,
- c1 / st ==> c1' / st' ->
- (c1 ;; c2) / st ==> (c1' ;; c2) / st'
- | CS_SeqFinish : forall st c2,
- (SKIP ;; c2) / st ==> c2 / st
- | CS_IfTrue : forall st c1 c2,
- (IFB BTrue THEN c1 ELSE c2 FI) / st ==> c1 / st
- | CS_IfFalse : forall st c1 c2,
- (IFB BFalse THEN c1 ELSE c2 FI) / st ==> c2 / st
- | CS_IfStep : forall st b b' c1 c2,
- b /st ==>b b' ->
- (IFB b THEN c1 ELSE c2 FI) / st ==> (IFB b' THEN c1 ELSE c2 FI) / st
- | CS_While : forall st b c1,
- (WHILE b DO c1 END) / st ==>
- (IFB b THEN (c1;; (WHILE b DO c1 END)) ELSE SKIP FI) / st
- (* New part: *)
- | CS_Par1 : forall st c1 c1' c2 st',
- c1 / st ==> c1' / st' ->
- (PAR c1 WITH c2 END) / st ==> (PAR c1' WITH c2 END) / st'
- | CS_Par2 : forall st c1 c2 c2' st',
- c2 / st ==> c2' / st' ->
- (PAR c1 WITH c2 END) / st ==> (PAR c1 WITH c2' END) / st'
- | CS_ParDone : forall st,
- (PAR SKIP WITH SKIP END) / st ==> SKIP / st
- where " t '/' st '==>' t' '/' st' " := (cstep (t,st) (t',st')).
-
-
-Definition cmultistep := multi cstep.
-
-Notation " t '/' st '==>*' t' '/' st' " :=
- (multi cstep (t,st) (t',st'))
- (at level 40, st at level 39, t' at level 39).
-
-
-
-
-
-(** Among the many interesting properties of this language is the fact
- that the following program can terminate with the variable [X] set
- to any value... *)
-
-Definition par_loop : com :=
- PAR
- Y ::= ANum 1
- WITH
- WHILE BEq (AId Y) (ANum 0) DO
- X ::= APlus (AId X) (ANum 1)
- END
- END.
-
-(** In particular, it can terminate with [X] set to [0]: *)
-
-Example par_loop_example_0:
- exists st',
- par_loop / empty_state ==>* SKIP / st'
- /\ st' X = 0.
-Proof.
- eapply ex_intro. split.
- unfold par_loop.
- eapply multi_step. apply CS_Par1.
- apply CS_Ass.
- eapply multi_step. apply CS_Par2. apply CS_While.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq1. apply AS_Id.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq. simpl.
- eapply multi_step. apply CS_Par2. apply CS_IfFalse.
- eapply multi_step. apply CS_ParDone.
- eapply multi_refl.
- reflexivity. Qed.
-
-(** It can also terminate with [X] set to [2]: *)
-
-Example par_loop_example_2:
- exists st',
- par_loop / empty_state ==>* SKIP / st'
- /\ st' X = 2.
-Proof.
- eapply ex_intro. split.
- eapply multi_step. apply CS_Par2. apply CS_While.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq1. apply AS_Id.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq. simpl.
- eapply multi_step. apply CS_Par2. apply CS_IfTrue.
- eapply multi_step. apply CS_Par2. apply CS_SeqStep.
- apply CS_AssStep. apply AS_Plus1. apply AS_Id.
- eapply multi_step. apply CS_Par2. apply CS_SeqStep.
- apply CS_AssStep. apply AS_Plus.
- eapply multi_step. apply CS_Par2. apply CS_SeqStep.
- apply CS_Ass.
- eapply multi_step. apply CS_Par2. apply CS_SeqFinish.
-
- eapply multi_step. apply CS_Par2. apply CS_While.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq1. apply AS_Id.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq. simpl.
- eapply multi_step. apply CS_Par2. apply CS_IfTrue.
- eapply multi_step. apply CS_Par2. apply CS_SeqStep.
- apply CS_AssStep. apply AS_Plus1. apply AS_Id.
- eapply multi_step. apply CS_Par2. apply CS_SeqStep.
- apply CS_AssStep. apply AS_Plus.
- eapply multi_step. apply CS_Par2. apply CS_SeqStep.
- apply CS_Ass.
-
- eapply multi_step. apply CS_Par1. apply CS_Ass.
- eapply multi_step. apply CS_Par2. apply CS_SeqFinish.
- eapply multi_step. apply CS_Par2. apply CS_While.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq1. apply AS_Id.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq. simpl.
- eapply multi_step. apply CS_Par2. apply CS_IfFalse.
- eapply multi_step. apply CS_ParDone.
- eapply multi_refl.
- reflexivity. Qed.
-
-(** More generally... *)
-
-(** **** Exercise: 3 stars, optional *)
-Lemma par_body_n__Sn : forall n st,
- st X = n /\ st Y = 0 ->
- par_loop / st ==>* par_loop / (update st X (S n)).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, optional *)
-Lemma par_body_n : forall n st,
- st X = 0 /\ st Y = 0 ->
- exists st',
- par_loop / st ==>* par_loop / st' /\ st' X = n /\ st' Y = 0.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** ... the above loop can exit with [X] having any value
- whatsoever. *)
-
-Theorem par_loop_any_X:
- forall n, exists st',
- par_loop / empty_state ==>* SKIP / st'
- /\ st' X = n.
-Proof.
- intros n.
- destruct (par_body_n n empty_state).
- split; unfold update; reflexivity.
-
- rename x into st.
- inversion H as [H' [HX HY]]; clear H.
- exists (update st Y 1). split.
- eapply multi_trans with (par_loop,st). apply H'.
- eapply multi_step. apply CS_Par1. apply CS_Ass.
- eapply multi_step. apply CS_Par2. apply CS_While.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq1. apply AS_Id. rewrite update_eq.
- eapply multi_step. apply CS_Par2. apply CS_IfStep.
- apply BS_Eq. simpl.
- eapply multi_step. apply CS_Par2. apply CS_IfFalse.
- eapply multi_step. apply CS_ParDone.
- apply multi_refl.
-
- rewrite update_neq. assumption. intro X; inversion X.
-Qed.
-
-End CImp.
-
-(* ########################################################### *)
-(** * A Small-Step Stack Machine *)
-
-(** Last example: a small-step semantics for the stack machine example
- from Imp.v. *)
-
-Definition stack := list nat.
-Definition prog := list sinstr.
-
-Inductive stack_step : state -> prog * stack -> prog * stack -> Prop :=
- | SS_Push : forall st stk n p',
- stack_step st (SPush n :: p', stk) (p', n :: stk)
- | SS_Load : forall st stk i p',
- stack_step st (SLoad i :: p', stk) (p', st i :: stk)
- | SS_Plus : forall st stk n m p',
- stack_step st (SPlus :: p', n::m::stk) (p', (m+n)::stk)
- | SS_Minus : forall st stk n m p',
- stack_step st (SMinus :: p', n::m::stk) (p', (m-n)::stk)
- | SS_Mult : forall st stk n m p',
- stack_step st (SMult :: p', n::m::stk) (p', (m*n)::stk).
-
-Theorem stack_step_deterministic : forall st,
- deterministic (stack_step st).
-Proof.
- unfold deterministic. intros st x y1 y2 H1 H2.
- induction H1; inversion H2; reflexivity.
-Qed.
-
-Definition stack_multistep st := multi (stack_step st).
-
-(** **** Exercise: 3 stars, advanced (compiler_is_correct) *)
-(** Remember the definition of [compile] for [aexp] given in the
- [Imp] chapter. We want now to prove [compile] correct with respect
- to the stack machine.
-
- State what it means for the compiler to be correct according to
- the stack machine small step semantics and then prove it. *)
-
-Definition compiler_is_correct_statement : Prop :=
-(* FILL IN HERE *) admit.
-
-
-Theorem compiler_is_correct : compiler_is_correct_statement.
-Proof.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* $Date: 2014-04-02 10:55:30 -0400 (Wed, 02 Apr 2014) $ *)
-
-
diff --git a/Stlc.html b/Stlc.html
deleted file mode 100644
index 7397e5a..0000000
--- a/Stlc.html
+++ /dev/null
@@ -1,1595 +0,0 @@
-
-
-
-
-
-Stlc: The Simply Typed Lambda-Calculus
-
-
-
-
-
-
-
-
-
-
-
-
-
-
StlcThe Simply Typed Lambda-Calculus
-
-
-
-
-
-
-
-
-
-
-RequireExportTypes.
-
-
-
-
-
-
The Simply Typed Lambda-Calculus
-
-
-
- The simply typed lambda-calculus (STLC) is a tiny core calculus
- embodying the key concept of functional abstraction, which shows
- up in pretty much every real-world programming language in some
- form (functions, procedures, methods, etc.).
-
-
-
- We will follow exactly the same pattern as in the previous
- chapter when formalizing this calculus (syntax, small-step
- semantics, typing rules) and its main properties (progress and
- preservation). The new technical challenges (which will take some
- work to deal with) all arise from the mechanisms of variable
- binding and substitution.
-
-
-
-
-
-
-
-
Overview
-
-
-
- The STLC is built on some collection of base types — booleans,
- numbers, strings, etc. The exact choice of base types doesn't
- matter — the construction of the language and its theoretical
- properties work out pretty much the same — so for the sake of
- brevity let's take just Bool for the moment. At the end of the
- chapter we'll see how to add more base types, and in later
- chapters we'll enrich the pure STLC with other useful constructs
- like pairs, records, subtyping, and mutable state.
-
-
-
- Starting from the booleans, we add three things:
-
-
-
-
-
variables
-
-
-
function abstractions
-
-
-
application
-
-
-
-
-
-
- This gives us the following collection of abstract syntax
- constructors (written out here in informal BNF notation — we'll
- formalize it below).
-
-
-
- The \ symbol (backslash, in ascii) in a function abstraction
- \x:T1.t2 is generally written as a greek letter "lambda" (hence
- the name of the calculus). The variable x is called the
- parameter to the function; the term t1 is its body. The
- annotation :T specifies the type of arguments that the function
- can be applied to.
-
-
- Some examples:
-
-
-
-
-
\x:Bool.x
-
-
-
- The identity function for booleans.
-
-
-
-
-
-
(\x:Bool.x)true
-
-
-
- The identity function for booleans, applied to the boolean true.
-
-
-
-
-
-
\x:Bool.ifxthenfalseelsetrue
-
-
-
- The boolean "not" function.
-
-
-
-
-
-
\x:Bool.true
-
-
-
- The constant function that takes every (boolean) argument to
- true.
-
-
-
-
-
-
-
\x:Bool.\y:Bool.x
-
-
-
- A two-argument function that takes two booleans and returns
- the first one. (Note that, as in Coq, a two-argument function
- is really a one-argument function whose body is also a
- one-argument function.)
-
-
-
-
-
-
(\x:Bool.\y:Bool.x)falsetrue
-
-
-
- A two-argument function that takes two booleans and returns
- the first one, applied to the booleans false and true.
-
-
-
- Note that, as in Coq, application associates to the left —
- i.e., this expression is parsed as ((\x:Bool.\y:Bool.x)
- false)true.
-
-
-
-
-
-
\f:Bool→Bool.f(ftrue)
-
-
-
- A higher-order function that takes a functionf (from
- booleans to booleans) as an argument, applies f to true,
- and applies f again to the result.
-
-
-
-
-
-
(\f:Bool→Bool.f(ftrue))(\x:Bool.false)
-
-
-
- The same higher-order function, applied to the constantly
- false function.
-
-
-
-
-
- As the last several examples show, the STLC is a language of
- higher-order functions: we can write down functions that take
- other functions as arguments and/or return other functions as
- results.
-
-
-
- Another point to note is that the STLC doesn't provide any
- primitive syntax for defining named functions — all functions
- are "anonymous." We'll see in chapter MoreStlc that it is easy
- to add named functions to what we've got — indeed, the
- fundamental naming and binding mechanisms are exactly the same.
-
-
-
- The types of the STLC include Bool, which classifies the
- boolean constants true and false as well as more complex
- computations that yield booleans, plus arrow types that classify
- functions.
-
-
-
-
-
-
- T ::= Bool
- | T1→T2
-
-
-
- For example:
-
-
-
-
-
\x:Bool.false has type Bool→Bool
-
-
-
-
-
-
\x:Bool.x has type Bool→Bool
-
-
-
-
-
-
(\x:Bool.x)true has type Bool
-
-
-
-
-
-
\x:Bool.\y:Bool.x has type Bool→Bool→Bool (i.e. Bool→(Bool→Bool))
-
-
-
-
-
-
(\x:Bool.\y:Bool.x)false has type Bool→Bool
-
-
-
-
-
-
(\x:Bool.\y:Bool.x)falsetrue has type Bool
-
-
-
-
-
-
-
-
-
-
-
-
Syntax
-
-
-
-
-
-ModuleSTLC.
-
-
-
-
-
-
Types
-
-
-
-
-
-Inductivety : Type :=
- | TBool : ty
- | TArrow : ty→ty→ty.
-
-
-
-Note that an abstraction \x:T.t (formally, tabsxTt) is
- always annotated with the type T of its parameter, in contrast
- to Coq (and other functional languages like ML, Haskell, etc.),
- which use type inference to fill in missing annotations. We're
- not considering type inference here, to keep things simple.
-
-
- Some examples...
-
-
-
-
-Definitionx := (Id 0).
-Definitiony := (Id 1).
-Definitionz := (Id 2).
-
-(We write these as Notations rather than Definitions to make
- things easier for auto.)
-
-
-
-
-
-
-
-
Operational Semantics
-
-
-
- To define the small-step semantics of STLC terms, we begin — as
- always — by defining the set of values. Next, we define the
- critical notions of free variables and substitution, which are
- used in the reduction rule for application expressions. And
- finally we give the small-step relation itself.
-
-
-
-
-
-
-
-
Values
-
-
-
- To define the values of the STLC, we have a few cases to consider.
-
-
-
- First, for the boolean part of the language, the situation is
- clear: true and false are the only values. An if
- expression is never a value.
-
-
- Second, an application is clearly not a value: It represents a
- function being invoked on some argument, which clearly still has
- work left to do.
-
-
- Third, for abstractions, we have a choice:
-
-
-
-
-
We can say that \x:T.t1 is a value only when t1 is a
- value — i.e., only if the function's body has been
- reduced (as much as it can be without knowing what argument it
- is going to be applied to).
-
-
-
-
-
-
Or we can say that \x:T.t1 is always a value, no matter
- whether t1 is one or not — in other words, we can say that
- reduction stops at abstractions.
-
-
-
-
-
-
- Coq, in its built-in functional programming langauge, makes the
- first choice — for example,
-
-
-
-
- Evalsimplin (funx:bool ⇒ 3 + 4)
-
-
-
- yields funx:bool⇒7.
-
-
-
- Most real-world functional programming languages make the second
- choice — reduction of a function's body only begins when the
- function is actually applied to an argument. We also make the
- second choice here.
-
-Finally, we must consider what constitutes a complete program.
-
-
-
- Intuitively, a "complete" program must not refer to any undefined
- variables. We'll see shortly how to define the "free" variables
- in a STLC term. A program is "closed", that is, it contains no
- free variables.
-
-
-
-
-
-
- Having made the choice not to reduce under abstractions,
- we don't need to worry about whether variables are values, since
- we'll always be reducing programs "from the outside in," and that
- means the step relation will always be working with closed
- terms (ones with no free variables).
-
-
-
-
-
-
-
-
Substitution
-
-
-
- Now we come to the heart of the STLC: the operation of
- substituting one term for a variable in another term.
-
-
-
- This operation will be used below to define the operational
- semantics of function application, where we will need to
- substitute the argument term for the function parameter in the
- function's body. For example, we reduce
-
-
-
-
- (\x:Bool. ifxthentrueelsex) false
-
-
-
- to
-
-
-
-
- iffalsethentrueelsefalse
-
-
-
- by substituting false for the parameter x in the body of the
- function.
-
-
-
- In general, we need to be able to substitute some given
- term s for occurrences of some variable x in another term t.
- In informal discussions, this is usually written [x:=s]t and
- pronounced "substitute x with s in t."
-
-
- The last example is very important: substituting x with true in
- \x:Bool.x does not yield \x:Bool.true! The reason for
- this is that the x in the body of \x:Bool.x is bound by the
- abstraction: it is a new, local name that just happens to be
- spelled the same as some global name x.
-
-Technical note: Substitution becomes trickier to define if we
- consider the case where s, the term being substituted for a
- variable in some other term, may itself contain free variables.
- Since we are only interested here in defining the step relation
- on closed terms (i.e., terms like \x:Bool.x, that do not mention
- variables are not bound by some enclosing lambda), we can skip
- this extra complexity here, but it must be dealt with when
- formalizing richer languages.
-
-
-
-
Exercise: 3 stars (substi)
-
-
-
- The definition that we gave above uses Coq's Fixpoint facility
- to define substitution as a function. Suppose, instead, we
- wanted to define substitution as an inductive relationsubsti.
- We've begun the definition by providing the Inductive header and
- one of the constructors; your job is to fill in the rest of the
- constructors.
-
-
-
-
-Inductivesubsti (s:tm) (x:id) : tm→tm→Prop :=
- | s_var1 :
- substisx (tvarx) s
- (* FILL IN HERE *)
-.
-
-
-HintConstructorssubsti.
-
-
-Theoremsubsti_correct : ∀sxtt',
- [x:=s]t = t'↔substisxtt'.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Reduction
-
-
-
- The small-step reduction relation for STLC now follows the same
- pattern as the ones we have seen before. Intuitively, to reduce a
- function application, we first reduce its left-hand side until it
- becomes a literal function; then we reduce its right-hand
- side (the argument) until it is also a value; and finally we
- substitute the argument for the bound variable in the body of the
- function. This last rule, written informally as
-
-
-
-
- (\x:T.t12) v2⇒ [x:=v2]t12
-
-
-
- is traditionally called "beta-reduction".
-
-
-
-
-
value v2
-
- (ST_AppAbs)
-
-
-
-
-
-
(\x:T.t12) v2⇒ [x:=v2]t12
-
-
-
-
-
t1⇒ t1'
-
- (ST_App1)
-
-
-
-
-
-
t1 t2⇒ t1' t2
-
-
-
-
-
value v1
-
-
-
-
t2⇒ t2'
-
- (ST_App2)
-
-
-
-
-
-
v1 t2⇒ v1 t2'
-
-
-
... plus the usual rules for booleans:
-
-
-
-
- (ST_IfTrue)
-
-
-
-
-
-
(if true then t1 else t2) ⇒ t1
-
-
-
-
-
-
- (ST_IfFalse)
-
-
-
-
-
-
(if false then t1 else t2) ⇒ t2
-
-
-
-
-
t1⇒ t1'
-
- (ST_If)
-
-
-
-
-
-
(if t1 then t2 else t3) ⇒ (if t1' then t2 else t3)
- Try to do this one both with and without normalize.
-
-
-
-
-Lemmastep_example5 :
- (tapp (tappidBBBBidBB) idB)
- ⇒*idB.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Typing
-
-
-
-
-
-
-
-
-
Contexts
-
-
-
- Question: What is the type of the term "xy"?
-
-
-
- Answer: It depends on the types of x and y!
-
-
-
- I.e., in order to assign a type to a term, we need to know
- what assumptions we should make about the types of its free
- variables.
-
-
-
- This leads us to a three-place "typing judgment", informally
- written Γ⊢t∈T, where Γ is a
- "typing context" — a mapping from variables to their types.
-
-
- We hide the definition of partial maps in a module since it is
- actually defined in SfLib.
-
-Informally, we'll write Γ,x:T for "extend the partial
- function Γ to also map x to T." Formally, we use the
- function extend to add a binding to a partial map.
-
-
- We can read the three-place relation Γ⊢t∈T as:
- "to the term t we can assign the type T using as types for
- the free variables of t the ones specified in the context
- Γ."
-
- Formally prove the following typing derivation holds:
-
-
-
- empty⊢ \x:Bool→B. λy:Bool→Bool. λz:Bool.
- y (xz)
- ∈ T.
-
-
-
-
-
-
-
-
-Exampletyping_example_3 :
- ∃T,
- empty⊢
- (tabsx (TArrowTBoolTBool)
- (tabsy (TArrowTBoolTBool)
- (tabszTBool
- (tapp (tvary) (tapp (tvarx) (tvarz)))))) ∈
- T.
-Proofwithauto.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- We can also show that terms are not typable. For example, let's
- formally check that there is no typing derivation assigning a type
- to the term \x:Bool.\y:Bool,xy — i.e.,
-
-
-
-
- ¬ ∃T,
- empty⊢ \x:Bool. λy:Bool, xy : T.
-
-
-
-
-
-
-
-
-
-
-Exampletyping_nonexample_1 :
- ¬ ∃T,
- empty⊢
- (tabsxTBool
- (tabsyTBool
- (tapp (tvarx) (tvary)))) ∈
- T.
-Proof.
- introsHc. inversionHc.
- (* The clear tactic is useful here for tidying away bits of
- the context that we're not going to need again. *)
- inversionH. subst. clearH.
- inversionH5. subst. clearH5.
- inversionH4. subst. clearH4.
- inversionH2. subst. clearH2.
- inversionH5. subst. clearH5.
- (* rewrite extend_neq in H1. rewrite extend_eq in H1. *)
- inversionH1. Qed.
-
-
-
-
-
-
-
Exercise: 3 stars, optional (typing_nonexample_3)
- Another nonexample:
-
-
-
-
- ¬ (∃S, ∃T,
- empty⊢ \x:S. xx : T).
-
-
-
-
-
-
-
-
-Exampletyping_nonexample_3 :
- ¬ (∃S, ∃T,
- empty⊢
- (tabsxS
- (tapp (tvarx) (tvarx))) ∈
- T).
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
\ No newline at end of file
diff --git a/Stlc.v b/Stlc.v
deleted file mode 100644
index 5480053..0000000
--- a/Stlc.v
+++ /dev/null
@@ -1,816 +0,0 @@
-(** * Stlc: The Simply Typed Lambda-Calculus *)
-
-Require Export Types.
-
-(* ###################################################################### *)
-(** * The Simply Typed Lambda-Calculus *)
-
-(** The simply typed lambda-calculus (STLC) is a tiny core calculus
- embodying the key concept of _functional abstraction_, which shows
- up in pretty much every real-world programming language in some
- form (functions, procedures, methods, etc.).
-
- We will follow exactly the same pattern as in the previous
- chapter when formalizing this calculus (syntax, small-step
- semantics, typing rules) and its main properties (progress and
- preservation). The new technical challenges (which will take some
- work to deal with) all arise from the mechanisms of _variable
- binding_ and _substitution_. *)
-
-(* ###################################################################### *)
-(** ** Overview *)
-
-(** The STLC is built on some collection of _base types_ -- booleans,
- numbers, strings, etc. The exact choice of base types doesn't
- matter -- the construction of the language and its theoretical
- properties work out pretty much the same -- so for the sake of
- brevity let's take just [Bool] for the moment. At the end of the
- chapter we'll see how to add more base types, and in later
- chapters we'll enrich the pure STLC with other useful constructs
- like pairs, records, subtyping, and mutable state.
-
- Starting from the booleans, we add three things:
- - variables
- - function abstractions
- - application
-
- This gives us the following collection of abstract syntax
- constructors (written out here in informal BNF notation -- we'll
- formalize it below).
-
-*)
-
-(** Informal concrete syntax:
- t ::= x variable
- | \x:T1.t2 abstraction
- | t1 t2 application
- | true constant true
- | false constant false
- | if t1 then t2 else t3 conditional
-*)
-
-(** The [\] symbol (backslash, in ascii) in a function abstraction
- [\x:T1.t2] is generally written as a greek letter "lambda" (hence
- the name of the calculus). The variable [x] is called the
- _parameter_ to the function; the term [t1] is its _body_. The
- annotation [:T] specifies the type of arguments that the function
- can be applied to. *)
-
-(** Some examples:
-
- - [\x:Bool. x]
-
- The identity function for booleans.
-
- - [(\x:Bool. x) true]
-
- The identity function for booleans, applied to the boolean [true].
-
- - [\x:Bool. if x then false else true]
-
- The boolean "not" function.
-
- - [\x:Bool. true]
-
- The constant function that takes every (boolean) argument to
- [true]. *)
-(**
- - [\x:Bool. \y:Bool. x]
-
- A two-argument function that takes two booleans and returns
- the first one. (Note that, as in Coq, a two-argument function
- is really a one-argument function whose body is also a
- one-argument function.)
-
- - [(\x:Bool. \y:Bool. x) false true]
-
- A two-argument function that takes two booleans and returns
- the first one, applied to the booleans [false] and [true].
-
- Note that, as in Coq, application associates to the left --
- i.e., this expression is parsed as [((\x:Bool. \y:Bool. x)
- false) true].
-
- - [\f:Bool->Bool. f (f true)]
-
- A higher-order function that takes a _function_ [f] (from
- booleans to booleans) as an argument, applies [f] to [true],
- and applies [f] again to the result.
-
- - [(\f:Bool->Bool. f (f true)) (\x:Bool. false)]
-
- The same higher-order function, applied to the constantly
- [false] function. *)
-
-(** As the last several examples show, the STLC is a language of
- _higher-order_ functions: we can write down functions that take
- other functions as arguments and/or return other functions as
- results.
-
- Another point to note is that the STLC doesn't provide any
- primitive syntax for defining _named_ functions -- all functions
- are "anonymous." We'll see in chapter [MoreStlc] that it is easy
- to add named functions to what we've got -- indeed, the
- fundamental naming and binding mechanisms are exactly the same.
-
- The _types_ of the STLC include [Bool], which classifies the
- boolean constants [true] and [false] as well as more complex
- computations that yield booleans, plus _arrow types_ that classify
- functions. *)
-(**
- T ::= Bool
- | T1 -> T2
- For example:
-
- - [\x:Bool. false] has type [Bool->Bool]
-
- - [\x:Bool. x] has type [Bool->Bool]
-
- - [(\x:Bool. x) true] has type [Bool]
-
- - [\x:Bool. \y:Bool. x] has type [Bool->Bool->Bool] (i.e. [Bool -> (Bool->Bool)])
-
- - [(\x:Bool. \y:Bool. x) false] has type [Bool->Bool]
-
- - [(\x:Bool. \y:Bool. x) false true] has type [Bool]
-*)
-
-
-
-
-
-(* ###################################################################### *)
-(** ** Syntax *)
-
-Module STLC.
-
-(* ################################### *)
-(** *** Types *)
-
-Inductive ty : Type :=
- | TBool : ty
- | TArrow : ty -> ty -> ty.
-
-(* ################################### *)
-(** *** Terms *)
-
-Inductive tm : Type :=
- | tvar : id -> tm
- | tapp : tm -> tm -> tm
- | tabs : id -> ty -> tm -> tm
- | ttrue : tm
- | tfalse : tm
- | tif : tm -> tm -> tm -> tm.
-
-Tactic Notation "t_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "tvar" | Case_aux c "tapp"
- | Case_aux c "tabs" | Case_aux c "ttrue"
- | Case_aux c "tfalse" | Case_aux c "tif" ].
-
-(** Note that an abstraction [\x:T.t] (formally, [tabs x T t]) is
- always annotated with the type [T] of its parameter, in contrast
- to Coq (and other functional languages like ML, Haskell, etc.),
- which use _type inference_ to fill in missing annotations. We're
- not considering type inference here, to keep things simple. *)
-
-(** Some examples... *)
-
-Definition x := (Id 0).
-Definition y := (Id 1).
-Definition z := (Id 2).
-Hint Unfold x.
-Hint Unfold y.
-Hint Unfold z.
-
-(** [idB = \x:Bool. x] *)
-
-Notation idB :=
- (tabs x TBool (tvar x)).
-
-(** [idBB = \x:Bool->Bool. x] *)
-
-Notation idBB :=
- (tabs x (TArrow TBool TBool) (tvar x)).
-
-(** [idBBBB = \x:(Bool->Bool) -> (Bool->Bool). x] *)
-
-Notation idBBBB :=
- (tabs x (TArrow (TArrow TBool TBool)
- (TArrow TBool TBool))
- (tvar x)).
-
-(** [k = \x:Bool. \y:Bool. x] *)
-
-Notation k := (tabs x TBool (tabs y TBool (tvar x))).
-
-(** [notB = \x:Bool. if x then false else true] *)
-
-Notation notB := (tabs x TBool (tif (tvar x) tfalse ttrue)).
-
-
-(** (We write these as [Notation]s rather than [Definition]s to make
- things easier for [auto].) *)
-
-(* ###################################################################### *)
-(** ** Operational Semantics *)
-
-(** To define the small-step semantics of STLC terms, we begin -- as
- always -- by defining the set of values. Next, we define the
- critical notions of _free variables_ and _substitution_, which are
- used in the reduction rule for application expressions. And
- finally we give the small-step relation itself. *)
-
-(* ################################### *)
-(** *** Values *)
-
-(** To define the values of the STLC, we have a few cases to consider.
-
- First, for the boolean part of the language, the situation is
- clear: [true] and [false] are the only values. An [if]
- expression is never a value. *)
-
-(** Second, an application is clearly not a value: It represents a
- function being invoked on some argument, which clearly still has
- work left to do. *)
-
-(** Third, for abstractions, we have a choice:
-
- - We can say that [\x:T.t1] is a value only when [t1] is a
- value -- i.e., only if the function's body has been
- reduced (as much as it can be without knowing what argument it
- is going to be applied to).
-
- - Or we can say that [\x:T.t1] is always a value, no matter
- whether [t1] is one or not -- in other words, we can say that
- reduction stops at abstractions.
-
- Coq, in its built-in functional programming langauge, makes the
- first choice -- for example,
- Eval simpl in (fun x:bool => 3 + 4)
- yields [fun x:bool => 7].
-
- Most real-world functional programming languages make the second
- choice -- reduction of a function's body only begins when the
- function is actually applied to an argument. We also make the
- second choice here. *)
-
-Inductive value : tm -> Prop :=
- | v_abs : forall x T t,
- value (tabs x T t)
- | v_true :
- value ttrue
- | v_false :
- value tfalse.
-
-Hint Constructors value.
-
-
-(** Finally, we must consider what constitutes a _complete_ program.
-
- Intuitively, a "complete" program must not refer to any undefined
- variables. We'll see shortly how to define the "free" variables
- in a STLC term. A program is "closed", that is, it contains no
- free variables.
-
-*)
-
-(** Having made the choice not to reduce under abstractions,
- we don't need to worry about whether variables are values, since
- we'll always be reducing programs "from the outside in," and that
- means the [step] relation will always be working with closed
- terms (ones with no free variables). *)
-
-
-
-(* ###################################################################### *)
-(** *** Substitution *)
-
-(** Now we come to the heart of the STLC: the operation of
- substituting one term for a variable in another term.
-
- This operation will be used below to define the operational
- semantics of function application, where we will need to
- substitute the argument term for the function parameter in the
- function's body. For example, we reduce
- (\x:Bool. if x then true else x) false
- to
- if false then true else false
-]]
- by substituting [false] for the parameter [x] in the body of the
- function.
-
- In general, we need to be able to substitute some given
- term [s] for occurrences of some variable [x] in another term [t].
- In informal discussions, this is usually written [ [x:=s]t ] and
- pronounced "substitute [x] with [s] in [t]." *)
-
-(** Here are some examples:
-
- - [[x:=true] (if x then x else false)] yields [if true then true else false]
-
- - [[x:=true] x] yields [true]
-
- - [[x:=true] (if x then x else y)] yields [if true then true else y]
-
- - [[x:=true] y] yields [y]
-
- - [[x:=true] false] yields [false] (vacuous substitution)
-
- - [[x:=true] (\y:Bool. if y then x else false)] yields [\y:Bool. if y then true else false]
- - [[x:=true] (\y:Bool. x)] yields [\y:Bool. true]
-
- - [[x:=true] (\y:Bool. y)] yields [\y:Bool. y]
-
- - [[x:=true] (\x:Bool. x)] yields [\x:Bool. x]
-
- The last example is very important: substituting [x] with [true] in
- [\x:Bool. x] does _not_ yield [\x:Bool. true]! The reason for
- this is that the [x] in the body of [\x:Bool. x] is _bound_ by the
- abstraction: it is a new, local name that just happens to be
- spelled the same as some global name [x]. *)
-
-(** Here is the definition, informally...
- [x:=s]x = s
- [x:=s]y = y if x <> y
- [x:=s](\x:T11.t12) = \x:T11. t12
- [x:=s](\y:T11.t12) = \y:T11. [x:=s]t12 if x <> y
- [x:=s](t1 t2) = ([x:=s]t1) ([x:=s]t2)
- [x:=s]true = true
- [x:=s]false = false
- [x:=s](if t1 then t2 else t3) =
- if [x:=s]t1 then [x:=s]t2 else [x:=s]t3
-]]
-*)
-
-(** ... and formally: *)
-
-Reserved Notation "'[' x ':=' s ']' t" (at level 20).
-
-Fixpoint subst (x:id) (s:tm) (t:tm) : tm :=
- match t with
- | tvar x' =>
- if eq_id_dec x x' then s else t
- | tabs x' T t1 =>
- tabs x' T (if eq_id_dec x x' then t1 else ([x:=s] t1))
- | tapp t1 t2 =>
- tapp ([x:=s] t1) ([x:=s] t2)
- | ttrue =>
- ttrue
- | tfalse =>
- tfalse
- | tif t1 t2 t3 =>
- tif ([x:=s] t1) ([x:=s] t2) ([x:=s] t3)
- end
-
-where "'[' x ':=' s ']' t" := (subst x s t).
-
-(** _Technical note_: Substitution becomes trickier to define if we
- consider the case where [s], the term being substituted for a
- variable in some other term, may itself contain free variables.
- Since we are only interested here in defining the [step] relation
- on closed terms (i.e., terms like [\x:Bool. x], that do not mention
- variables are not bound by some enclosing lambda), we can skip
- this extra complexity here, but it must be dealt with when
- formalizing richer languages. *)
-
-(** *** *)
-(** **** Exercise: 3 stars (substi) *)
-
-(** The definition that we gave above uses Coq's [Fixpoint] facility
- to define substitution as a _function_. Suppose, instead, we
- wanted to define substitution as an inductive _relation_ [substi].
- We've begun the definition by providing the [Inductive] header and
- one of the constructors; your job is to fill in the rest of the
- constructors. *)
-
-Inductive substi (s:tm) (x:id) : tm -> tm -> Prop :=
- | s_var1 :
- substi s x (tvar x) s
- (* FILL IN HERE *)
-.
-
-Hint Constructors substi.
-
-Theorem substi_correct : forall s x t t',
- [x:=s]t = t' <-> substi s x t t'.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ################################### *)
-(** *** Reduction *)
-
-(** The small-step reduction relation for STLC now follows the same
- pattern as the ones we have seen before. Intuitively, to reduce a
- function application, we first reduce its left-hand side until it
- becomes a literal function; then we reduce its right-hand
- side (the argument) until it is also a value; and finally we
- substitute the argument for the bound variable in the body of the
- function. This last rule, written informally as
- (\x:T.t12) v2 ==> [x:=v2]t12
- is traditionally called "beta-reduction". *)
-
-(**
- value v2
- ---------------------------- (ST_AppAbs)
- (\x:T.t12) v2 ==> [x:=v2]t12
-
- t1 ==> t1'
- ---------------- (ST_App1)
- t1 t2 ==> t1' t2
-
- value v1
- t2 ==> t2'
- ---------------- (ST_App2)
- v1 t2 ==> v1 t2'
-*)
-(** ... plus the usual rules for booleans:
- -------------------------------- (ST_IfTrue)
- (if true then t1 else t2) ==> t1
-
- --------------------------------- (ST_IfFalse)
- (if false then t1 else t2) ==> t2
-
- t1 ==> t1'
- ---------------------------------------------------- (ST_If)
- (if t1 then t2 else t3) ==> (if t1' then t2 else t3)
-*)
-
-Reserved Notation "t1 '==>' t2" (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_AppAbs : forall x T t12 v2,
- value v2 ->
- (tapp (tabs x T t12) v2) ==> [x:=v2]t12
- | ST_App1 : forall t1 t1' t2,
- t1 ==> t1' ->
- tapp t1 t2 ==> tapp t1' t2
- | ST_App2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- tapp v1 t2 ==> tapp v1 t2'
- | ST_IfTrue : forall t1 t2,
- (tif ttrue t1 t2) ==> t1
- | ST_IfFalse : forall t1 t2,
- (tif tfalse t1 t2) ==> t2
- | ST_If : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- (tif t1 t2 t3) ==> (tif t1' t2 t3)
-
-where "t1 '==>' t2" := (step t1 t2).
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_AppAbs" | Case_aux c "ST_App1"
- | Case_aux c "ST_App2" | Case_aux c "ST_IfTrue"
- | Case_aux c "ST_IfFalse" | Case_aux c "ST_If" ].
-
-Hint Constructors step.
-
-Notation multistep := (multi step).
-Notation "t1 '==>*' t2" := (multistep t1 t2) (at level 40).
-
-(* ##################################### *)
-(** *** Examples *)
-
-(** Example:
- ((\x:Bool->Bool. x) (\x:Bool. x)) ==>* (\x:Bool. x)
-i.e.
- (idBB idB) ==>* idB
-*)
-
-Lemma step_example1 :
- (tapp idBB idB) ==>* idB.
-Proof.
- eapply multi_step.
- apply ST_AppAbs.
- apply v_abs.
- simpl.
- apply multi_refl. Qed.
-
-(** Example:
-((\x:Bool->Bool. x) ((\x:Bool->Bool. x) (\x:Bool. x)))
- ==>* (\x:Bool. x)
-i.e.
- (idBB (idBB idB)) ==>* idB.
-*)
-
-Lemma step_example2 :
- (tapp idBB (tapp idBB idB)) ==>* idB.
-Proof.
- eapply multi_step.
- apply ST_App2. auto.
- apply ST_AppAbs. auto.
- eapply multi_step.
- apply ST_AppAbs. simpl. auto.
- simpl. apply multi_refl. Qed.
-
-(** Example:
-((\x:Bool->Bool. x) (\x:Bool. if x then false
- else true)) true)
- ==>* false
-i.e.
- ((idBB notB) ttrue) ==>* tfalse.
-*)
-
-Lemma step_example3 :
- tapp (tapp idBB notB) ttrue ==>* tfalse.
-Proof.
- eapply multi_step.
- apply ST_App1. apply ST_AppAbs. auto. simpl.
- eapply multi_step.
- apply ST_AppAbs. auto. simpl.
- eapply multi_step.
- apply ST_IfTrue. apply multi_refl. Qed.
-
-(** Example:
-((\x:Bool->Bool. x) ((\x:Bool. if x then false
- else true) true))
- ==>* false
-i.e.
- (idBB (notB ttrue)) ==>* tfalse.
-*)
-
-Lemma step_example4 :
- tapp idBB (tapp notB ttrue) ==>* tfalse.
-Proof.
- eapply multi_step.
- apply ST_App2. auto.
- apply ST_AppAbs. auto. simpl.
- eapply multi_step.
- apply ST_App2. auto.
- apply ST_IfTrue.
- eapply multi_step.
- apply ST_AppAbs. auto. simpl.
- apply multi_refl. Qed.
-
-
-(** A more automatic proof *)
-
-Lemma step_example1' :
- (tapp idBB idB) ==>* idB.
-Proof. normalize. Qed.
-
-(** Again, we can use the [normalize] tactic from above to simplify
- the proof. *)
-
-Lemma step_example2' :
- (tapp idBB (tapp idBB idB)) ==>* idB.
-Proof.
- normalize.
-Qed.
-
-Lemma step_example3' :
- tapp (tapp idBB notB) ttrue ==>* tfalse.
-Proof. normalize. Qed.
-
-Lemma step_example4' :
- tapp idBB (tapp notB ttrue) ==>* tfalse.
-Proof. normalize. Qed.
-
-(** **** Exercise: 2 stars (step_example3) *)
-(** Try to do this one both with and without [normalize]. *)
-
-Lemma step_example5 :
- (tapp (tapp idBBBB idBB) idB)
- ==>* idB.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ###################################################################### *)
-(** ** Typing *)
-
-(* ################################### *)
-(** *** Contexts *)
-
-(** _Question_: What is the type of the term "[x y]"?
-
- _Answer_: It depends on the types of [x] and [y]!
-
- I.e., in order to assign a type to a term, we need to know
- what assumptions we should make about the types of its free
- variables.
-
- This leads us to a three-place "typing judgment", informally
- written [Gamma |- t \in T], where [Gamma] is a
- "typing context" -- a mapping from variables to their types. *)
-
-(** We hide the definition of partial maps in a module since it is
- actually defined in [SfLib]. *)
-
-Module PartialMap.
-
-Definition partial_map (A:Type) := id -> option A.
-
-Definition empty {A:Type} : partial_map A := (fun _ => None).
-
-(** Informally, we'll write [Gamma, x:T] for "extend the partial
- function [Gamma] to also map [x] to [T]." Formally, we use the
- function [extend] to add a binding to a partial map. *)
-
-Definition extend {A:Type} (Gamma : partial_map A) (x:id) (T : A) :=
- fun x' => if eq_id_dec x x' then Some T else Gamma x'.
-
-Lemma extend_eq : forall A (ctxt: partial_map A) x T,
- (extend ctxt x T) x = Some T.
-Proof.
- intros. unfold extend. rewrite eq_id. auto.
-Qed.
-
-Lemma extend_neq : forall A (ctxt: partial_map A) x1 T x2,
- x2 <> x1 ->
- (extend ctxt x2 T) x1 = ctxt x1.
-Proof.
- intros. unfold extend. rewrite neq_id; auto.
-Qed.
-
-End PartialMap.
-
-Definition context := partial_map ty.
-
-(* ################################### *)
-(** *** Typing Relation *)
-
-(**
- Gamma x = T
- -------------- (T_Var)
- Gamma |- x \in T
-
- Gamma , x:T11 |- t12 \in T12
- ---------------------------- (T_Abs)
- Gamma |- \x:T11.t12 \in T11->T12
-
- Gamma |- t1 \in T11->T12
- Gamma |- t2 \in T11
- ---------------------- (T_App)
- Gamma |- t1 t2 \in T12
-
- -------------------- (T_True)
- Gamma |- true \in Bool
-
- --------------------- (T_False)
- Gamma |- false \in Bool
-
- Gamma |- t1 \in Bool Gamma |- t2 \in T Gamma |- t3 \in T
- -------------------------------------------------------- (T_If)
- Gamma |- if t1 then t2 else t3 \in T
-
-
- We can read the three-place relation [Gamma |- t \in T] as:
- "to the term [t] we can assign the type [T] using as types for
- the free variables of [t] the ones specified in the context
- [Gamma]." *)
-
-Reserved Notation "Gamma '|-' t '\in' T" (at level 40).
-
-Inductive has_type : context -> tm -> ty -> Prop :=
- | T_Var : forall Gamma x T,
- Gamma x = Some T ->
- Gamma |- tvar x \in T
- | T_Abs : forall Gamma x T11 T12 t12,
- extend Gamma x T11 |- t12 \in T12 ->
- Gamma |- tabs x T11 t12 \in TArrow T11 T12
- | T_App : forall T11 T12 Gamma t1 t2,
- Gamma |- t1 \in TArrow T11 T12 ->
- Gamma |- t2 \in T11 ->
- Gamma |- tapp t1 t2 \in T12
- | T_True : forall Gamma,
- Gamma |- ttrue \in TBool
- | T_False : forall Gamma,
- Gamma |- tfalse \in TBool
- | T_If : forall t1 t2 t3 T Gamma,
- Gamma |- t1 \in TBool ->
- Gamma |- t2 \in T ->
- Gamma |- t3 \in T ->
- Gamma |- tif t1 t2 t3 \in T
-
-where "Gamma '|-' t '\in' T" := (has_type Gamma t T).
-
-Tactic Notation "has_type_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "T_Var" | Case_aux c "T_Abs"
- | Case_aux c "T_App" | Case_aux c "T_True"
- | Case_aux c "T_False" | Case_aux c "T_If" ].
-
-Hint Constructors has_type.
-
-(* ################################### *)
-(** *** Examples *)
-
-Example typing_example_1 :
- empty |- tabs x TBool (tvar x) \in TArrow TBool TBool.
-Proof.
- apply T_Abs. apply T_Var. reflexivity. Qed.
-
-(** Note that since we added the [has_type] constructors to the hints
- database, auto can actually solve this one immediately. *)
-
-Example typing_example_1' :
- empty |- tabs x TBool (tvar x) \in TArrow TBool TBool.
-Proof. auto. Qed.
-
-(** Another example:
- empty |- \x:A. \y:A->A. y (y x))
- \in A -> (A->A) -> A.
-*)
-
-Example typing_example_2 :
- empty |-
- (tabs x TBool
- (tabs y (TArrow TBool TBool)
- (tapp (tvar y) (tapp (tvar y) (tvar x))))) \in
- (TArrow TBool (TArrow (TArrow TBool TBool) TBool)).
-Proof with auto using extend_eq.
- apply T_Abs.
- apply T_Abs.
- eapply T_App. apply T_Var...
- eapply T_App. apply T_Var...
- apply T_Var...
-Qed.
-
-(** **** Exercise: 2 stars, optional (typing_example_2_full) *)
-(** Prove the same result without using [auto], [eauto], or
- [eapply]. *)
-
-Example typing_example_2_full :
- empty |-
- (tabs x TBool
- (tabs y (TArrow TBool TBool)
- (tapp (tvar y) (tapp (tvar y) (tvar x))))) \in
- (TArrow TBool (TArrow (TArrow TBool TBool) TBool)).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 2 stars (typing_example_3) *)
-(** Formally prove the following typing derivation holds: *)
-(**
- empty |- \x:Bool->B. \y:Bool->Bool. \z:Bool.
- y (x z)
- \in T.
-*)
-
-Example typing_example_3 :
- exists T,
- empty |-
- (tabs x (TArrow TBool TBool)
- (tabs y (TArrow TBool TBool)
- (tabs z TBool
- (tapp (tvar y) (tapp (tvar x) (tvar z)))))) \in
- T.
-Proof with auto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** We can also show that terms are _not_ typable. For example, let's
- formally check that there is no typing derivation assigning a type
- to the term [\x:Bool. \y:Bool, x y] -- i.e.,
- ~ exists T,
- empty |- \x:Bool. \y:Bool, x y : T.
-*)
-
-Example typing_nonexample_1 :
- ~ exists T,
- empty |-
- (tabs x TBool
- (tabs y TBool
- (tapp (tvar x) (tvar y)))) \in
- T.
-Proof.
- intros Hc. inversion Hc.
- (* The [clear] tactic is useful here for tidying away bits of
- the context that we're not going to need again. *)
- inversion H. subst. clear H.
- inversion H5. subst. clear H5.
- inversion H4. subst. clear H4.
- inversion H2. subst. clear H2.
- inversion H5. subst. clear H5.
- (* rewrite extend_neq in H1. rewrite extend_eq in H1. *)
- inversion H1. Qed.
-
-(** **** Exercise: 3 stars, optional (typing_nonexample_3) *)
-(** Another nonexample:
- ~ (exists S, exists T,
- empty |- \x:S. x x : T).
-*)
-
-Example typing_nonexample_3 :
- ~ (exists S, exists T,
- empty |-
- (tabs x S
- (tapp (tvar x) (tvar x))) \in
- T).
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-End STLC.
-
-(* $Date: 2013-11-20 13:03:49 -0500 (Wed, 20 Nov 2013) $ *)
-
diff --git a/StlcProp.html b/StlcProp.html
deleted file mode 100644
index 776db4f..0000000
--- a/StlcProp.html
+++ /dev/null
@@ -1,1487 +0,0 @@
-
-
-
-
-
-StlcProp: Properties of STLC
-
-
-
-
-
-
-
-
- As before, the progress theorem tells us that closed, well-typed
- terms are not stuck: either a well-typed term is a value, or it
- can take an evaluation step. The proof is a relatively
- straightforward extension of the progress proof we saw in the
- Types chapter.
-
-Proof: by induction on the derivation of ⊢t∈T.
-
-
-
-
-
The last rule of the derivation cannot be T_Var, since a
- variable is never well typed in an empty context.
-
-
-
-
-
-
The T_True, T_False, and T_Abs cases are trivial, since in
- each of these cases we know immediately that t is a value.
-
-
-
-
-
-
If the last rule of the derivation was T_App, then t=t1
- t2, and we know that t1 and t2 are also well typed in the
- empty context; in particular, there exists a type T2 such that
- ⊢t1∈T2→T and ⊢t2∈T2. By the induction
- hypothesis, either t1 is a value or it can take an evaluation
- step.
-
-
-
-
-
If t1 is a value, we now consider t2, which by the other
- induction hypothesis must also either be a value or take an
- evaluation step.
-
-
-
-
-
Suppose t2 is a value. Since t1 is a value with an
- arrow type, it must be a lambda abstraction; hence t1
- t2 can take a step by ST_AppAbs.
-
-
-
-
-
-
Otherwise, t2 can take a step, and hence so can t1
- t2 by ST_App2.
-
-
-
-
-
-
-
-
-
If t1 can take a step, then so can t1t2 by ST_App1.
-
-
-
-
-
-
-
-
-
If the last rule of the derivation was T_If, then t=ift1
- thent2elset3, where t1 has type Bool. By the IH, t1
- either is a value or takes a step.
-
-
-
-
-
If t1 is a value, then since it has type Bool it must be
- either true or false. If it is true, then t steps
- to t2; otherwise it steps to t3.
-
-
-
-
-
-
Otherwise, t1 takes a step, and therefore so does t (by
- ST_If).
-
-
-
-
-
-
-
-
-
-
-
-
-
-Proofwitheauto.
- introstTHt.
- remember (@emptyty) asΓ.
- has_type_cases (inductionHt) Case; substΓ...
- Case "T_Var".
- (* contradictory: variables cannot be typed in an
- empty context *)
- inversionH.
-
-
- Case "T_App".
- (* t = t1t2. Proceed by cases on whether t1 is a
- value or steps... *)
- right. destructIHHt1...
- SCase "t1 is a value".
- destructIHHt2...
- SSCase "t2 is also a value".
- assert (∃x0t0, t1 = tabsx0T11t0).
- eapplycanonical_forms_fun; eauto.
- destructH1as [x0 [t0Heq]]. subst.
- ∃([x0:=t2]t0)...
-
-
- SSCase "t2 steps".
- inversionH0as [t2'Hstp]. ∃(tappt1t2')...
-
-
- SCase "t1 steps".
- inversionHas [t1'Hstp]. ∃(tappt1't2)...
-
-
- Case "T_If".
- right. destructIHHt1...
-
-
- SCase "t1 is a value".
- destruct (canonical_forms_boolt1); subst; eauto.
-
-
- SCase "t1 also steps".
- inversionHas [t1'Hstp]. ∃(tift1't2t3)...
-Qed.
-
- Show that progress can also be proved by induction on terms
- instead of induction on typing derivations.
-
-
-
-
-Theoremprogress' : ∀tT,
- empty⊢t ∈ T→
- valuet∨∃t', t⇒t'.
-Proof.
- introst.
- t_cases (inductiont) Case; introsTHt; auto.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Preservation
-
-
-
- The other half of the type soundness property is the preservation
- of types during reduction. For this, we need to develop some
- technical machinery for reasoning about variables and
- substitution. Working from top to bottom (the high-level property
- we are actually interested in to the lowest-level technical lemmas
- that are needed by various cases of the more interesting proofs),
- the story goes like this:
-
-
-
-
-
The preservation theorem is proved by induction on a typing
- derivation, pretty much as we did in the Types chapter. The
- one case that is significantly different is the one for the
- ST_AppAbs rule, which is defined using the substitution
- operation. To see that this step preserves typing, we need to
- know that the substitution itself does. So we prove a...
-
-
-
-
-
-
substitution lemma, stating that substituting a (closed)
- term s for a variable x in a term t preserves the type
- of t. The proof goes by induction on the form of t and
- requires looking at all the different cases in the definition
- of substitition. This time, the tricky cases are the ones for
- variables and for function abstractions. In both cases, we
- discover that we need to take a term s that has been shown
- to be well-typed in some context Γ and consider the same
- term s in a slightly different context Γ'. For this
- we prove a...
-
-
-
-
-
-
context invariance lemma, showing that typing is preserved
- under "inessential changes" to the context Γ — in
- particular, changes that do not affect any of the free
- variables of the term. For this, we need a careful definition
- of
-
-
-
-
-
-
the free variables of a term — i.e., the variables occuring
- in the term that are not in the scope of a function
- abstraction that binds them.
-
-
-
-
-
-
-
-
-
-
-
-
Free Occurrences
-
-
-
- A variable xappears free in a term t if t contains some
- occurrence of x that is not under an abstraction labeled x. For example:
-
-
-
- We first need a technical lemma connecting free variables and
- typing contexts. If a variable x appears free in a term t,
- and if we know t is well typed in context Γ, then it must
- be the case that Γ assigns a type to x.
-
-Proof: We show, by induction on the proof that x appears free
- in t, that, for all contexts Γ, if t is well typed
- under Γ, then Γ assigns some type to x.
-
-
-
-
-
If the last rule used was afi_var, then t=x, and from
- the assumption that t is well typed under Γ we have
- immediately that Γ assigns a type to x.
-
-
-
-
-
-
If the last rule used was afi_app1, then t=t1t2 and x
- appears free in t1. Since t is well typed under Γ,
- we can see from the typing rules that t1 must also be, and
- the IH then tells us that Γ assigns x a type.
-
-
-
-
-
-
Almost all the other cases are similar: x appears free in a
- subterm of t, and since t is well typed under Γ, we
- know the subterm of t in which x appears is well typed
- under Γ as well, and the IH gives us exactly the
- conclusion we want.
-
-
-
-
-
-
The only remaining case is afi_abs. In this case t=
- \y:T11.t12, and x appears free in t12; we also know that
- x is different from y. The difference from the previous
- cases is that whereas t is well typed under Γ, its
- body t12 is well typed under (Γ,y:T11), so the IH
- allows us to conclude that x is assigned some type by the
- extended context (Γ,y:T11). To conclude that Γ
- assigns a type to x, we appeal to lemma extend_neq, noting
- that x and y are different variables.
-
-Corollarytypable_empty__closed : ∀tT,
- empty⊢t ∈ T→
- closedt.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
- Sometimes, when we have a proof Γ⊢t:T, we will need to
- replace Γ by a different context Γ'. When is it safe
- to do this? Intuitively, it must at least be the case that
- Γ' assigns the same types as Γ to all the variables
- that appear free in t. In fact, this is the only condition that
- is needed.
-
-Proof: By induction on the derivation of Γ⊢t∈T.
-
-
-
-
-
If the last rule in the derivation was T_Var, then t=x
- and Γx=T. By assumption, Γ'x=T as well, and
- hence Γ'⊢t∈T by T_Var.
-
-
-
-
-
-
If the last rule was T_Abs, then t=\y:T11.t12, with T
- =T11→T12 and Γ,y:T11⊢t12∈T12. The induction
- hypothesis is that for any context Γ'', if Γ,
- y:T11 and Γ'' assign the same types to all the free
- variables in t12, then t12 has type T12 under Γ''.
- Let Γ' be a context which agrees with Γ on the
- free variables in t; we must show Γ'⊢\y:T11.t12∈
- T11→T12.
-
-
-
- By T_Abs, it suffices to show that Γ',y:T11⊢t12∈
- T12. By the IH (setting Γ''=Γ',y:T11), it
- suffices to show that Γ,y:T11 and Γ',y:T11 agree
- on all the variables that appear free in t12.
-
-
-
- Any variable occurring free in t12 must either be y, or
- some other variable. Γ,y:T11 and Γ',y:T11
- clearly agree on y. Otherwise, we note that any variable
- other than y which occurs free in t12 also occurs free in
- t=\y:T11.t12, and by assumption Γ and Γ'
- agree on all such variables, and hence so do Γ,y:T11
- and Γ',y:T11.
-
-
-
-
-
-
If the last rule was T_App, then t=t1t2, with Γ⊢
- t1∈T2→T and Γ⊢t2∈T2. One induction
- hypothesis states that for all contexts Γ', if Γ'
- agrees with Γ on the free variables in t1, then t1
- has type T2→T under Γ'; there is a similar IH for
- t2. We must show that t1t2 also has type T under
- Γ', given the assumption that Γ' agrees with
- Γ on all the free variables in t1t2. By T_App, it
- suffices to show that t1 and t2 each have the same type
- under Γ' as under Γ. However, we note that all
- free variables in t1 are also free in t1t2, and similarly
- for free variables in t2; hence the desired result follows
- by the two IHs.
-
-
-
-
-
-
-
-
-
-
-Proofwitheauto.
- intros.
- generalizedependentΓ'.
- has_type_cases (inductionH) Case; intros; auto.
- Case "T_Var".
- applyT_Var. rewrite←H0...
- Case "T_Abs".
- applyT_Abs.
- applyIHhas_type. introsx1Hafi.
- (* the only tricky step... the Γ' we use to
- instantiate is extendΓxT11 *)
- unfoldextend. destruct (eq_id_decx0x1)...
- Case "T_App".
- applyT_AppwithT11...
-Qed.
-
-
-
-
-
-
-Now we come to the conceptual heart of the proof that reduction
- preserves types — namely, the observation that substitution
- preserves types.
-
-
-
- Formally, the so-called Substitution Lemma says this: suppose we
- have a term t with a free variable x, and suppose we've been
- able to assign a type T to t under the assumption that x has
- some type U. Also, suppose that we have some other term v and
- that we've shown that v has type U. Then, since v satisfies
- the assumption we made about x when typing t, we should be
- able to substitute v for each of the occurrences of x in t
- and obtain a new term that still has type T.
-
-
- Lemma: If Γ,x:U⊢t∈T and ⊢v∈U, then Γ⊢
- [x:=v]t∈T.
-
-One technical subtlety in the statement of the lemma is that we
- assign v the type U in the empty context — in other words,
- we assume v is closed. This assumption considerably simplifies
- the T_Abs case of the proof (compared to assuming Γ⊢v∈
- U, which would be the other reasonable assumption at this point)
- because the context invariance lemma then tells us that v has
- type U in any context at all — we don't have to worry about
- free variables in v clashing with the variable being introduced
- into the context by T_Abs.
-
-
-
- Proof: We prove, by induction on t, that, for all T and
- Γ, if Γ,x:U⊢t∈T and ⊢v∈U, then Γ⊢
- [x:=v]t∈T.
-
-
-
-
-
If t is a variable, there are two cases to consider, depending
- on whether t is x or some other variable.
-
-
-
-
-
If t=x, then from the fact that Γ,x:U⊢x∈T we
- conclude that U=T. We must show that [x:=v]x=v has
- type T under Γ, given the assumption that v has
- type U=T under the empty context. This follows from
- context invariance: if a closed term has type T in the
- empty context, it has that type in any context.
-
-
-
-
-
-
If t is some variable y that is not equal to x, then
- we need only note that y has the same type under Γ,
- x:U as under Γ.
-
-
-
-
-
-
-
-
-
If t is an abstraction \y:T11.t12, then the IH tells us,
- for all Γ' and T', that if Γ',x:U⊢t12∈T'
- and ⊢v∈U, then Γ'⊢[x:=v]t12∈T'.
-
-
-
- The substitution in the conclusion behaves differently,
- depending on whether x and y are the same variable name.
-
-
-
- First, suppose x=y. Then, by the definition of
- substitution, [x:=v]t=t, so we just need to show Γ⊢
- t∈T. But we know Γ,x:U⊢t:T, and since the
- variable y does not appear free in \y:T11.t12, the
- context invariance lemma yields Γ⊢t∈T.
-
-
-
- Second, suppose x≠y. We know Γ,x:U,y:T11⊢t12∈
- T12 by inversion of the typing relation, and Γ,y:T11,x:U
- ⊢t12∈T12 follows from this by the context invariance
- lemma, so the IH applies, giving us Γ,y:T11⊢[x:=v]t12∈
- T12. By T_Abs, Γ⊢\y:T11.[x:=v]t12∈T11→T12, and
- by the definition of substitution (noting that x≠y),
- Γ⊢\y:T11.[x:=v]t12∈T11→T12 as required.
-
-
-
-
-
-
If t is an application t1t2, the result follows
- straightforwardly from the definition of substitution and the
- induction hypotheses.
-
-
-
-
-
-
The remaining cases are similar to the application case.
-
-
-
-
-
-
- Another technical note: This proof is a rare case where an
- induction on terms, rather than typing derivations, yields a
- simpler argument. The reason for this is that the assumption
- extendΓxU⊢t∈T is not completely generic, in
- the sense that one of the "slots" in the typing relation — namely
- the context — is not just a variable, and this means that Coq's
- native induction tactic does not give us the induction hypothesis
- that we want. It is possible to work around this, but the needed
- generalization is a little tricky. The term t, on the other
- hand, is completely generic.
-
-
-
-
-
-
-Proofwitheauto.
- introsΓxUtvTHtHt'.
- generalizedependentΓ. generalizedependentT.
- t_cases (inductiont) Case; introsTΓH;
- (* in each case, we'll want to get at the derivation of H *)
- inversionH; subst; simpl...
- Case "tvar".
- renameiintoy. destruct (eq_id_decxy).
- SCase "x=y".
- subst.
- rewriteextend_eqinH2.
- inversionH2; subst. clearH2.
- eapplycontext_invariance... introsxHcontra.
- destruct (free_in_context__TemptyHcontra) as [T'HT']...
- inversionHT'.
- SCase "x≠y".
- applyT_Var. rewriteextend_neqinH2...
- Case "tabs".
- renameiintoy. applyT_Abs.
- destruct (eq_id_decxy).
- SCase "x=y".
- eapplycontext_invariance...
- subst.
- introsxHafi. unfoldextend.
- destruct (eq_id_decyx)...
- SCase "x≠y".
- applyIHt. eapplycontext_invariance...
- introszHafi. unfoldextend.
- destruct (eq_id_decyz)...
- subst. rewriteneq_id...
-Qed.
-
-
-
-
-
-
-The substitution lemma can be viewed as a kind of "commutation"
- property. Intuitively, it says that substitution and typing can
- be done in either order: we can either assign types to the terms
- t and v separately (under suitable contexts) and then combine
- them using substitution, or we can substitute first and then
- assign a type to [x:=v]t — the result is the same either
- way.
-
-
-
-
-
-
-
-
Main Theorem
-
-
-
- We now have the tools we need to prove preservation: if a closed
- term t has type T, and takes an evaluation step to t', then t'
- is also a closed term with type T. In other words, the small-step
- evaluation relation preserves types.
-
-
-Proof: by induction on the derivation of ⊢t∈T.
-
-
-
-
-
We can immediately rule out T_Var, T_Abs, T_True, and
- T_False as the final rules in the derivation, since in each of
- these cases t cannot take a step.
-
-
-
-
-
-
If the last rule in the derivation was T_App, then t=t1
- t2. There are three cases to consider, one for each rule that
- could have been used to show that t1t2 takes a step to t'.
-
-
-
-
-
If t1t2 takes a step by ST_App1, with t1 stepping to
- t1', then by the IH t1' has the same type as t1, and
- hence t1't2 has the same type as t1t2.
-
-
-
-
-
-
The ST_App2 case is similar.
-
-
-
-
-
-
If t1t2 takes a step by ST_AppAbs, then t1=
- \x:T11.t12 and t1t2 steps to [x:=t2]t12; the
- desired result now follows from the fact that substitution
- preserves types.
-
-
-
-
-
-
-
-
-
If the last rule in the derivation was T_If, then t=ift1
- thent2elset3, and there are again three cases depending on
- how t steps.
-
-
-
-
-
If t steps to t2 or t3, the result is immediate, since
- t2 and t3 have the same type as t.
-
-
-
-
-
-
Otherwise, t steps by ST_If, and the desired conclusion
- follows directly from the induction hypothesis.
-
-
-
-
-
-
-
-
-
-
-
-
-
-Proofwitheauto.
- remember (@emptyty) asΓ.
- introstt'THT. generalizedependentt'.
- has_type_cases (inductionHT) Case;
- introst'HE; substΓ; subst;
- trysolve [inversionHE; subst; auto].
- Case "T_App".
- inversionHE; subst...
- (* Most of the cases are immediate by induction,
- and eauto takes care of them *)
- SCase "ST_AppAbs".
- applysubstitution_preserves_typingwithT11...
- inversionHT1...
-Qed.
-
-
-
-
-
-
-
Exercise: 2 stars (subject_expansion_stlc)
- An exercise in the Types chapter asked about the subject
- expansion property for the simple language of arithmetic and
- boolean expressions. Does this property hold for STLC? That is,
- is it always the case that, if t⇒t' and has_typet'T,
- then empty⊢t∈T? If so, prove it. If not, give a
- counter-example not involving conditionals.
-
-
-
-(* FILL IN HERE *)
-☐
-
-
-
-
-
-
-
-
-
Type Soundness
-
-
-
-
Exercise: 2 stars, optional (type_soundness)
-
-
-
- Put progress and preservation together and show that a well-typed
- term can never reach a stuck state.
-
- Another pleasant property of the STLC is that types are
- unique: a given term (in a given context) has at most one
- type. Formalize this statement and prove it.
-
-
-
-
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 1 star (progress_preservation_statement)
- Without peeking, write down the progress and preservation
- theorems for the simply typed lambda-calculus. ☐
-
-
-
Exercise: 2 stars (stlc_variation1)
- Suppose we add a new term zap with the following reduction rule:
-
-
-
-
- (ST_Zap)
-
-
-
-
-
-
t ⇒ zap
-
-
-
and the following typing rule:
-
-
-
-
- (T_Zap)
-
-
-
-
-
-
Γ⊢ zap : T
-
-
-
Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (stlc_variation2)
- Suppose instead that we add a new term foo with the following reduction rules:
-
-
-
-
- (ST_Foo1)
-
-
-
-
-
-
(\x:A. x) ⇒ foo
-
-
-
-
-
-
- (ST_Foo2)
-
-
-
-
-
-
foo ⇒ true
-
-
-
Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (stlc_variation3)
- Suppose instead that we remove the rule ST_App1 from the step
- relation. Which of the following properties of the STLC remain
- true in the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars, optional (stlc_variation4)
- Suppose instead that we add the following new rule to the reduction relation:
-
-
-
-
- (ST_FunnyIfTrue)
-
-
-
-
-
-
(if true then t1 else t2) ⇒ true
-
-
-
Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-
-
-
-
Exercise: 2 stars, optional (stlc_variation5)
- Suppose instead that we add the following new rule to the typing relation:
-
-
-
Γ⊢ t1 ∈ Bool->Bool->Bool
-
-
-
-
Γ⊢ t2 ∈ Bool
-
- (T_FunnyApp)
-
-
-
-
-
-
Γ⊢ t1 t2 ∈ Bool
-
-
-
Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-
-
-
-
Exercise: 2 stars, optional (stlc_variation6)
- Suppose instead that we add the following new rule to the typing relation:
-
-
-
Γ⊢ t1 ∈ Bool
-
-
-
-
Γ⊢ t2 ∈ Bool
-
- (T_FunnyApp')
-
-
-
-
-
-
Γ⊢ t1 t2 ∈ Bool
-
-
-
Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-
-
-
-
Exercise: 2 stars, optional (stlc_variation7)
- Suppose we add the following new rule to the typing
- relation of the STLC:
-
-
-
-
- (T_FunnyAbs)
-
-
-
-
-
-
⊢ \x:Bool.t ∈ Bool
-
-
-
Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-☐
-
-
-
-
-
-EndSTLCProp.
-
-
-
-
-
-
Exercise: STLC with Arithmetic
-
-
-
- To see how the STLC might function as the core of a real
- programming language, let's extend it with a concrete base
- type of numbers and some constants and primitive
- operators.
-
-
-
-
-ModuleSTLCArith.
-
-
-
-
-
-To types, we add a base type of natural numbers (and remove
- booleans, for brevity)
-
- Finish formalizing the definition and properties of the STLC extended
- with arithmetic. Specifically:
-
-
-
-
-
Copy the whole development of STLC that we went through above (from
- the definition of values through the Progress theorem), and
- paste it into the file at this point.
-
-
-
-
-
-
Extend the definitions of the subst operation and the step
- relation to include appropriate clauses for the arithmetic operators.
-
-
-
-
-
-
Extend the proofs of all the properties (up to soundness) of
- the original STLC to deal with the new syntactic forms. Make
- sure Coq accepts the whole file.
-
-
-
-
\ No newline at end of file
diff --git a/StlcProp.v b/StlcProp.v
deleted file mode 100644
index c26abc9..0000000
--- a/StlcProp.v
+++ /dev/null
@@ -1,794 +0,0 @@
-(** * StlcProp: Properties of STLC *)
-
-Require Export Stlc.
-
-Module STLCProp.
-Import STLC.
-
-(** In this chapter, we develop the fundamental theory of the Simply
- Typed Lambda Calculus -- in particular, the type safety
- theorem. *)
-
-(* ###################################################################### *)
-(** * Canonical Forms *)
-
-Lemma canonical_forms_bool : forall t,
- empty |- t \in TBool ->
- value t ->
- (t = ttrue) \/ (t = tfalse).
-Proof.
- intros t HT HVal.
- inversion HVal; intros; subst; try inversion HT; auto.
-Qed.
-
-Lemma canonical_forms_fun : forall t T1 T2,
- empty |- t \in (TArrow T1 T2) ->
- value t ->
- exists x u, t = tabs x T1 u.
-Proof.
- intros t T1 T2 HT HVal.
- inversion HVal; intros; subst; try inversion HT; subst; auto.
- exists x0. exists t0. auto.
-Qed.
-
-
-(* ###################################################################### *)
-(** * Progress *)
-
-(** As before, the _progress_ theorem tells us that closed, well-typed
- terms are not stuck: either a well-typed term is a value, or it
- can take an evaluation step. The proof is a relatively
- straightforward extension of the progress proof we saw in the
- [Types] chapter. *)
-
-Theorem progress : forall t T,
- empty |- t \in T ->
- value t \/ exists t', t ==> t'.
-
-(** _Proof_: by induction on the derivation of [|- t \in T].
-
- - The last rule of the derivation cannot be [T_Var], since a
- variable is never well typed in an empty context.
-
- - The [T_True], [T_False], and [T_Abs] cases are trivial, since in
- each of these cases we know immediately that [t] is a value.
-
- - If the last rule of the derivation was [T_App], then [t = t1
- t2], and we know that [t1] and [t2] are also well typed in the
- empty context; in particular, there exists a type [T2] such that
- [|- t1 \in T2 -> T] and [|- t2 \in T2]. By the induction
- hypothesis, either [t1] is a value or it can take an evaluation
- step.
-
- - If [t1] is a value, we now consider [t2], which by the other
- induction hypothesis must also either be a value or take an
- evaluation step.
-
- - Suppose [t2] is a value. Since [t1] is a value with an
- arrow type, it must be a lambda abstraction; hence [t1
- t2] can take a step by [ST_AppAbs].
-
- - Otherwise, [t2] can take a step, and hence so can [t1
- t2] by [ST_App2].
-
- - If [t1] can take a step, then so can [t1 t2] by [ST_App1].
-
- - If the last rule of the derivation was [T_If], then [t = if t1
- then t2 else t3], where [t1] has type [Bool]. By the IH, [t1]
- either is a value or takes a step.
-
- - If [t1] is a value, then since it has type [Bool] it must be
- either [true] or [false]. If it is [true], then [t] steps
- to [t2]; otherwise it steps to [t3].
-
- - Otherwise, [t1] takes a step, and therefore so does [t] (by
- [ST_If]).
-*)
-
-Proof with eauto.
- intros t T Ht.
- remember (@empty ty) as Gamma.
- has_type_cases (induction Ht) Case; subst Gamma...
- Case "T_Var".
- (* contradictory: variables cannot be typed in an
- empty context *)
- inversion H.
-
- Case "T_App".
- (* [t] = [t1 t2]. Proceed by cases on whether [t1] is a
- value or steps... *)
- right. destruct IHHt1...
- SCase "t1 is a value".
- destruct IHHt2...
- SSCase "t2 is also a value".
- assert (exists x0 t0, t1 = tabs x0 T11 t0).
- eapply canonical_forms_fun; eauto.
- destruct H1 as [x0 [t0 Heq]]. subst.
- exists ([x0:=t2]t0)...
-
- SSCase "t2 steps".
- inversion H0 as [t2' Hstp]. exists (tapp t1 t2')...
-
- SCase "t1 steps".
- inversion H as [t1' Hstp]. exists (tapp t1' t2)...
-
- Case "T_If".
- right. destruct IHHt1...
-
- SCase "t1 is a value".
- destruct (canonical_forms_bool t1); subst; eauto.
-
- SCase "t1 also steps".
- inversion H as [t1' Hstp]. exists (tif t1' t2 t3)...
-Qed.
-
-(** **** Exercise: 3 stars, optional (progress_from_term_ind) *)
-(** Show that progress can also be proved by induction on terms
- instead of induction on typing derivations. *)
-
-Theorem progress' : forall t T,
- empty |- t \in T ->
- value t \/ exists t', t ==> t'.
-Proof.
- intros t.
- t_cases (induction t) Case; intros T Ht; auto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** * Preservation *)
-
-(** The other half of the type soundness property is the preservation
- of types during reduction. For this, we need to develop some
- technical machinery for reasoning about variables and
- substitution. Working from top to bottom (the high-level property
- we are actually interested in to the lowest-level technical lemmas
- that are needed by various cases of the more interesting proofs),
- the story goes like this:
-
- - The _preservation theorem_ is proved by induction on a typing
- derivation, pretty much as we did in the [Types] chapter. The
- one case that is significantly different is the one for the
- [ST_AppAbs] rule, which is defined using the substitution
- operation. To see that this step preserves typing, we need to
- know that the substitution itself does. So we prove a...
-
- - _substitution lemma_, stating that substituting a (closed)
- term [s] for a variable [x] in a term [t] preserves the type
- of [t]. The proof goes by induction on the form of [t] and
- requires looking at all the different cases in the definition
- of substitition. This time, the tricky cases are the ones for
- variables and for function abstractions. In both cases, we
- discover that we need to take a term [s] that has been shown
- to be well-typed in some context [Gamma] and consider the same
- term [s] in a slightly different context [Gamma']. For this
- we prove a...
-
- - _context invariance_ lemma, showing that typing is preserved
- under "inessential changes" to the context [Gamma] -- in
- particular, changes that do not affect any of the free
- variables of the term. For this, we need a careful definition
- of
-
- - the _free variables_ of a term -- i.e., the variables occuring
- in the term that are not in the scope of a function
- abstraction that binds them.
-*)
-
-(* ###################################################################### *)
-(** ** Free Occurrences *)
-
-(** A variable [x] _appears free in_ a term _t_ if [t] contains some
- occurrence of [x] that is not under an abstraction labeled [x]. For example:
- - [y] appears free, but [x] does not, in [\x:T->U. x y]
- - both [x] and [y] appear free in [(\x:T->U. x y) x]
- - no variables appear free in [\x:T->U. \y:T. x y] *)
-
-Inductive appears_free_in : id -> tm -> Prop :=
- | afi_var : forall x,
- appears_free_in x (tvar x)
- | afi_app1 : forall x t1 t2,
- appears_free_in x t1 -> appears_free_in x (tapp t1 t2)
- | afi_app2 : forall x t1 t2,
- appears_free_in x t2 -> appears_free_in x (tapp t1 t2)
- | afi_abs : forall x y T11 t12,
- y <> x ->
- appears_free_in x t12 ->
- appears_free_in x (tabs y T11 t12)
- | afi_if1 : forall x t1 t2 t3,
- appears_free_in x t1 ->
- appears_free_in x (tif t1 t2 t3)
- | afi_if2 : forall x t1 t2 t3,
- appears_free_in x t2 ->
- appears_free_in x (tif t1 t2 t3)
- | afi_if3 : forall x t1 t2 t3,
- appears_free_in x t3 ->
- appears_free_in x (tif t1 t2 t3).
-
-Tactic Notation "afi_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "afi_var"
- | Case_aux c "afi_app1" | Case_aux c "afi_app2"
- | Case_aux c "afi_abs"
- | Case_aux c "afi_if1" | Case_aux c "afi_if2"
- | Case_aux c "afi_if3" ].
-
-Hint Constructors appears_free_in.
-
-(** A term in which no variables appear free is said to be _closed_. *)
-
-Definition closed (t:tm) :=
- forall x, ~ appears_free_in x t.
-
-(* ###################################################################### *)
-(** ** Substitution *)
-
-(** We first need a technical lemma connecting free variables and
- typing contexts. If a variable [x] appears free in a term [t],
- and if we know [t] is well typed in context [Gamma], then it must
- be the case that [Gamma] assigns a type to [x]. *)
-
-Lemma free_in_context : forall x t T Gamma,
- appears_free_in x t ->
- Gamma |- t \in T ->
- exists T', Gamma x = Some T'.
-
-(** _Proof_: We show, by induction on the proof that [x] appears free
- in [t], that, for all contexts [Gamma], if [t] is well typed
- under [Gamma], then [Gamma] assigns some type to [x].
-
- - If the last rule used was [afi_var], then [t = x], and from
- the assumption that [t] is well typed under [Gamma] we have
- immediately that [Gamma] assigns a type to [x].
-
- - If the last rule used was [afi_app1], then [t = t1 t2] and [x]
- appears free in [t1]. Since [t] is well typed under [Gamma],
- we can see from the typing rules that [t1] must also be, and
- the IH then tells us that [Gamma] assigns [x] a type.
-
- - Almost all the other cases are similar: [x] appears free in a
- subterm of [t], and since [t] is well typed under [Gamma], we
- know the subterm of [t] in which [x] appears is well typed
- under [Gamma] as well, and the IH gives us exactly the
- conclusion we want.
-
- - The only remaining case is [afi_abs]. In this case [t =
- \y:T11.t12], and [x] appears free in [t12]; we also know that
- [x] is different from [y]. The difference from the previous
- cases is that whereas [t] is well typed under [Gamma], its
- body [t12] is well typed under [(Gamma, y:T11)], so the IH
- allows us to conclude that [x] is assigned some type by the
- extended context [(Gamma, y:T11)]. To conclude that [Gamma]
- assigns a type to [x], we appeal to lemma [extend_neq], noting
- that [x] and [y] are different variables. *)
-
-Proof.
- intros x t T Gamma H H0. generalize dependent Gamma.
- generalize dependent T.
- afi_cases (induction H) Case;
- intros; try solve [inversion H0; eauto].
- Case "afi_abs".
- inversion H1; subst.
- apply IHappears_free_in in H7.
- rewrite extend_neq in H7; assumption.
-Qed.
-
-(** Next, we'll need the fact that any term [t] which is well typed in
- the empty context is closed -- that is, it has no free variables. *)
-
-(** **** Exercise: 2 stars, optional (typable_empty__closed) *)
-Corollary typable_empty__closed : forall t T,
- empty |- t \in T ->
- closed t.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Sometimes, when we have a proof [Gamma |- t : T], we will need to
- replace [Gamma] by a different context [Gamma']. When is it safe
- to do this? Intuitively, it must at least be the case that
- [Gamma'] assigns the same types as [Gamma] to all the variables
- that appear free in [t]. In fact, this is the only condition that
- is needed. *)
-
-Lemma context_invariance : forall Gamma Gamma' t T,
- Gamma |- t \in T ->
- (forall x, appears_free_in x t -> Gamma x = Gamma' x) ->
- Gamma' |- t \in T.
-
-(** _Proof_: By induction on the derivation of [Gamma |- t \in T].
-
- - If the last rule in the derivation was [T_Var], then [t = x]
- and [Gamma x = T]. By assumption, [Gamma' x = T] as well, and
- hence [Gamma' |- t \in T] by [T_Var].
-
- - If the last rule was [T_Abs], then [t = \y:T11. t12], with [T
- = T11 -> T12] and [Gamma, y:T11 |- t12 \in T12]. The induction
- hypothesis is that for any context [Gamma''], if [Gamma,
- y:T11] and [Gamma''] assign the same types to all the free
- variables in [t12], then [t12] has type [T12] under [Gamma''].
- Let [Gamma'] be a context which agrees with [Gamma] on the
- free variables in [t]; we must show [Gamma' |- \y:T11. t12 \in
- T11 -> T12].
-
- By [T_Abs], it suffices to show that [Gamma', y:T11 |- t12 \in
- T12]. By the IH (setting [Gamma'' = Gamma', y:T11]), it
- suffices to show that [Gamma, y:T11] and [Gamma', y:T11] agree
- on all the variables that appear free in [t12].
-
- Any variable occurring free in [t12] must either be [y], or
- some other variable. [Gamma, y:T11] and [Gamma', y:T11]
- clearly agree on [y]. Otherwise, we note that any variable
- other than [y] which occurs free in [t12] also occurs free in
- [t = \y:T11. t12], and by assumption [Gamma] and [Gamma']
- agree on all such variables, and hence so do [Gamma, y:T11]
- and [Gamma', y:T11].
-
- - If the last rule was [T_App], then [t = t1 t2], with [Gamma |-
- t1 \in T2 -> T] and [Gamma |- t2 \in T2]. One induction
- hypothesis states that for all contexts [Gamma'], if [Gamma']
- agrees with [Gamma] on the free variables in [t1], then [t1]
- has type [T2 -> T] under [Gamma']; there is a similar IH for
- [t2]. We must show that [t1 t2] also has type [T] under
- [Gamma'], given the assumption that [Gamma'] agrees with
- [Gamma] on all the free variables in [t1 t2]. By [T_App], it
- suffices to show that [t1] and [t2] each have the same type
- under [Gamma'] as under [Gamma]. However, we note that all
- free variables in [t1] are also free in [t1 t2], and similarly
- for free variables in [t2]; hence the desired result follows
- by the two IHs.
-*)
-
-Proof with eauto.
- intros.
- generalize dependent Gamma'.
- has_type_cases (induction H) Case; intros; auto.
- Case "T_Var".
- apply T_Var. rewrite <- H0...
- Case "T_Abs".
- apply T_Abs.
- apply IHhas_type. intros x1 Hafi.
- (* the only tricky step... the [Gamma'] we use to
- instantiate is [extend Gamma x T11] *)
- unfold extend. destruct (eq_id_dec x0 x1)...
- Case "T_App".
- apply T_App with T11...
-Qed.
-
-(** Now we come to the conceptual heart of the proof that reduction
- preserves types -- namely, the observation that _substitution_
- preserves types.
-
- Formally, the so-called _Substitution Lemma_ says this: suppose we
- have a term [t] with a free variable [x], and suppose we've been
- able to assign a type [T] to [t] under the assumption that [x] has
- some type [U]. Also, suppose that we have some other term [v] and
- that we've shown that [v] has type [U]. Then, since [v] satisfies
- the assumption we made about [x] when typing [t], we should be
- able to substitute [v] for each of the occurrences of [x] in [t]
- and obtain a new term that still has type [T]. *)
-
-(** _Lemma_: If [Gamma,x:U |- t \in T] and [|- v \in U], then [Gamma |-
- [x:=v]t \in T]. *)
-
-Lemma substitution_preserves_typing : forall Gamma x U t v T,
- extend Gamma x U |- t \in T ->
- empty |- v \in U ->
- Gamma |- [x:=v]t \in T.
-
-(** One technical subtlety in the statement of the lemma is that we
- assign [v] the type [U] in the _empty_ context -- in other words,
- we assume [v] is closed. This assumption considerably simplifies
- the [T_Abs] case of the proof (compared to assuming [Gamma |- v \in
- U], which would be the other reasonable assumption at this point)
- because the context invariance lemma then tells us that [v] has
- type [U] in any context at all -- we don't have to worry about
- free variables in [v] clashing with the variable being introduced
- into the context by [T_Abs].
-
- _Proof_: We prove, by induction on [t], that, for all [T] and
- [Gamma], if [Gamma,x:U |- t \in T] and [|- v \in U], then [Gamma |-
- [x:=v]t \in T].
-
- - If [t] is a variable, there are two cases to consider, depending
- on whether [t] is [x] or some other variable.
-
- - If [t = x], then from the fact that [Gamma, x:U |- x \in T] we
- conclude that [U = T]. We must show that [[x:=v]x = v] has
- type [T] under [Gamma], given the assumption that [v] has
- type [U = T] under the empty context. This follows from
- context invariance: if a closed term has type [T] in the
- empty context, it has that type in any context.
-
- - If [t] is some variable [y] that is not equal to [x], then
- we need only note that [y] has the same type under [Gamma,
- x:U] as under [Gamma].
-
- - If [t] is an abstraction [\y:T11. t12], then the IH tells us,
- for all [Gamma'] and [T'], that if [Gamma',x:U |- t12 \in T']
- and [|- v \in U], then [Gamma' |- [x:=v]t12 \in T'].
-
- The substitution in the conclusion behaves differently,
- depending on whether [x] and [y] are the same variable name.
-
- First, suppose [x = y]. Then, by the definition of
- substitution, [[x:=v]t = t], so we just need to show [Gamma |-
- t \in T]. But we know [Gamma,x:U |- t : T], and since the
- variable [y] does not appear free in [\y:T11. t12], the
- context invariance lemma yields [Gamma |- t \in T].
-
- Second, suppose [x <> y]. We know [Gamma,x:U,y:T11 |- t12 \in
- T12] by inversion of the typing relation, and [Gamma,y:T11,x:U
- |- t12 \in T12] follows from this by the context invariance
- lemma, so the IH applies, giving us [Gamma,y:T11 |- [x:=v]t12 \in
- T12]. By [T_Abs], [Gamma |- \y:T11. [x:=v]t12 \in T11->T12], and
- by the definition of substitution (noting that [x <> y]),
- [Gamma |- \y:T11. [x:=v]t12 \in T11->T12] as required.
-
- - If [t] is an application [t1 t2], the result follows
- straightforwardly from the definition of substitution and the
- induction hypotheses.
-
- - The remaining cases are similar to the application case.
-
- Another technical note: This proof is a rare case where an
- induction on terms, rather than typing derivations, yields a
- simpler argument. The reason for this is that the assumption
- [extend Gamma x U |- t \in T] is not completely generic, in
- the sense that one of the "slots" in the typing relation -- namely
- the context -- is not just a variable, and this means that Coq's
- native induction tactic does not give us the induction hypothesis
- that we want. It is possible to work around this, but the needed
- generalization is a little tricky. The term [t], on the other
- hand, _is_ completely generic. *)
-
-Proof with eauto.
- intros Gamma x U t v T Ht Ht'.
- generalize dependent Gamma. generalize dependent T.
- t_cases (induction t) Case; intros T Gamma H;
- (* in each case, we'll want to get at the derivation of H *)
- inversion H; subst; simpl...
- Case "tvar".
- rename i into y. destruct (eq_id_dec x y).
- SCase "x=y".
- subst.
- rewrite extend_eq in H2.
- inversion H2; subst. clear H2.
- eapply context_invariance... intros x Hcontra.
- destruct (free_in_context _ _ T empty Hcontra) as [T' HT']...
- inversion HT'.
- SCase "x<>y".
- apply T_Var. rewrite extend_neq in H2...
- Case "tabs".
- rename i into y. apply T_Abs.
- destruct (eq_id_dec x y).
- SCase "x=y".
- eapply context_invariance...
- subst.
- intros x Hafi. unfold extend.
- destruct (eq_id_dec y x)...
- SCase "x<>y".
- apply IHt. eapply context_invariance...
- intros z Hafi. unfold extend.
- destruct (eq_id_dec y z)...
- subst. rewrite neq_id...
-Qed.
-
-(** The substitution lemma can be viewed as a kind of "commutation"
- property. Intuitively, it says that substitution and typing can
- be done in either order: we can either assign types to the terms
- [t] and [v] separately (under suitable contexts) and then combine
- them using substitution, or we can substitute first and then
- assign a type to [ [x:=v] t ] -- the result is the same either
- way. *)
-
-(* ###################################################################### *)
-(** ** Main Theorem *)
-
-(** We now have the tools we need to prove preservation: if a closed
- term [t] has type [T], and takes an evaluation step to [t'], then [t']
- is also a closed term with type [T]. In other words, the small-step
- evaluation relation preserves types.
-*)
-
-Theorem preservation : forall t t' T,
- empty |- t \in T ->
- t ==> t' ->
- empty |- t' \in T.
-
-(** _Proof_: by induction on the derivation of [|- t \in T].
-
- - We can immediately rule out [T_Var], [T_Abs], [T_True], and
- [T_False] as the final rules in the derivation, since in each of
- these cases [t] cannot take a step.
-
- - If the last rule in the derivation was [T_App], then [t = t1
- t2]. There are three cases to consider, one for each rule that
- could have been used to show that [t1 t2] takes a step to [t'].
-
- - If [t1 t2] takes a step by [ST_App1], with [t1] stepping to
- [t1'], then by the IH [t1'] has the same type as [t1], and
- hence [t1' t2] has the same type as [t1 t2].
-
- - The [ST_App2] case is similar.
-
- - If [t1 t2] takes a step by [ST_AppAbs], then [t1 =
- \x:T11.t12] and [t1 t2] steps to [[x:=t2]t12]; the
- desired result now follows from the fact that substitution
- preserves types.
-
- - If the last rule in the derivation was [T_If], then [t = if t1
- then t2 else t3], and there are again three cases depending on
- how [t] steps.
-
- - If [t] steps to [t2] or [t3], the result is immediate, since
- [t2] and [t3] have the same type as [t].
-
- - Otherwise, [t] steps by [ST_If], and the desired conclusion
- follows directly from the induction hypothesis.
-*)
-
-Proof with eauto.
- remember (@empty ty) as Gamma.
- intros t t' T HT. generalize dependent t'.
- has_type_cases (induction HT) Case;
- intros t' HE; subst Gamma; subst;
- try solve [inversion HE; subst; auto].
- Case "T_App".
- inversion HE; subst...
- (* Most of the cases are immediate by induction,
- and [eauto] takes care of them *)
- SCase "ST_AppAbs".
- apply substitution_preserves_typing with T11...
- inversion HT1...
-Qed.
-
-(** **** Exercise: 2 stars (subject_expansion_stlc) *)
-(** An exercise in the [Types] chapter asked about the subject
- expansion property for the simple language of arithmetic and
- boolean expressions. Does this property hold for STLC? That is,
- is it always the case that, if [t ==> t'] and [has_type t' T],
- then [empty |- t \in T]? If so, prove it. If not, give a
- counter-example not involving conditionals.
-
-(* FILL IN HERE *)
-[]
-*)
-
-
-(* ###################################################################### *)
-(** * Type Soundness *)
-
-(** **** Exercise: 2 stars, optional (type_soundness) *)
-
-(** Put progress and preservation together and show that a well-typed
- term can _never_ reach a stuck state. *)
-
-Definition stuck (t:tm) : Prop :=
- (normal_form step) t /\ ~ value t.
-
-Corollary soundness : forall t t' T,
- empty |- t \in T ->
- t ==>* t' ->
- ~(stuck t').
-Proof.
- intros t t' T Hhas_type Hmulti. unfold stuck.
- intros [Hnf Hnot_val]. unfold normal_form in Hnf.
- induction Hmulti.
- (* FILL IN HERE *) Admitted.
-
-(* ###################################################################### *)
-(** * Uniqueness of Types *)
-
-(** **** Exercise: 3 stars (types_unique) *)
-(** Another pleasant property of the STLC is that types are
- unique: a given term (in a given context) has at most one
- type. *)
-(** Formalize this statement and prove it. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-(* ###################################################################### *)
-(** * Additional Exercises *)
-
-(** **** Exercise: 1 star (progress_preservation_statement) *)
-(** Without peeking, write down the progress and preservation
- theorems for the simply typed lambda-calculus. *)
-(** [] *)
-
-
-(** **** Exercise: 2 stars (stlc_variation1) *)
-(** Suppose we add a new term [zap] with the following reduction rule:
- --------- (ST_Zap)
- t ==> zap
-and the following typing rule:
- ---------------- (T_Zap)
- Gamma |- zap : T
- Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-[]
-*)
-
-(** **** Exercise: 2 stars (stlc_variation2) *)
-(** Suppose instead that we add a new term [foo] with the following reduction rules:
- ----------------- (ST_Foo1)
- (\x:A. x) ==> foo
-
- ------------ (ST_Foo2)
- foo ==> true
- Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-[]
-*)
-
-(** **** Exercise: 2 stars (stlc_variation3) *)
-(** Suppose instead that we remove the rule [ST_App1] from the [step]
- relation. Which of the following properties of the STLC remain
- true in the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (stlc_variation4) *)
-(** Suppose instead that we add the following new rule to the reduction relation:
- ---------------------------------- (ST_FunnyIfTrue)
- (if true then t1 else t2) ==> true
- Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-*)
-
-(** **** Exercise: 2 stars, optional (stlc_variation5) *)
-(** Suppose instead that we add the following new rule to the typing relation:
- Gamma |- t1 \in Bool->Bool->Bool
- Gamma |- t2 \in Bool
- ------------------------------ (T_FunnyApp)
- Gamma |- t1 t2 \in Bool
- Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-*)
-
-(** **** Exercise: 2 stars, optional (stlc_variation6) *)
-(** Suppose instead that we add the following new rule to the typing relation:
- Gamma |- t1 \in Bool
- Gamma |- t2 \in Bool
- --------------------- (T_FunnyApp')
- Gamma |- t1 t2 \in Bool
- Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-*)
-
-(** **** Exercise: 2 stars, optional (stlc_variation7) *)
-(** Suppose we add the following new rule to the typing
- relation of the STLC:
- ------------------- (T_FunnyAbs)
- |- \x:Bool.t \in Bool
- Which of the following properties of the STLC remain true in
- the presence of this rule? For each one, write either
- "remains true" or else "becomes false." If a property becomes
- false, give a counterexample.
-
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-[]
-*)
-
-End STLCProp.
-
-(* ###################################################################### *)
-(* ###################################################################### *)
-(** ** Exercise: STLC with Arithmetic *)
-
-(** To see how the STLC might function as the core of a real
- programming language, let's extend it with a concrete base
- type of numbers and some constants and primitive
- operators. *)
-
-Module STLCArith.
-
-(** To types, we add a base type of natural numbers (and remove
- booleans, for brevity) *)
-
-Inductive ty : Type :=
- | TArrow : ty -> ty -> ty
- | TNat : ty.
-
-(** To terms, we add natural number constants, along with
- successor, predecessor, multiplication, and zero-testing... *)
-
-Inductive tm : Type :=
- | tvar : id -> tm
- | tapp : tm -> tm -> tm
- | tabs : id -> ty -> tm -> tm
- | tnat : nat -> tm
- | tsucc : tm -> tm
- | tpred : tm -> tm
- | tmult : tm -> tm -> tm
- | tif0 : tm -> tm -> tm -> tm.
-
-Tactic Notation "t_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "tvar" | Case_aux c "tapp"
- | Case_aux c "tabs" | Case_aux c "tnat"
- | Case_aux c "tsucc" | Case_aux c "tpred"
- | Case_aux c "tmult" | Case_aux c "tif0" ].
-
-(** **** Exercise: 4 stars (stlc_arith) *)
-(** Finish formalizing the definition and properties of the STLC extended
- with arithmetic. Specifically:
-
- - Copy the whole development of STLC that we went through above (from
- the definition of values through the Progress theorem), and
- paste it into the file at this point.
-
- - Extend the definitions of the [subst] operation and the [step]
- relation to include appropriate clauses for the arithmetic operators.
-
- - Extend the proofs of all the properties (up to [soundness]) of
- the original STLC to deal with the new syntactic forms. Make
- sure Coq accepts the whole file. *)
-
-(* FILL IN HERE *)
-(** [] *)
-
-End STLCArith.
-
-(* $Date: 2014-04-23 09:37:37 -0400 (Wed, 23 Apr 2014) $ *)
-
diff --git a/Sub.html b/Sub.html
deleted file mode 100644
index 2f953d7..0000000
--- a/Sub.html
+++ /dev/null
@@ -1,2849 +0,0 @@
-
-
-
-
-
-Sub: Subtyping
-
-
-
-
-
-
-
-
-
-
-
-
-
-
SubSubtyping
-
-
-
-
-
-
-
-
-
-
-RequireExportMoreStlc.
-
-
-
-
-
-
Concepts
-
-
-
- We now turn to the study of subtyping, perhaps the most
- characteristic feature of the static type systems of recently
- designed programming languages and a key feature needed to support
- the object-oriented programming style.
-
-
-
-
-
-
-
-
A Motivating Example
-
-
-
- Suppose we are writing a program involving two record types
- defined as follows:
-
- is not typable: it involves an application of a function that wants
- a one-field record to an argument that actually provides two
- fields, while the T_App rule demands that the domain type of the
- function being applied must match the type of the argument
- precisely.
-
-
-
- But this is silly: we're passing the function a better argument
- than it needs! The only thing the body of the function can
- possibly do with its record argument r is project the field age
- from it: nothing else is allowed by the type, and the presence or
- absence of an extra gpa field makes no difference at all. So,
- intuitively, it seems that this function should be applicable to
- any record value that has at least an age field.
-
-
-
- Looking at the same thing from another point of view, a record with
- more fields is "at least as good in any context" as one with just a
- subset of these fields, in the sense that any value belonging to
- the longer record type can be used safely in any context
- expecting the shorter record type. If the context expects
- something with the shorter type but we actually give it something
- with the longer type, nothing bad will happen (formally, the
- program will not get stuck).
-
-
-
- The general principle at work here is called subtyping. We say
- that "S is a subtype of T", informally written S<:T, if a
- value of type S can safely be used in any context where a value
- of type T is expected. The idea of subtyping applies not only to
- records, but to all of the type constructors in the language —
- functions, pairs, etc.
-
-
-
Subtyping and Object-Oriented Languages
-
-
-
- Subtyping plays a fundamental role in many programming
- languages — in particular, it is closely related to the notion of
- subclassing in object-oriented languages.
-
-
-
- An object in Java, C#, etc. can be thought of as a record,
- some of whose fields are functions ("methods") and some of whose
- fields are data values ("fields" or "instance variables").
- Invoking a method m of an object o on some arguments a1..an
- consists of projecting out the m field of o and applying it to
- a1..an.
-
-
-
- The type of an object can be given as either a class or an
- interface. Both of these provide a description of which methods
- and which data fields the object offers.
-
-
-
- Classes and interfaces are related by the subclass and
- subinterface relations. An object belonging to a subclass (or
- subinterface) is required to provide all the methods and fields of
- one belonging to a superclass (or superinterface), plus possibly
- some more.
-
-
-
- The fact that an object from a subclass (or sub-interface) can be
- used in place of one from a superclass (or super-interface)
- provides a degree of flexibility that is is extremely handy for
- organizing complex libraries. For example, a GUI toolkit like
- Java's Swing framework might define an abstract interface
- Component that collects together the common fields and methods
- of all objects having a graphical representation that can be
- displayed on the screen and that can interact with the user.
- Examples of such object would include the buttons, checkboxes, and
- scrollbars of a typical GUI. A method that relies only on this
- common interface can now be applied to any of these objects.
-
-
-
- Of course, real object-oriented languages include many other
- features besides these. For example, fields can be updated.
- Fields and methods can be declared private. Classes also give
- code that is used when constructing objects and implementing
- their methods, and the code in subclasses cooperate with code in
- superclasses via inheritance. Classes can have static methods
- and fields, initializers, etc., etc.
-
-
-
- To keep things simple here, we won't deal with any of these
- issues — in fact, we won't even talk any more about objects or
- classes. (There is a lot of discussion in Types and Programming
- Languages, if you are interested.) Instead, we'll study the core
- concepts behind the subclass / subinterface relation in the
- simplified setting of the STLC.
-
-
-
- Of course, real OO languages have lots of other features...
-
-
-
-
-
mutable fields
-
-
-
private and other visibility modifiers
-
-
-
method inheritance
-
-
-
static components
-
-
-
etc., etc.
-
-
-
-
-
-
- We'll ignore all these and focus on core mechanisms.
-
-
-
The Subsumption Rule
-
-
-
- Our goal for this chapter is to add subtyping to the simply typed
- lambda-calculus (with some of the basic extensions from MoreStlc).
- This involves two steps:
-
-
-
-
-
Defining a binary subtype relation between types.
-
-
-
-
-
-
Enriching the typing relation to take subtyping into account.
-
-
-
-
-
-
- The second step is actually very simple. We add just a single rule
- to the typing relation: the so-called rule of subsumption:
-
-
-
Γ⊢ t : S S <: T
-
- (T_Sub)
-
-
-
-
-
-
Γ⊢ t : T
-
-
-
This rule says, intuitively, that it is OK to "forget" some of
- what we know about a term. For example, we may know that t is a record with two
- fields (e.g., S={x:A→A,y:B→B}), but choose to forget about
- one of the fields (T={y:B→B}) so that we can pass t to a
- function that requires just a single-field record.
-
-
-
The Subtype Relation
-
-
-
- The first step — the definition of the relation S<:T — is
- where all the action is. Let's look at each of the clauses of its
- definition.
-
-
-
Structural Rules
-
-
-
- To start off, we impose two "structural rules" that are
- independent of any particular type constructor: a rule of
- transitivity, which says intuitively that, if S is better than
- U and U is better than T, then S is better than T...
-
-
-
S <: U U <: T
-
- (S_Trans)
-
-
-
-
-
-
S <: T
-
-
-
... and a rule of reflexivity, since certainly any type T is
- as good as itself:
-
-
-
-
- (S_Refl)
-
-
-
-
-
-
T <: T
-
-
-
-
-
-
Products
-
-
-
- Now we consider the individual type constructors, one by one,
- beginning with product types. We consider one pair to be "better
- than" another if each of its components is.
-
-
-
S1 <: T1 S2 <: T2
-
- (S_Prod)
-
-
-
-
-
-
S1 × S2 <: T1 × T2
-
-
-
-
-
-
Arrows
-
-
-
- Suppose we have two functions f and g with these types:
-
-
-
-
- f : C→Student
- g : (C→Person) →D
-
-
-
- That is, f is a function that yields a record of type Student,
- and g is a (higher-order) function that expects its (function)
- argument to yield a record of type Person. Also suppose, even
- though we haven't yet discussed subtyping for records, that
- Student is a subtype of Person. Then the application gf is
- safe even though their types do not match up precisely, because
- the only thing g can do with f is to apply it to some
- argument (of type C); the result will actually be a Student,
- while g will be expecting a Person, but this is safe because
- the only thing g can then do is to project out the two fields
- that it knows about (name and age), and these will certainly
- be among the fields that are present.
-
-
-
- This example suggests that the subtyping rule for arrow types
- should say that two arrow types are in the subtype relation if
- their results are:
-
-
-
S2 <: T2
-
- (S_Arrow_Co)
-
-
-
-
-
-
S1→ S2 <: S1→ T2
-
-
-
We can generalize this to allow the arguments of the two arrow
- types to be in the subtype relation as well:
-
-
-
T1 <: S1 S2 <: T2
-
- (S_Arrow)
-
-
-
-
-
-
S1→ S2 <: T1→ T2
-
-
-
Notice that the argument types are subtypes "the other way round":
- in order to conclude that S1→S2 to be a subtype of T1→T2, it
- must be the case that T1 is a subtype of S1. The arrow
- constructor is said to be contravariant in its first argument
- and covariant in its second.
-
-
-
- Here is an example that illustrates this:
-
-
-
-
- f : Person→C
- g : (Student→C) →D
-
-
-
- The application gf is safe, because the only thing the body of
- g can do with f is to apply it to some argument of type
- Student. Since f requires records having (at least) the
- fields of a Person, this will always work. So Person→C is a
- subtype of Student→C since Student is a subtype of
- Person.
-
-
-
- The intuition is that, if we have a function f of type S1→S2,
- then we know that f accepts elements of type S1; clearly, f
- will also accept elements of any subtype T1 of S1. The type of
- f also tells us that it returns elements of type S2; we can
- also view these results belonging to any supertype T2 of
- S2. That is, any function f of type S1→S2 can also be
- viewed as having type T1→T2.
-
-
-
-
Records
-
-
-
- What about subtyping for record types?
-
-
- The basic intuition about subtyping for record types is that it is
- always safe to use a "bigger" record in place of a "smaller" one.
- That is, given a record type, adding extra fields will always
- result in a subtype. If some code is expecting a record with
- fields x and y, it is perfectly safe for it to receive a record
- with fields x, y, and z; the z field will simply be ignored.
- For example,
-
-
- This is known as "width subtyping" for records.
-
-
- We can also create a subtype of a record type by replacing the type
- of one of its fields with a subtype. If some code is expecting a
- record with a field x of type T, it will be happy with a record
- having a field x of type S as long as S is a subtype of
- T. For example,
-
-
-
-
- {x:Student} <: {x:Person}
-
-
-
- This is known as "depth subtyping".
-
-
- Finally, although the fields of a record type are written in a
- particular order, the order does not really matter. For example,
-
-
-
- We could formalize these requirements in a single subtyping rule
- for records as follows:
-
-
-
for each jk in j1..jn,
-
-
-
-
∃ip in i1..im, such that
-
-
-
-
jk=ip and Sp <: Tk
-
- (S_Rcd)
-
-
-
-
-
-
{i1:S1...im:Sm} <: {j1:T1...jn:Tn}
-
-
-
That is, the record on the left should have all the field labels of
- the one on the right (and possibly more), while the types of the
- common fields should be in the subtype relation. However, this rule
- is rather heavy and hard to read. If we like, we can decompose it
- into three simpler rules, which can be combined using S_Trans to
- achieve all the same effects.
-
-
- First, adding fields to the end of a record type gives a subtype:
-
-
-
n > m
-
- (S_RcdWidth)
-
-
-
-
-
-
{i1:T1...in:Tn} <: {i1:T1...im:Tm}
-
-
-
We can use S_RcdWidth to drop later fields of a multi-field
- record while keeping earlier fields, showing for example that
- {age:Nat,name:String}<:{name:String}.
-
-
- Second, we can apply subtyping inside the components of a compound
- record type:
-
-
-
S1 <: T1 ... Sn <: Tn
-
- (S_RcdDepth)
-
-
-
-
-
-
{i1:S1...in:Sn} <: {i1:T1...in:Tn}
-
-
-
For example, we can use S_RcdDepth and S_RcdWidth together to
- show that {y:Student,x:Nat}<:{y:Person}.
-
-
- Third, we need to be able to reorder fields. For example, we
- might expect that {name:String,gpa:Nat,age:Nat}<:Person. We
- haven't quite achieved this yet: using just S_RcdDepth and
- S_RcdWidth we can only drop fields from the end of a record
- type. So we need:
-
-
-
{i1:S1...in:Sn} is a permutation of {i1:T1...in:Tn}
-
- (S_RcdPerm)
-
-
-
-
-
-
{i1:S1...in:Sn} <: {i1:T1...in:Tn}
-
-
-
-
-
- It is worth noting that full-blown language designs may choose not
- to adopt all of these subtyping rules. For example, in Java:
-
-
-
-
-
A subclass may not change the argument or result types of a
- method of its superclass (i.e., no depth subtyping or no arrow
- subtyping, depending how you look at it).
-
-
-
-
-
-
Each class has just one superclass ("single inheritance" of
- classes).
-
-
-
-
-
-
Each class member (field or method) can be assigned a single
- index, adding new indices "on the right" as more members are
- added in subclasses (i.e., no permutation for classes).
-
-
-
-
-
-
A class may implement multiple interfaces — so-called "multiple
- inheritance" of interfaces (i.e., permutation is allowed for
- interfaces).
-
-
-
-
-
-
Exercise: 2 stars (arrow_sub_wrong)
- Suppose we had incorrectly defined subtyping as covariant on both
- the right and the left of arrow types:
-
-
-
S1 <: T1 S2 <: T2
-
- (S_Arrow_wrong)
-
-
-
-
-
-
S1→ S2 <: T1→ T2
-
-
-
Give a concrete example of functions f and g with the following types...
-
-
-
-
- f : Student→Nat
- g : (Person→Nat) →Nat
-
-
-
- ... such that the application gf will get stuck during
- execution.
-
-
-
-☐
-
-
-
-
Top
-
-
-
- Finally, it is natural to give the subtype relation a maximal
- element — a type that lies above every other type and is
- inhabited by all (well-typed) values. We do this by adding to the
- language one new type constant, called Top, together with a
- subtyping rule that places it above every other type in the
- subtype relation:
-
-
-
-
- (S_Top)
-
-
-
-
-
-
S <: Top
-
-
-
The Top type is an analog of the Object type in Java and C#.
-
-
-
-
-
-
-
-
Summary
-
-
-
- In summary, we form the STLC with subtyping by starting with the
- pure STLC (over some set of base types) and...
-
-
-
-
-
adding a base type Top,
-
-
-
-
-
-
adding the rule of subsumption
-
-
-
Γ⊢ t : S S <: T
-
- (T_Sub)
-
-
-
-
-
-
Γ⊢ t : T
-
-
-
to the typing relation, and
-
-
-
-
-
-
defining a subtype relation as follows:
-
-
-
S <: U U <: T
-
- (S_Trans)
-
-
-
-
-
-
S <: T
-
-
-
-
-
-
- (S_Refl)
-
-
-
-
-
-
T <: T
-
-
-
-
-
-
- (S_Top)
-
-
-
-
-
-
S <: Top
-
-
-
-
-
S1 <: T1 S2 <: T2
-
- (S_Prod)
-
-
-
-
-
-
S1 × S2 <: T1 × T2
-
-
-
-
-
T1 <: S1 S2 <: T2
-
- (S_Arrow)
-
-
-
-
-
-
S1→ S2 <: T1→ T2
-
-
-
-
-
n > m
-
- (S_RcdWidth)
-
-
-
-
-
-
{i1:T1...in:Tn} <: {i1:T1...im:Tm}
-
-
-
-
-
S1 <: T1 ... Sn <: Tn
-
- (S_RcdDepth)
-
-
-
-
-
-
{i1:S1...in:Sn} <: {i1:T1...in:Tn}
-
-
-
-
-
{i1:S1...in:Sn} is a permutation of {i1:T1...in:Tn}
- Suppose we have types S, T, U, and V with S<:T
- and U<:V. Which of the following subtyping assertions
- are then true? Write true or false after each one.
- (A, B, and C here are base types.)
-
-
-
-
-
T→S<:T→S
-
-
-
-
-
-
Top→U<:S→Top
-
-
-
-
-
-
(C→C)→(A×B)<:(C→C)→(Top×B)
-
-
-
-
-
-
T→T→U<:S→S→V
-
-
-
-
-
-
(T→T)→U<:(S→S)→V
-
-
-
-
-
-
((T→S)→T)→U<:((S→T)→S)→V
-
-
-
-
-
-
S×V<:T×U
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (subtype_order)
- The following types happen to form a linear order with respect to subtyping:
-
-
-
-
-
Top
-
-
-
Top→Student
-
-
-
Student→Person
-
-
-
Student→Top
-
-
-
Person→Student
-
-
-
-
-
-
-Write these types in order from the most specific to the most general.
-
-
-
-Where does the type Top→Top→Student fit into this order?
-
-
-
-
-
-
-
Exercise: 1 star (subtype_instances_tf_2)
- Which of the following statements are true? Write true or
- false after each one.
-
-
-
-
- ∀ST,
- S <: T→
- S→S <: T→T
-
- ∀S,
- S <: A→A→
- ∃T,
- S = T→T∧T <: A
-
- ∀ST1T2,
- (S <: T1→T2) →
- ∃S1S2,
- S = S1→S2∧T1 <: S1∧S2 <: T2
-
- ∃S,
- S <: S→S
-
- ∃S,
- S→S <: S
-
- ∀ST1T2,
- S <: T1×T2→
- ∃S1S2,
- S = S1×S2∧S1 <: T1∧S2 <: T2
-
-
-
-☐
-
-
-
Exercise: 1 star (subtype_concepts_tf)
- Which of the following statements are true, and which are false?
-
-
-
-
-
There exists a type that is a supertype of every other type.
-
-
-
-
-
-
There exists a type that is a subtype of every other type.
-
-
-
-
-
-
There exists a pair type that is a supertype of every other
- pair type.
-
-
-
-
-
-
There exists a pair type that is a subtype of every other
- pair type.
-
-
-
-
-
-
There exists an arrow type that is a supertype of every other
- arrow type.
-
-
-
-
-
-
There exists an arrow type that is a subtype of every other
- arrow type.
-
-
-
-
-
-
There is an infinite descending chain of distinct types in the
- subtype relation—-that is, an infinite sequence of types
- S0, S1, etc., such that all the Si's are different and
- each S(i+1) is a subtype of Si.
-
-
-
-
-
-
There is an infinite ascending chain of distinct types in
- the subtype relation—-that is, an infinite sequence of types
- S0, S1, etc., such that all the Si's are different and
- each S(i+1) is a supertype of Si.
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (proper_subtypes)
- Is the following statement true or false? Briefly explain your
- answer.
-
-
-
-
- ∀T,
- ~(∃n, T = TBasen) →
- ∃S,
- S <: T∧S ≠ T
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (small_large_1)
-
-
-
-
-
What is the smallest type T ("smallest" in the subtype
- relation) that makes the following assertion true? (Assume we
- have Unit among the base types and unit as a constant of this
- type.)
-
-
What is the largest type T that makes the same assertion true?
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars, optional (small_large_3)
-
-
-
-
-
What is the smallest type T that makes the following
- assertion true?
-
-
-
-
- a:A⊢ (\p:(A×T). (p.snd) (p.fst)) (a , \z:A.z) : A
-
-
-
-
-
-
-
-
-
What is the largest type T that makes the same assertion true?
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (small_large_4)
-
-
-
-
-
What is the smallest type T that makes the following
- assertion true?
-
-
-
-
- ∃S,
- empty⊢ (\p:(A×T). (p.snd) (p.fst)) : S
-
-
-
-
-
-
-
-
-
What is the largest type T that makes the same
- assertion true?
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (smallest_1)
- What is the smallest type T that makes the following
- assertion true?
-
-
-
-
- ∃S, ∃t,
- empty⊢ (\x:T. xx) t : S
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (smallest_2)
- What is the smallest type T that makes the following
- assertion true?
-
-
-
-
- empty⊢ (\x:Top. x) ((\z:A.z) , (\z:B.z)) : T
-
-
-
-☐
-
-
-
-
Exercise: 3 stars, optional (count_supertypes)
- How many supertypes does the record type {x:A,y:C→C} have? That is,
- how many different types T are there such that {x:A,y:C→C}<:
- T? (We consider two types to be different if they are written
- differently, even if each is a subtype of the other. For example,
- {x:A,y:B} and {y:B,x:A} are different.)
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (pair_permutation)
- The subtyping rule for product types
-
-
-
S1 <: T1 S2 <: T2
-
- (S_Prod)
-
-
-
-
-
-
S1*S2 <: T1*T2
-
-
-
intuitively corresponds to the "depth" subtyping rule for records. Extending the analogy, we might consider adding a "permutation" rule
-
-
-
-
-
-
-
-
-
-
-
T1*T2 <: T2*T1
-
-
-
for products.
-Is this a good idea? Briefly explain why or why not.
-
-
-
-☐
-
-
-
-
-
-
-
-
-
Formal Definitions
-
-
-
- Most of the definitions — in particular, the syntax and
- operational semantics of the language — are identical to what we
- saw in the last chapter. We just need to extend the typing
- relation with the subsumption rule and add a new Inductive
- definition for the subtyping relation. Let's first do the
- identical bits.
-
-
-
-
-
-
-
-
Core Definitions
-
-
-
-
-
-
-
-
-
Syntax
-
-
-
- For the sake of more interesting examples below, we'll allow an
- arbitrary set of additional base types like String, Float,
- etc. We won't bother adding any constants belonging to these
- types or any operators on them, but we could easily do so.
-
-
- In the rest of the chapter, we formalize just base types,
- booleans, arrow types, Unit, and Top, omitting record types
- and leaving product types as an exercise.
-
-
- Now we come to the most interesting part. We begin by
- defining the subtyping relation and developing some of its
- important technical properties.
-
-
- The definition of subtyping is just what we sketched in the
- motivating discussion.
-
-Note that we don't need any special rules for base types: they are
- automatically subtypes of themselves (by S_Refl) and Top (by
- S_Top), and that's all we want.
-
-
-
-
-HintConstructorssubtype.
-
-
-Tactic Notation "subtype_cases" tactic(first) ident(c) :=
- first;
- [ Case_auxc "S_Refl" | Case_auxc "S_Trans"
- | Case_auxc "S_Top" | Case_auxc "S_Arrow"
- ].
-
-
-ModuleExamples.
-
-
-Notationx := (Id 0).
-Notationy := (Id 1).
-Notationz := (Id 2).
-
-
-NotationA := (TBase (Id 6)).
-NotationB := (TBase (Id 7)).
-NotationC := (TBase (Id 8)).
-
-
-NotationString := (TBase (Id 9)).
-NotationFloat := (TBase (Id 10)).
-NotationInteger := (TBase (Id 11)).
-
-
-
-
- (Do this exercise after you have added product types to the
- language, at least up to this point in the file).
-
-
-
- Using the encoding of records into pairs, define pair types
- representing the record types
-
-
-
-
- Person := { name : String }
- Student := { name : String ;
- gpa : Float }
- Employee := { name : String ;
- ssn : Integer }
-
-
-
-
-
-
-
-
-DefinitionPerson : ty :=
-(* FILL IN HERE *)admit.
-DefinitionStudent : ty :=
-(* FILL IN HERE *)admit.
-DefinitionEmployee : ty :=
-(* FILL IN HERE *)admit.
-
-
-Examplesub_student_person :
- Student <: Person.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-
-Examplesub_employee_person :
- Employee <: Person.
-Proof.
-(* FILL IN HERE *)Admitted.
-
-The following facts are mostly easy to prove in Coq. To get
- full benefit from the exercises, make sure you also
- understand how to prove them on paper!
-
-
-
Exercise: 1 star, optional (subtyping_example_1)
-
-
-
-Examplesubtyping_example_1 :
- (TArrowTTopStudent) <: (TArrow (TArrowCC) Person).
- (* Top->Student <: (C->C)->Person *)
-Proofwitheauto.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (subtyping_example_2)
-
-
-
-Examplesubtyping_example_2 :
- (TArrowTTopPerson) <: (TArrowPersonTTop).
- (* Top->Person <: Person->Top *)
-Proofwitheauto.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-EndExamples.
-
-
-
-
-
-
Typing
-
-
-
- The only change to the typing relation is the addition of the rule
- of subsumption, T_Sub.
-
-Do the following exercises after you have added product types to
- the language. For each informal typing judgement, write it as a
- formal statement in Coq and prove it.
-
-
-
Exercise: 1 star, optional (typing_example_0)
-
-
-
-(* empty |- ((\z:A.z), (\z:B.z))
- : (A->A * B->B) *)
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (typing_example_1)
-
-
-
-(* empty |- (\x:(Top * B->B). x.snd) ((\z:A.z), (\z:B.z))
- : B->B *)
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
Exercise: 2 stars, optional (typing_example_2)
-
-
-
-(* empty |- (\z:(C->C)->(Top * B->B). (z (\x:C.x)).snd)
- (\z:C->C. ((\z:A.z), (\z:B.z)))
- : B->B *)
-(* FILL IN HERE *)
-
-
-
-☐
-
-
-
-
-EndExamples2.
-
-
-
-
-
-
Properties
-
-
-
- The fundamental properties of the system that we want to check are
- the same as always: progress and preservation. Unlike the
- extension of the STLC with references, we don't need to change the
- statements of these properties to take subtyping into account.
- However, their proofs do become a little bit more involved.
-
-
-
-
-
-
-
-
Inversion Lemmas for Subtyping
-
-
-
- Before we look at the properties of the typing relation, we need
- to record a couple of critical structural properties of the subtype
- relation:
-
-
-
-
-
Bool is the only subtype of Bool
-
-
-
every subtype of an arrow type is itself an arrow type.
-
-
-
-
-
- These are called inversion lemmas because they play the same
- role in later proofs as the built-in inversion tactic: given a
- hypothesis that there exists a derivation of some subtyping
- statement S<:T and some constraints on the shape of S and/or
- T, each one reasons about what this derivation must look like to
- tell us something further about the shapes of S and T and the
- existence of subtype relations between their parts.
-
-
-
Exercise: 2 stars, optional (sub_inversion_Bool)
-
-
-
-Lemmasub_inversion_Bool : ∀U,
- U <: TBool→
- U = TBool.
-Proofwithauto.
- introsUHs.
- rememberTBoolasV.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-
Exercise: 3 stars, optional (sub_inversion_arrow)
-
-
-
-Lemmasub_inversion_arrow : ∀UV1V2,
- U <: (TArrowV1V2) →
- ∃U1, ∃U2,
- U = (TArrowU1U2) ∧ (V1 <: U1) ∧ (U2 <: V2).
-Proofwitheauto.
- introsUV1V2Hs.
- remember (TArrowV1V2) asV.
- generalizedependentV2. generalizedependentV1.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-☐
-
-
-
-
-
-
-
-
Canonical Forms
-
-
-
- We'll see first that the proof of the progress theorem doesn't
- change too much — we just need one small refinement. When we're
- considering the case where the term in question is an application
- t1t2 where both t1 and t2 are values, we need to know that
- t1 has the form of a lambda-abstraction, so that we can apply
- the ST_AppAbs reduction rule. In the ordinary STLC, this is
- obvious: we know that t1 has a function type T11→T12, and
- there is only one rule that can be used to give a function type to
- a value — rule T_Abs — and the form of the conclusion of this
- rule forces t1 to be an abstraction.
-
-
-
- In the STLC with subtyping, this reasoning doesn't quite work
- because there's another rule that can be used to show that a value
- has a function type: subsumption. Fortunately, this possibility
- doesn't change things much: if the last rule used to show Γ
- ⊢t1:T11→T12 is subsumption, then there is some
- sub-derivation whose subject is also t1, and we can reason by
- induction until we finally bottom out at a use of T_Abs.
-
-
-
- This bit of reasoning is packaged up in the following lemma, which
- tells us the possible "canonical forms" (i.e. values) of function
- type.
-
-
- The proof of progress proceeds like the one for the pure
- STLC, except that in several places we invoke canonical forms
- lemmas...
-
-
- Theorem (Progress): For any term t and type T, if empty⊢
- t:T then t is a value or t⇒t' for some term t'.
-
-
-
- Proof: Let t and T be given, with empty⊢t:T. Proceed
- by induction on the typing derivation.
-
-
-
- The cases for T_Abs, T_Unit, T_True and T_False are
- immediate because abstractions, unit, true, and false are
- already values. The T_Var case is vacuous because variables
- cannot be typed in the empty context. The remaining cases are
- more interesting:
-
-
-
-
-
If the last step in the typing derivation uses rule T_App,
- then there are terms t1t2 and types T1 and T2 such that
- t=t1t2, T=T2, empty⊢t1:T1→T2, and empty⊢
- t2:T1. Moreover, by the induction hypothesis, either t1 is
- a value or it steps, and either t2 is a value or it steps.
- There are three possibilities to consider:
-
-
-
-
-
Suppose t1⇒t1' for some term t1'. Then t1t2⇒t1't2
- by ST_App1.
-
-
-
-
-
-
Suppose t1 is a value and t2⇒t2' for some term t2'.
- Then t1t2⇒t1t2' by rule ST_App2 because t1 is a
- value.
-
-
-
-
-
-
Finally, suppose t1 and t2 are both values. By Lemma
- canonical_forms_for_arrow_types, we know that t1 has the
- form \x:S1.s2 for some x, S1, and s2. But then
- (\x:S1.s2)t2⇒[x:=t2]s2 by ST_AppAbs, since t2 is a
- value.
-
-
-
-
-
-
-
-
-
If the final step of the derivation uses rule T_If, then there
- are terms t1, t2, and t3 such that t=ift1thent2else
- t3, with empty⊢t1:Bool and with empty⊢t2:T and
- empty⊢t3:T. Moreover, by the induction hypothesis,
- either t1 is a value or it steps.
-
-
-
-
-
If t1 is a value, then by the canonical forms lemma for
- booleans, either t1=true or t1=false. In either
- case, t can step, using rule ST_IfTrue or ST_IfFalse.
-
-
-
-
-
-
If t1 can step, then so can t, by rule ST_If.
-
-
-
-
-
-
-
-
-
If the final step of the derivation is by T_Sub, then there is
- a type S such that S<:T and empty⊢t:S. The desired
- result is exactly the induction hypothesis for the typing
- subderivation.
-
-
-Proofwitheauto.
- introstTHt.
- rememberemptyasΓ.
- revertHeqGamma.
- has_type_cases (inductionHt) Case;
- introsHeqGamma; subst...
- Case "T_Var".
- inversionH.
- Case "T_App".
- right.
- destructIHHt1; subst...
- SCase "t1 is a value".
- destructIHHt2; subst...
- SSCase "t2 is a value".
- destruct (canonical_forms_of_arrow_typesemptyt1T1T2)
- as [x [S1 [t12Heqt1]]]...
- subst. ∃([x:=t2]t12)...
- SSCase "t2 steps".
- inversionH0as [t2'Hstp]. ∃(tappt1t2')...
- SCase "t1 steps".
- inversionHas [t1'Hstp]. ∃(tappt1't2)...
- Case "T_If".
- right.
- destructIHHt1.
- SCase "t1 is a value"...
- assert (t1 = ttrue∨t1 = tfalse)
- by (eapplycanonical_forms_of_Bool; eauto).
- inversionH0; subst...
- inversionH. renamexintot1'. eauto.
-
-
-Qed.
-
-
-
-
-
-
-
Inversion Lemmas for Typing
-
-
-
- The proof of the preservation theorem also becomes a little more
- complex with the addition of subtyping. The reason is that, as
- with the "inversion lemmas for subtyping" above, there are a
- number of facts about the typing relation that are "obvious from
- the definition" in the pure STLC (and hence can be obtained
- directly from the inversion tactic) but that require real proofs
- in the presence of subtyping because there are multiple ways to
- derive the same has_type statement.
-
-
-
- The following "inversion lemma" tells us that, if we have a
- derivation of some typing statement Γ⊢\x:S1.t2:T whose
- subject is an abstraction, then there must be some subderivation
- giving a type to the body t2.
-
-
- Lemma: If Γ⊢\x:S1.t2:T, then there is a type S2
- such that Γ,x:S1⊢t2:S2 and S1→S2<:T.
-
-
-
- (Notice that the lemma does not say, "then T itself is an arrow
- type" — this is tempting, but false!)
-
-
-
- Proof: Let Γ, x, S1, t2 and T be given as
- described. Proceed by induction on the derivation of Γ⊢
- \x:S1.t2:T. Cases T_Var, T_App, are vacuous as those
- rules cannot be used to give a type to a syntactic abstraction.
-
-
-
-
-
If the last step of the derivation is a use of T_Abs then
- there is a type T12 such that T=S1→T12 and Γ,
- x:S1⊢t2:T12. Picking T12 for S2 gives us what we
- need: S1→T12<:S1→T12 follows from S_Refl.
-
-
-
-
-
-
If the last step of the derivation is a use of T_Sub then
- there is a type S such that S<:T and Γ⊢\x:S1.t2:
- S. The IH for the typing subderivation tell us that there is
- some type S2 with S1→S2<:S and Γ,x:S1⊢t2:
- S2. Picking type S2 gives us what we need, since S1→S2
- <:T then follows by S_Trans.
-
-The inversion lemmas for typing and for subtyping between arrow
- types can be packaged up as a useful "combination lemma" telling
- us exactly what we'll actually require below.
-
-
- The substitution lemma is proved along the same lines as
- for the pure STLC. The only significant change is that there are
- several places where, instead of the built-in inversion tactic,
- we need to use the inversion lemmas that we proved above to
- extract structural information from assumptions about the
- well-typedness of subterms.
-
-
- The proof of preservation now proceeds pretty much as in earlier
- chapters, using the substitution lemma at the appropriate point
- and again using inversion lemmas from above to extract structural
- information from typing assumptions.
-
-
- Theorem (Preservation): If t, t' are terms and T is a type
- such that empty⊢t:T and t⇒t', then empty⊢t':
- T.
-
-
-
- Proof: Let t and T be given such that empty⊢t:T. We
- proceed by induction on the structure of this typing derivation,
- leaving t' general. The cases T_Abs, T_Unit, T_True, and
- T_False cases are vacuous because abstractions and constants
- don't step. Case T_Var is vacuous as well, since the context is
- empty.
-
-
-
-
-
If the final step of the derivation is by T_App, then there
- are terms t1 and t2 and types T1 and T2 such that
- t=t1t2, T=T2, empty⊢t1:T1→T2, and
- empty⊢t2:T1.
-
-
-
- By the definition of the step relation, there are three ways
- t1t2 can step. Cases ST_App1 and ST_App2 follow
- immediately by the induction hypotheses for the typing
- subderivations and a use of T_App.
-
-
-
- Suppose instead t1t2 steps by ST_AppAbs. Then t1=
- \x:S.t12 for some type S and term t12, and t'=
- [x:=t2]t12.
-
-
-
- By lemma abs_arrow, we have T1<:S and x:S1⊢s2:T2.
- It then follows by the substitution lemma
- (substitution_preserves_typing) that empty⊢[x:=t2]
- t12:T2 as desired.
-
-
-
-
-
If the final step of the derivation uses rule T_If, then
- there are terms t1, t2, and t3 such that t=ift1then
- t2elset3, with empty⊢t1:Bool and with empty⊢t2:
- T and empty⊢t3:T. Moreover, by the induction
- hypothesis, if t1 steps to t1' then empty⊢t1':Bool.
- There are three cases to consider, depending on which rule was
- used to show t⇒t'.
-
-
-
-
-
If t⇒t' by rule ST_If, then t'=ift1'thent2
- elset3 with t1⇒t1'. By the induction hypothesis,
- empty⊢t1':Bool, and so empty⊢t':T by T_If.
-
-
-
-
-
-
If t⇒t' by rule ST_IfTrue or ST_IfFalse, then
- either t'=t2 or t'=t3, and empty⊢t':T
- follows by assumption.
-
-
-
-
-
-
-
-
-
-
-
-
If the final step of the derivation is by T_Sub, then there
- is a type S such that S<:T and empty⊢t:S. The
- result is immediate by the induction hypothesis for the typing
- subderivation and an application of T_Sub. ☐
-
-
- This formalization of the STLC with subtyping has omitted record
- types, for brevity. If we want to deal with them more seriously,
- we have two choices.
-
-
-
- First, we can treat them as part of the core language, writing
- down proper syntax, typing, and subtyping rules for them. Chapter
- RecordSub shows how this extension works.
-
-
-
- On the other hand, if we are treating them as a derived form that
- is desugared in the parser, then we shouldn't need any new rules:
- we should just check that the existing rules for subtyping product
- and Unit types give rise to reasonable rules for record
- subtyping via this encoding. To do this, we just need to make one
- small change to the encoding described earlier: instead of using
- Unit as the base case in the encoding of tuples and the "don't
- care" placeholder in the encoding of records, we use Top. So:
-
- {a:Nat, b:Nat} ----> {Nat,Nat} i.e. (Nat,(Nat,Top))
- {c:Nat, a:Nat} ----> {Nat,Top,Nat} i.e. (Nat,(Top,(Nat,Top)))
-
- The encoding of record values doesn't change at all. It is
- easy (and instructive) to check that the subtyping rules above are
- validated by the encoding. For the rest of this chapter, we'll
- follow this encoding-based approach.
-
-
-
-
-
-
-
-
Exercises
-
-
-
-
Exercise: 2 stars (variations)
- Each part of this problem suggests a different way of
- changing the definition of the STLC with Unit and
- subtyping. (These changes are not cumulative: each part
- starts from the original language.) In each part, list which
- properties (Progress, Preservation, both, or neither) become
- false. If a property becomes false, give a counterexample.
-
-
-
-
-
Suppose we add the following typing rule:
-
-
-
Γ⊢ t : S1->S2
-
-
-
-
S1 <: T1 T1 <: S1 S2 <: T2
-
- (T_Funny1)
-
-
-
-
-
-
Γ⊢ t : T1->T2
-
-
-
-
-
-
-
-
Suppose we add the following reduction rule:
-
-
-
-
- (ST_Funny21)
-
-
-
-
-
-
unit ⇒ (\x:Top. x)
-
-
-
-
-
-
-
-
Suppose we add the following subtyping rule:
-
-
-
-
- (S_Funny3)
-
-
-
-
-
-
Unit <: Top->Top
-
-
-
-
-
-
-
-
Suppose we add the following subtyping rule:
-
-
-
-
- (S_Funny4)
-
-
-
-
-
-
Top->Top <: Unit
-
-
-
-
-
-
-
-
Suppose we add the following evaluation rule:
-
-
-
-
- (ST_Funny5)
-
-
-
-
-
-
(unit t) ⇒ (t unit)
-
-
-
-
-
-
-
-
Suppose we add the same evaluation rule and a new typing rule:
-
-
-
-
- (ST_Funny5)
-
-
-
-
-
-
(unit t) ⇒ (t unit)
-
-
-
-
-
-
- (T_Funny6)
-
-
-
-
-
-
empty ⊢ Unit : Top->Top
-
-
-
-
-
-
-
-
Suppose we change the arrow subtyping rule to:
-
-
-
S1 <: T1 S2 <: T2
-
- (S_Arrow')
-
-
-
-
-
-
S1->S2 <: T1->T2
-
-
-
-
-
-
-
-
-☐
-
-
-
-
-
-
-
-
-
Exercise: Adding Products
-
-
-
-
Exercise: 4 stars, optional (products)
- Adding pairs, projections, and product types to the system we have
- defined is a relatively straightforward matter. Carry out this
- extension:
-
-
-
-
-
Add constructors for pairs, first and second projections, and
- product types to the definitions of ty and tm. (Don't
- forget to add corresponding cases to T_cases and t_cases.)
-
-
-
-
-
-
Extend the well-formedness relation in the obvious way.
-
-
-
-
-
-
Extend the operational semantics with the same reduction rules
- as in the last chapter.
-
-
-
-
-
-
Extend the subtyping relation with this rule:
-
-
-
S1 <: T1 S2 <: T2
-
- (Sub_Prod)
-
-
-
-
-
-
S1 × S2 <: T1 × T2
-
-
-
-
-
Extend the typing relation with the same rules for pairs and
- projections as in the last chapter.
-
-
-
-
-
-
Extend the proofs of progress, preservation, and all their
- supporting lemmas to deal with the new constructs. (You'll also
- need to add some completely new lemmas.) ☐
-
-
-
-
-
\ No newline at end of file
diff --git a/Sub.v b/Sub.v
deleted file mode 100644
index 50dff1e..0000000
--- a/Sub.v
+++ /dev/null
@@ -1,1584 +0,0 @@
-(** * Sub: Subtyping *)
-
-
-Require Export MoreStlc.
-
-(* ###################################################### *)
-(** * Concepts *)
-
-(** We now turn to the study of _subtyping_, perhaps the most
- characteristic feature of the static type systems of recently
- designed programming languages and a key feature needed to support
- the object-oriented programming style. *)
-
-(* ###################################################### *)
-(** ** A Motivating Example *)
-
-(** Suppose we are writing a program involving two record types
- defined as follows:
-<<
- Person = {name:String, age:Nat}
- Student = {name:String, age:Nat, gpa:Nat}
->>
-*)
-
-(** In the simply typed lamdba-calculus with records, the term
-<<
- (\r:Person. (r.age)+1) {name="Pat",age=21,gpa=1}
->>
- is not typable: it involves an application of a function that wants
- a one-field record to an argument that actually provides two
- fields, while the [T_App] rule demands that the domain type of the
- function being applied must match the type of the argument
- precisely.
-
- But this is silly: we're passing the function a _better_ argument
- than it needs! The only thing the body of the function can
- possibly do with its record argument [r] is project the field [age]
- from it: nothing else is allowed by the type, and the presence or
- absence of an extra [gpa] field makes no difference at all. So,
- intuitively, it seems that this function should be applicable to
- any record value that has at least an [age] field.
-
- Looking at the same thing from another point of view, a record with
- more fields is "at least as good in any context" as one with just a
- subset of these fields, in the sense that any value belonging to
- the longer record type can be used _safely_ in any context
- expecting the shorter record type. If the context expects
- something with the shorter type but we actually give it something
- with the longer type, nothing bad will happen (formally, the
- program will not get stuck).
-
- The general principle at work here is called _subtyping_. We say
- that "[S] is a subtype of [T]", informally written [S <: T], if a
- value of type [S] can safely be used in any context where a value
- of type [T] is expected. The idea of subtyping applies not only to
- records, but to all of the type constructors in the language --
- functions, pairs, etc. *)
-
-(** ** Subtyping and Object-Oriented Languages *)
-
-(** Subtyping plays a fundamental role in many programming
- languages -- in particular, it is closely related to the notion of
- _subclassing_ in object-oriented languages.
-
- An _object_ in Java, C[#], etc. can be thought of as a record,
- some of whose fields are functions ("methods") and some of whose
- fields are data values ("fields" or "instance variables").
- Invoking a method [m] of an object [o] on some arguments [a1..an]
- consists of projecting out the [m] field of [o] and applying it to
- [a1..an].
-
- The type of an object can be given as either a _class_ or an
- _interface_. Both of these provide a description of which methods
- and which data fields the object offers.
-
- Classes and interfaces are related by the _subclass_ and
- _subinterface_ relations. An object belonging to a subclass (or
- subinterface) is required to provide all the methods and fields of
- one belonging to a superclass (or superinterface), plus possibly
- some more.
-
- The fact that an object from a subclass (or sub-interface) can be
- used in place of one from a superclass (or super-interface)
- provides a degree of flexibility that is is extremely handy for
- organizing complex libraries. For example, a GUI toolkit like
- Java's Swing framework might define an abstract interface
- [Component] that collects together the common fields and methods
- of all objects having a graphical representation that can be
- displayed on the screen and that can interact with the user.
- Examples of such object would include the buttons, checkboxes, and
- scrollbars of a typical GUI. A method that relies only on this
- common interface can now be applied to any of these objects.
-
- Of course, real object-oriented languages include many other
- features besides these. For example, fields can be updated.
- Fields and methods can be declared [private]. Classes also give
- _code_ that is used when constructing objects and implementing
- their methods, and the code in subclasses cooperate with code in
- superclasses via _inheritance_. Classes can have static methods
- and fields, initializers, etc., etc.
-
- To keep things simple here, we won't deal with any of these
- issues -- in fact, we won't even talk any more about objects or
- classes. (There is a lot of discussion in _Types and Programming
- Languages_, if you are interested.) Instead, we'll study the core
- concepts behind the subclass / subinterface relation in the
- simplified setting of the STLC. *)
-
-(** *** *)
-(** Of course, real OO languages have lots of other features...
- - mutable fields
- - [private] and other visibility modifiers
- - method inheritance
- - static components
- - etc., etc.
-
- We'll ignore all these and focus on core mechanisms. *)
-
-(** ** The Subsumption Rule *)
-
-(** Our goal for this chapter is to add subtyping to the simply typed
- lambda-calculus (with some of the basic extensions from [MoreStlc]).
- This involves two steps:
-
- - Defining a binary _subtype relation_ between types.
-
- - Enriching the typing relation to take subtyping into account.
-
- The second step is actually very simple. We add just a single rule
- to the typing relation: the so-called _rule of subsumption_:
- Gamma |- t : S S <: T
- ------------------------- (T_Sub)
- Gamma |- t : T
- This rule says, intuitively, that it is OK to "forget" some of
- what we know about a term. *)
-(** For example, we may know that [t] is a record with two
- fields (e.g., [S = {x:A->A, y:B->B}]), but choose to forget about
- one of the fields ([T = {y:B->B}]) so that we can pass [t] to a
- function that requires just a single-field record. *)
-
-(** ** The Subtype Relation *)
-
-(** The first step -- the definition of the relation [S <: T] -- is
- where all the action is. Let's look at each of the clauses of its
- definition. *)
-
-(** *** Structural Rules *)
-
-(** To start off, we impose two "structural rules" that are
- independent of any particular type constructor: a rule of
- _transitivity_, which says intuitively that, if [S] is better than
- [U] and [U] is better than [T], then [S] is better than [T]...
- S <: U U <: T
- ---------------- (S_Trans)
- S <: T
- ... and a rule of _reflexivity_, since certainly any type [T] is
- as good as itself:
- ------ (S_Refl)
- T <: T
-*)
-
-(** *** Products *)
-
-(** Now we consider the individual type constructors, one by one,
- beginning with product types. We consider one pair to be "better
- than" another if each of its components is.
- S1 <: T1 S2 <: T2
- -------------------- (S_Prod)
- S1 * S2 <: T1 * T2
-*)
-
-(** *** Arrows *)
-
-(** Suppose we have two functions [f] and [g] with these types:
- f : C -> Student
- g : (C->Person) -> D
- That is, [f] is a function that yields a record of type [Student],
- and [g] is a (higher-order) function that expects its (function)
- argument to yield a record of type [Person]. Also suppose, even
- though we haven't yet discussed subtyping for records, that
- [Student] is a subtype of [Person]. Then the application [g f] is
- safe even though their types do not match up precisely, because
- the only thing [g] can do with [f] is to apply it to some
- argument (of type [C]); the result will actually be a [Student],
- while [g] will be expecting a [Person], but this is safe because
- the only thing [g] can then do is to project out the two fields
- that it knows about ([name] and [age]), and these will certainly
- be among the fields that are present.
-
- This example suggests that the subtyping rule for arrow types
- should say that two arrow types are in the subtype relation if
- their results are:
- S2 <: T2
- ---------------- (S_Arrow_Co)
- S1 -> S2 <: S1 -> T2
- We can generalize this to allow the arguments of the two arrow
- types to be in the subtype relation as well:
- T1 <: S1 S2 <: T2
- -------------------- (S_Arrow)
- S1 -> S2 <: T1 -> T2
- Notice that the argument types are subtypes "the other way round":
- in order to conclude that [S1->S2] to be a subtype of [T1->T2], it
- must be the case that [T1] is a subtype of [S1]. The arrow
- constructor is said to be _contravariant_ in its first argument
- and _covariant_ in its second.
-
- Here is an example that illustrates this:
- f : Person -> C
- g : (Student -> C) -> D
- The application [g f] is safe, because the only thing the body of
- [g] can do with [f] is to apply it to some argument of type
- [Student]. Since [f] requires records having (at least) the
- fields of a [Person], this will always work. So [Person -> C] is a
- subtype of [Student -> C] since [Student] is a subtype of
- [Person].
-
- The intuition is that, if we have a function [f] of type [S1->S2],
- then we know that [f] accepts elements of type [S1]; clearly, [f]
- will also accept elements of any subtype [T1] of [S1]. The type of
- [f] also tells us that it returns elements of type [S2]; we can
- also view these results belonging to any supertype [T2] of
- [S2]. That is, any function [f] of type [S1->S2] can also be
- viewed as having type [T1->T2].
-*)
-
-(** *** Records *)
-
-(** What about subtyping for record types? *)
-
-(** The basic intuition about subtyping for record types is that it is
- always safe to use a "bigger" record in place of a "smaller" one.
- That is, given a record type, adding extra fields will always
- result in a subtype. If some code is expecting a record with
- fields [x] and [y], it is perfectly safe for it to receive a record
- with fields [x], [y], and [z]; the [z] field will simply be ignored.
- For example,
- {name:String, age:Nat, gpa:Nat} <: {name:String, age:Nat}
- {name:String, age:Nat} <: {name:String}
- {name:String} <: {}
- This is known as "width subtyping" for records. *)
-
-(** We can also create a subtype of a record type by replacing the type
- of one of its fields with a subtype. If some code is expecting a
- record with a field [x] of type [T], it will be happy with a record
- having a field [x] of type [S] as long as [S] is a subtype of
- [T]. For example,
- {x:Student} <: {x:Person}
- This is known as "depth subtyping". *)
-
-(** Finally, although the fields of a record type are written in a
- particular order, the order does not really matter. For example,
- {name:String,age:Nat} <: {age:Nat,name:String}
- This is known as "permutation subtyping". *)
-
-(** We could formalize these requirements in a single subtyping rule
- for records as follows:
- for each jk in j1..jn,
- exists ip in i1..im, such that
- jk=ip and Sp <: Tk
- ---------------------------------- (S_Rcd)
- {i1:S1...im:Sm} <: {j1:T1...jn:Tn}
- That is, the record on the left should have all the field labels of
- the one on the right (and possibly more), while the types of the
- common fields should be in the subtype relation. However, this rule
- is rather heavy and hard to read. If we like, we can decompose it
- into three simpler rules, which can be combined using [S_Trans] to
- achieve all the same effects. *)
-
-(** First, adding fields to the end of a record type gives a subtype:
- n > m
- --------------------------------- (S_RcdWidth)
- {i1:T1...in:Tn} <: {i1:T1...im:Tm}
- We can use [S_RcdWidth] to drop later fields of a multi-field
- record while keeping earlier fields, showing for example that
- [{age:Nat,name:String} <: {name:String}]. *)
-
-(** Second, we can apply subtyping inside the components of a compound
- record type:
- S1 <: T1 ... Sn <: Tn
- ---------------------------------- (S_RcdDepth)
- {i1:S1...in:Sn} <: {i1:T1...in:Tn}
- For example, we can use [S_RcdDepth] and [S_RcdWidth] together to
- show that [{y:Student, x:Nat} <: {y:Person}]. *)
-
-(** Third, we need to be able to reorder fields. For example, we
- might expect that [{name:String, gpa:Nat, age:Nat} <: Person]. We
- haven't quite achieved this yet: using just [S_RcdDepth] and
- [S_RcdWidth] we can only drop fields from the _end_ of a record
- type. So we need:
- {i1:S1...in:Sn} is a permutation of {i1:T1...in:Tn}
- --------------------------------------------------- (S_RcdPerm)
- {i1:S1...in:Sn} <: {i1:T1...in:Tn}
-*)
-
-(** It is worth noting that full-blown language designs may choose not
- to adopt all of these subtyping rules. For example, in Java:
-
- - A subclass may not change the argument or result types of a
- method of its superclass (i.e., no depth subtyping or no arrow
- subtyping, depending how you look at it).
-
- - Each class has just one superclass ("single inheritance" of
- classes).
-
- - Each class member (field or method) can be assigned a single
- index, adding new indices "on the right" as more members are
- added in subclasses (i.e., no permutation for classes).
-
- - A class may implement multiple interfaces -- so-called "multiple
- inheritance" of interfaces (i.e., permutation is allowed for
- interfaces). *)
-
-(** **** Exercise: 2 stars (arrow_sub_wrong) *)
-(** Suppose we had incorrectly defined subtyping as covariant on both
- the right and the left of arrow types:
- S1 <: T1 S2 <: T2
- -------------------- (S_Arrow_wrong)
- S1 -> S2 <: T1 -> T2
- Give a concrete example of functions [f] and [g] with the following types...
- f : Student -> Nat
- g : (Person -> Nat) -> Nat
- ... such that the application [g f] will get stuck during
- execution.
-
-[]
-*)
-
-(** *** Top *)
-
-(** Finally, it is natural to give the subtype relation a maximal
- element -- a type that lies above every other type and is
- inhabited by all (well-typed) values. We do this by adding to the
- language one new type constant, called [Top], together with a
- subtyping rule that places it above every other type in the
- subtype relation:
- -------- (S_Top)
- S <: Top
- The [Top] type is an analog of the [Object] type in Java and C[#]. *)
-
-(* ############################################### *)
-(** *** Summary *)
-
-(** In summary, we form the STLC with subtyping by starting with the
- pure STLC (over some set of base types) and...
-
- - adding a base type [Top],
-
- - adding the rule of subsumption
- Gamma |- t : S S <: T
- ------------------------- (T_Sub)
- Gamma |- t : T
- to the typing relation, and
-
- - defining a subtype relation as follows:
- S <: U U <: T
- ---------------- (S_Trans)
- S <: T
-
- ------ (S_Refl)
- T <: T
-
- -------- (S_Top)
- S <: Top
-
- S1 <: T1 S2 <: T2
- -------------------- (S_Prod)
- S1 * S2 <: T1 * T2
-
- T1 <: S1 S2 <: T2
- -------------------- (S_Arrow)
- S1 -> S2 <: T1 -> T2
-
- n > m
- --------------------------------- (S_RcdWidth)
- {i1:T1...in:Tn} <: {i1:T1...im:Tm}
-
- S1 <: T1 ... Sn <: Tn
- ---------------------------------- (S_RcdDepth)
- {i1:S1...in:Sn} <: {i1:T1...in:Tn}
-
- {i1:S1...in:Sn} is a permutation of {i1:T1...in:Tn}
- --------------------------------------------------- (S_RcdPerm)
- {i1:S1...in:Sn} <: {i1:T1...in:Tn}
-*)
-
-
-
-(* ############################################### *)
-(** ** Exercises *)
-
-(** **** Exercise: 1 star, optional (subtype_instances_tf_1) *)
-(** Suppose we have types [S], [T], [U], and [V] with [S <: T]
- and [U <: V]. Which of the following subtyping assertions
- are then true? Write _true_ or _false_ after each one.
- ([A], [B], and [C] here are base types.)
-
- - [T->S <: T->S]
-
- - [Top->U <: S->Top]
-
- - [(C->C) -> (A*B) <: (C->C) -> (Top*B)]
-
- - [T->T->U <: S->S->V]
-
- - [(T->T)->U <: (S->S)->V]
-
- - [((T->S)->T)->U <: ((S->T)->S)->V]
-
- - [S*V <: T*U]
-
-[]
-*)
-
-(** **** Exercise: 2 stars (subtype_order) *)
-(** The following types happen to form a linear order with respect to subtyping:
- - [Top]
- - [Top -> Student]
- - [Student -> Person]
- - [Student -> Top]
- - [Person -> Student]
-
-Write these types in order from the most specific to the most general.
-
-
-Where does the type [Top->Top->Student] fit into this order?
-
-
-*)
-
-(** **** Exercise: 1 star (subtype_instances_tf_2) *)
-(** Which of the following statements are true? Write _true_ or
- _false_ after each one.
- forall S T,
- S <: T ->
- S->S <: T->T
-
- forall S,
- S <: A->A ->
- exists T,
- S = T->T /\ T <: A
-
- forall S T1 T2,
- (S <: T1 -> T2) ->
- exists S1 S2,
- S = S1 -> S2 /\ T1 <: S1 /\ S2 <: T2
-
- exists S,
- S <: S->S
-
- exists S,
- S->S <: S
-
- forall S T1 T2,
- S <: T1*T2 ->
- exists S1 S2,
- S = S1*S2 /\ S1 <: T1 /\ S2 <: T2
-[] *)
-
-(** **** Exercise: 1 star (subtype_concepts_tf) *)
-(** Which of the following statements are true, and which are false?
- - There exists a type that is a supertype of every other type.
-
- - There exists a type that is a subtype of every other type.
-
- - There exists a pair type that is a supertype of every other
- pair type.
-
- - There exists a pair type that is a subtype of every other
- pair type.
-
- - There exists an arrow type that is a supertype of every other
- arrow type.
-
- - There exists an arrow type that is a subtype of every other
- arrow type.
-
- - There is an infinite descending chain of distinct types in the
- subtype relation---that is, an infinite sequence of types
- [S0], [S1], etc., such that all the [Si]'s are different and
- each [S(i+1)] is a subtype of [Si].
-
- - There is an infinite _ascending_ chain of distinct types in
- the subtype relation---that is, an infinite sequence of types
- [S0], [S1], etc., such that all the [Si]'s are different and
- each [S(i+1)] is a supertype of [Si].
-
-[]
-*)
-
-(** **** Exercise: 2 stars (proper_subtypes) *)
-(** Is the following statement true or false? Briefly explain your
- answer.
- forall T,
- ~(exists n, T = TBase n) ->
- exists S,
- S <: T /\ S <> T
-]]
-[]
-*)
-
-(** **** Exercise: 2 stars (small_large_1) *)
-(**
- - What is the _smallest_ type [T] ("smallest" in the subtype
- relation) that makes the following assertion true? (Assume we
- have [Unit] among the base types and [unit] as a constant of this
- type.)
- empty |- (\p:T*Top. p.fst) ((\z:A.z), unit) : A->A
-
- - What is the _largest_ type [T] that makes the same assertion true?
-
-[]
-*)
-
-(** **** Exercise: 2 stars (small_large_2) *)
-(**
- - What is the _smallest_ type [T] that makes the following
- assertion true?
- empty |- (\p:(A->A * B->B). p) ((\z:A.z), (\z:B.z)) : T
-
- - What is the _largest_ type [T] that makes the same assertion true?
-
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (small_large_3) *)
-(**
- - What is the _smallest_ type [T] that makes the following
- assertion true?
- a:A |- (\p:(A*T). (p.snd) (p.fst)) (a , \z:A.z) : A
-
- - What is the _largest_ type [T] that makes the same assertion true?
-
-[]
-*)
-
-
-
-
-(** **** Exercise: 2 stars (small_large_4) *)
-(**
- - What is the _smallest_ type [T] that makes the following
- assertion true?
- exists S,
- empty |- (\p:(A*T). (p.snd) (p.fst)) : S
-
- - What is the _largest_ type [T] that makes the same
- assertion true?
-
-[]
-*)
-
-(** **** Exercise: 2 stars (smallest_1) *)
-(** What is the _smallest_ type [T] that makes the following
- assertion true?
- exists S, exists t,
- empty |- (\x:T. x x) t : S
-]]
-[]
-*)
-
-(** **** Exercise: 2 stars (smallest_2) *)
-(** What is the _smallest_ type [T] that makes the following
- assertion true?
- empty |- (\x:Top. x) ((\z:A.z) , (\z:B.z)) : T
-]]
-[]
-*)
-
-(** **** Exercise: 3 stars, optional (count_supertypes) *)
-(** How many supertypes does the record type [{x:A, y:C->C}] have? That is,
- how many different types [T] are there such that [{x:A, y:C->C} <:
- T]? (We consider two types to be different if they are written
- differently, even if each is a subtype of the other. For example,
- [{x:A,y:B}] and [{y:B,x:A}] are different.)
-
-
-[]
-*)
-
-(** **** Exercise: 2 stars (pair_permutation) *)
-(** The subtyping rule for product types
- S1 <: T1 S2 <: T2
- -------------------- (S_Prod)
- S1*S2 <: T1*T2
-intuitively corresponds to the "depth" subtyping rule for records. Extending the analogy, we might consider adding a "permutation" rule
- --------------
- T1*T2 <: T2*T1
-for products.
-Is this a good idea? Briefly explain why or why not.
-
-[]
-*)
-
-(* ###################################################### *)
-(** * Formal Definitions *)
-
-(** Most of the definitions -- in particular, the syntax and
- operational semantics of the language -- are identical to what we
- saw in the last chapter. We just need to extend the typing
- relation with the subsumption rule and add a new [Inductive]
- definition for the subtyping relation. Let's first do the
- identical bits. *)
-
-(* ###################################################### *)
-(** ** Core Definitions *)
-
-(* ################################### *)
-(** *** Syntax *)
-
-(** For the sake of more interesting examples below, we'll allow an
- arbitrary set of additional base types like [String], [Float],
- etc. We won't bother adding any constants belonging to these
- types or any operators on them, but we could easily do so. *)
-
-(** In the rest of the chapter, we formalize just base types,
- booleans, arrow types, [Unit], and [Top], omitting record types
- and leaving product types as an exercise. *)
-
-Inductive ty : Type :=
- | TTop : ty
- | TBool : ty
- | TBase : id -> ty
- | TArrow : ty -> ty -> ty
- | TUnit : ty
-.
-
-Tactic Notation "T_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "TTop" | Case_aux c "TBool"
- | Case_aux c "TBase" | Case_aux c "TArrow"
- | Case_aux c "TUnit" |
- ].
-
-Inductive tm : Type :=
- | tvar : id -> tm
- | tapp : tm -> tm -> tm
- | tabs : id -> ty -> tm -> tm
- | ttrue : tm
- | tfalse : tm
- | tif : tm -> tm -> tm -> tm
- | tunit : tm
-.
-
-Tactic Notation "t_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "tvar" | Case_aux c "tapp"
- | Case_aux c "tabs" | Case_aux c "ttrue"
- | Case_aux c "tfalse" | Case_aux c "tif"
- | Case_aux c "tunit"
- ].
-
-(* ################################### *)
-(** *** Substitution *)
-
-(** The definition of substitution remains exactly the same as for the
- pure STLC. *)
-
-Fixpoint subst (x:id) (s:tm) (t:tm) : tm :=
- match t with
- | tvar y =>
- if eq_id_dec x y then s else t
- | tabs y T t1 =>
- tabs y T (if eq_id_dec x y then t1 else (subst x s t1))
- | tapp t1 t2 =>
- tapp (subst x s t1) (subst x s t2)
- | ttrue =>
- ttrue
- | tfalse =>
- tfalse
- | tif t1 t2 t3 =>
- tif (subst x s t1) (subst x s t2) (subst x s t3)
- | tunit =>
- tunit
- end.
-
-Notation "'[' x ':=' s ']' t" := (subst x s t) (at level 20).
-
-(* ################################### *)
-(** *** Reduction *)
-
-(** Likewise the definitions of the [value] property and the [step]
- relation. *)
-
-Inductive value : tm -> Prop :=
- | v_abs : forall x T t,
- value (tabs x T t)
- | v_true :
- value ttrue
- | v_false :
- value tfalse
- | v_unit :
- value tunit
-.
-
-Hint Constructors value.
-
-Reserved Notation "t1 '==>' t2" (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_AppAbs : forall x T t12 v2,
- value v2 ->
- (tapp (tabs x T t12) v2) ==> [x:=v2]t12
- | ST_App1 : forall t1 t1' t2,
- t1 ==> t1' ->
- (tapp t1 t2) ==> (tapp t1' t2)
- | ST_App2 : forall v1 t2 t2',
- value v1 ->
- t2 ==> t2' ->
- (tapp v1 t2) ==> (tapp v1 t2')
- | ST_IfTrue : forall t1 t2,
- (tif ttrue t1 t2) ==> t1
- | ST_IfFalse : forall t1 t2,
- (tif tfalse t1 t2) ==> t2
- | ST_If : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- (tif t1 t2 t3) ==> (tif t1' t2 t3)
-where "t1 '==>' t2" := (step t1 t2).
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_AppAbs" | Case_aux c "ST_App1"
- | Case_aux c "ST_App2" | Case_aux c "ST_IfTrue"
- | Case_aux c "ST_IfFalse" | Case_aux c "ST_If"
- ].
-
-Hint Constructors step.
-
-(* ###################################################################### *)
-(** ** Subtyping *)
-
-(** Now we come to the most interesting part. We begin by
- defining the subtyping relation and developing some of its
- important technical properties. *)
-
-(** The definition of subtyping is just what we sketched in the
- motivating discussion. *)
-
-Reserved Notation "T '<:' U" (at level 40).
-
-Inductive subtype : ty -> ty -> Prop :=
- | S_Refl : forall T,
- T <: T
- | S_Trans : forall S U T,
- S <: U ->
- U <: T ->
- S <: T
- | S_Top : forall S,
- S <: TTop
- | S_Arrow : forall S1 S2 T1 T2,
- T1 <: S1 ->
- S2 <: T2 ->
- (TArrow S1 S2) <: (TArrow T1 T2)
-where "T '<:' U" := (subtype T U).
-
-(** Note that we don't need any special rules for base types: they are
- automatically subtypes of themselves (by [S_Refl]) and [Top] (by
- [S_Top]), and that's all we want. *)
-
-Hint Constructors subtype.
-
-Tactic Notation "subtype_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "S_Refl" | Case_aux c "S_Trans"
- | Case_aux c "S_Top" | Case_aux c "S_Arrow"
- ].
-
-Module Examples.
-
-Notation x := (Id 0).
-Notation y := (Id 1).
-Notation z := (Id 2).
-
-Notation A := (TBase (Id 6)).
-Notation B := (TBase (Id 7)).
-Notation C := (TBase (Id 8)).
-
-Notation String := (TBase (Id 9)).
-Notation Float := (TBase (Id 10)).
-Notation Integer := (TBase (Id 11)).
-
-(** **** Exercise: 2 stars, optional (subtyping_judgements) *)
-
-(** (Do this exercise after you have added product types to the
- language, at least up to this point in the file).
-
- Using the encoding of records into pairs, define pair types
- representing the record types
- Person := { name : String }
- Student := { name : String ;
- gpa : Float }
- Employee := { name : String ;
- ssn : Integer }
-*)
-
-Definition Person : ty :=
-(* FILL IN HERE *) admit.
-Definition Student : ty :=
-(* FILL IN HERE *) admit.
-Definition Employee : ty :=
-(* FILL IN HERE *) admit.
-
-Example sub_student_person :
- Student <: Person.
-Proof.
-(* FILL IN HERE *) Admitted.
-
-Example sub_employee_person :
- Employee <: Person.
-Proof.
-(* FILL IN HERE *) Admitted.
-(** [] *)
-
-Example subtyping_example_0 :
- (TArrow C Person) <: (TArrow C TTop).
- (* C->Person <: C->Top *)
-Proof.
- apply S_Arrow.
- apply S_Refl. auto.
-Qed.
-
-(** The following facts are mostly easy to prove in Coq. To get
- full benefit from the exercises, make sure you also
- understand how to prove them on paper! *)
-
-(** **** Exercise: 1 star, optional (subtyping_example_1) *)
-Example subtyping_example_1 :
- (TArrow TTop Student) <: (TArrow (TArrow C C) Person).
- (* Top->Student <: (C->C)->Person *)
-Proof with eauto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 1 star, optional (subtyping_example_2) *)
-Example subtyping_example_2 :
- (TArrow TTop Person) <: (TArrow Person TTop).
- (* Top->Person <: Person->Top *)
-Proof with eauto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-End Examples.
-
-
-(* ###################################################################### *)
-(** ** Typing *)
-
-(** The only change to the typing relation is the addition of the rule
- of subsumption, [T_Sub]. *)
-
-Definition context := id -> (option ty).
-Definition empty : context := (fun _ => None).
-Definition extend (Gamma : context) (x:id) (T : ty) :=
- fun x' => if eq_id_dec x x' then Some T else Gamma x'.
-
-Reserved Notation "Gamma '|-' t '\in' T" (at level 40).
-
-Inductive has_type : context -> tm -> ty -> Prop :=
- (* Same as before *)
- | T_Var : forall Gamma x T,
- Gamma x = Some T ->
- Gamma |- (tvar x) \in T
- | T_Abs : forall Gamma x T11 T12 t12,
- (extend Gamma x T11) |- t12 \in T12 ->
- Gamma |- (tabs x T11 t12) \in (TArrow T11 T12)
- | T_App : forall T1 T2 Gamma t1 t2,
- Gamma |- t1 \in (TArrow T1 T2) ->
- Gamma |- t2 \in T1 ->
- Gamma |- (tapp t1 t2) \in T2
- | T_True : forall Gamma,
- Gamma |- ttrue \in TBool
- | T_False : forall Gamma,
- Gamma |- tfalse \in TBool
- | T_If : forall t1 t2 t3 T Gamma,
- Gamma |- t1 \in TBool ->
- Gamma |- t2 \in T ->
- Gamma |- t3 \in T ->
- Gamma |- (tif t1 t2 t3) \in T
- | T_Unit : forall Gamma,
- Gamma |- tunit \in TUnit
- (* New rule of subsumption *)
- | T_Sub : forall Gamma t S T,
- Gamma |- t \in S ->
- S <: T ->
- Gamma |- t \in T
-
-where "Gamma '|-' t '\in' T" := (has_type Gamma t T).
-
-Hint Constructors has_type.
-
-Tactic Notation "has_type_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "T_Var" | Case_aux c "T_Abs"
- | Case_aux c "T_App" | Case_aux c "T_True"
- | Case_aux c "T_False" | Case_aux c "T_If"
- | Case_aux c "T_Unit"
- | Case_aux c "T_Sub" ].
-
-(* ############################################### *)
-(** ** Typing examples *)
-
-Module Examples2.
-Import Examples.
-
-(** Do the following exercises after you have added product types to
- the language. For each informal typing judgement, write it as a
- formal statement in Coq and prove it. *)
-
-(** **** Exercise: 1 star, optional (typing_example_0) *)
-(* empty |- ((\z:A.z), (\z:B.z))
- : (A->A * B->B) *)
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (typing_example_1) *)
-(* empty |- (\x:(Top * B->B). x.snd) ((\z:A.z), (\z:B.z))
- : B->B *)
-(* FILL IN HERE *)
-(** [] *)
-
-(** **** Exercise: 2 stars, optional (typing_example_2) *)
-(* empty |- (\z:(C->C)->(Top * B->B). (z (\x:C.x)).snd)
- (\z:C->C. ((\z:A.z), (\z:B.z)))
- : B->B *)
-(* FILL IN HERE *)
-(** [] *)
-
-End Examples2.
-
-(* ###################################################################### *)
-(** * Properties *)
-
-(** The fundamental properties of the system that we want to check are
- the same as always: progress and preservation. Unlike the
- extension of the STLC with references, we don't need to change the
- _statements_ of these properties to take subtyping into account.
- However, their proofs do become a little bit more involved. *)
-
-(* ###################################################################### *)
-(** ** Inversion Lemmas for Subtyping *)
-
-(** Before we look at the properties of the typing relation, we need
- to record a couple of critical structural properties of the subtype
- relation:
- - [Bool] is the only subtype of [Bool]
- - every subtype of an arrow type is itself an arrow type. *)
-
-(** These are called _inversion lemmas_ because they play the same
- role in later proofs as the built-in [inversion] tactic: given a
- hypothesis that there exists a derivation of some subtyping
- statement [S <: T] and some constraints on the shape of [S] and/or
- [T], each one reasons about what this derivation must look like to
- tell us something further about the shapes of [S] and [T] and the
- existence of subtype relations between their parts. *)
-
-(** **** Exercise: 2 stars, optional (sub_inversion_Bool) *)
-Lemma sub_inversion_Bool : forall U,
- U <: TBool ->
- U = TBool.
-Proof with auto.
- intros U Hs.
- remember TBool as V.
- (* FILL IN HERE *) Admitted.
-
-(** **** Exercise: 3 stars, optional (sub_inversion_arrow) *)
-Lemma sub_inversion_arrow : forall U V1 V2,
- U <: (TArrow V1 V2) ->
- exists U1, exists U2,
- U = (TArrow U1 U2) /\ (V1 <: U1) /\ (U2 <: V2).
-Proof with eauto.
- intros U V1 V2 Hs.
- remember (TArrow V1 V2) as V.
- generalize dependent V2. generalize dependent V1.
- (* FILL IN HERE *) Admitted.
-
-
-(** [] *)
-
-(* ########################################## *)
-(** ** Canonical Forms *)
-
-(** We'll see first that the proof of the progress theorem doesn't
- change too much -- we just need one small refinement. When we're
- considering the case where the term in question is an application
- [t1 t2] where both [t1] and [t2] are values, we need to know that
- [t1] has the _form_ of a lambda-abstraction, so that we can apply
- the [ST_AppAbs] reduction rule. In the ordinary STLC, this is
- obvious: we know that [t1] has a function type [T11->T12], and
- there is only one rule that can be used to give a function type to
- a value -- rule [T_Abs] -- and the form of the conclusion of this
- rule forces [t1] to be an abstraction.
-
- In the STLC with subtyping, this reasoning doesn't quite work
- because there's another rule that can be used to show that a value
- has a function type: subsumption. Fortunately, this possibility
- doesn't change things much: if the last rule used to show [Gamma
- |- t1 : T11->T12] is subsumption, then there is some
- _sub_-derivation whose subject is also [t1], and we can reason by
- induction until we finally bottom out at a use of [T_Abs].
-
- This bit of reasoning is packaged up in the following lemma, which
- tells us the possible "canonical forms" (i.e. values) of function
- type. *)
-
-(** **** Exercise: 3 stars, optional (canonical_forms_of_arrow_types) *)
-Lemma canonical_forms_of_arrow_types : forall Gamma s T1 T2,
- Gamma |- s \in (TArrow T1 T2) ->
- value s ->
- exists x, exists S1, exists s2,
- s = tabs x S1 s2.
-Proof with eauto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** Similarly, the canonical forms of type [Bool] are the constants
- [true] and [false]. *)
-
-Lemma canonical_forms_of_Bool : forall Gamma s,
- Gamma |- s \in TBool ->
- value s ->
- (s = ttrue \/ s = tfalse).
-Proof with eauto.
- intros Gamma s Hty Hv.
- remember TBool as T.
- has_type_cases (induction Hty) Case; try solve by inversion...
- Case "T_Sub".
- subst. apply sub_inversion_Bool in H. subst...
-Qed.
-
-
-(* ########################################## *)
-(** ** Progress *)
-
-(** The proof of progress proceeds like the one for the pure
- STLC, except that in several places we invoke canonical forms
- lemmas... *)
-
-(** _Theorem_ (Progress): For any term [t] and type [T], if [empty |-
- t : T] then [t] is a value or [t ==> t'] for some term [t'].
-
- _Proof_: Let [t] and [T] be given, with [empty |- t : T]. Proceed
- by induction on the typing derivation.
-
- The cases for [T_Abs], [T_Unit], [T_True] and [T_False] are
- immediate because abstractions, [unit], [true], and [false] are
- already values. The [T_Var] case is vacuous because variables
- cannot be typed in the empty context. The remaining cases are
- more interesting:
-
- - If the last step in the typing derivation uses rule [T_App],
- then there are terms [t1] [t2] and types [T1] and [T2] such that
- [t = t1 t2], [T = T2], [empty |- t1 : T1 -> T2], and [empty |-
- t2 : T1]. Moreover, by the induction hypothesis, either [t1] is
- a value or it steps, and either [t2] is a value or it steps.
- There are three possibilities to consider:
-
- - Suppose [t1 ==> t1'] for some term [t1']. Then [t1 t2 ==> t1' t2]
- by [ST_App1].
-
- - Suppose [t1] is a value and [t2 ==> t2'] for some term [t2'].
- Then [t1 t2 ==> t1 t2'] by rule [ST_App2] because [t1] is a
- value.
-
- - Finally, suppose [t1] and [t2] are both values. By Lemma
- [canonical_forms_for_arrow_types], we know that [t1] has the
- form [\x:S1.s2] for some [x], [S1], and [s2]. But then
- [(\x:S1.s2) t2 ==> [x:=t2]s2] by [ST_AppAbs], since [t2] is a
- value.
-
- - If the final step of the derivation uses rule [T_If], then there
- are terms [t1], [t2], and [t3] such that [t = if t1 then t2 else
- t3], with [empty |- t1 : Bool] and with [empty |- t2 : T] and
- [empty |- t3 : T]. Moreover, by the induction hypothesis,
- either [t1] is a value or it steps.
-
- - If [t1] is a value, then by the canonical forms lemma for
- booleans, either [t1 = true] or [t1 = false]. In either
- case, [t] can step, using rule [ST_IfTrue] or [ST_IfFalse].
-
- - If [t1] can step, then so can [t], by rule [ST_If].
-
- - If the final step of the derivation is by [T_Sub], then there is
- a type [S] such that [S <: T] and [empty |- t : S]. The desired
- result is exactly the induction hypothesis for the typing
- subderivation.
-*)
-
-Theorem progress : forall t T,
- empty |- t \in T ->
- value t \/ exists t', t ==> t'.
-Proof with eauto.
- intros t T Ht.
- remember empty as Gamma.
- revert HeqGamma.
- has_type_cases (induction Ht) Case;
- intros HeqGamma; subst...
- Case "T_Var".
- inversion H.
- Case "T_App".
- right.
- destruct IHHt1; subst...
- SCase "t1 is a value".
- destruct IHHt2; subst...
- SSCase "t2 is a value".
- destruct (canonical_forms_of_arrow_types empty t1 T1 T2)
- as [x [S1 [t12 Heqt1]]]...
- subst. exists ([x:=t2]t12)...
- SSCase "t2 steps".
- inversion H0 as [t2' Hstp]. exists (tapp t1 t2')...
- SCase "t1 steps".
- inversion H as [t1' Hstp]. exists (tapp t1' t2)...
- Case "T_If".
- right.
- destruct IHHt1.
- SCase "t1 is a value"...
- assert (t1 = ttrue \/ t1 = tfalse)
- by (eapply canonical_forms_of_Bool; eauto).
- inversion H0; subst...
- inversion H. rename x into t1'. eauto.
-
-Qed.
-
-(* ########################################## *)
-(** ** Inversion Lemmas for Typing *)
-
-(** The proof of the preservation theorem also becomes a little more
- complex with the addition of subtyping. The reason is that, as
- with the "inversion lemmas for subtyping" above, there are a
- number of facts about the typing relation that are "obvious from
- the definition" in the pure STLC (and hence can be obtained
- directly from the [inversion] tactic) but that require real proofs
- in the presence of subtyping because there are multiple ways to
- derive the same [has_type] statement.
-
- The following "inversion lemma" tells us that, if we have a
- derivation of some typing statement [Gamma |- \x:S1.t2 : T] whose
- subject is an abstraction, then there must be some subderivation
- giving a type to the body [t2]. *)
-
-(** _Lemma_: If [Gamma |- \x:S1.t2 : T], then there is a type [S2]
- such that [Gamma, x:S1 |- t2 : S2] and [S1 -> S2 <: T].
-
- (Notice that the lemma does _not_ say, "then [T] itself is an arrow
- type" -- this is tempting, but false!)
-
- _Proof_: Let [Gamma], [x], [S1], [t2] and [T] be given as
- described. Proceed by induction on the derivation of [Gamma |-
- \x:S1.t2 : T]. Cases [T_Var], [T_App], are vacuous as those
- rules cannot be used to give a type to a syntactic abstraction.
-
- - If the last step of the derivation is a use of [T_Abs] then
- there is a type [T12] such that [T = S1 -> T12] and [Gamma,
- x:S1 |- t2 : T12]. Picking [T12] for [S2] gives us what we
- need: [S1 -> T12 <: S1 -> T12] follows from [S_Refl].
-
- - If the last step of the derivation is a use of [T_Sub] then
- there is a type [S] such that [S <: T] and [Gamma |- \x:S1.t2 :
- S]. The IH for the typing subderivation tell us that there is
- some type [S2] with [S1 -> S2 <: S] and [Gamma, x:S1 |- t2 :
- S2]. Picking type [S2] gives us what we need, since [S1 -> S2
- <: T] then follows by [S_Trans]. *)
-
-Lemma typing_inversion_abs : forall Gamma x S1 t2 T,
- Gamma |- (tabs x S1 t2) \in T ->
- (exists S2, (TArrow S1 S2) <: T
- /\ (extend Gamma x S1) |- t2 \in S2).
-Proof with eauto.
- intros Gamma x S1 t2 T H.
- remember (tabs x S1 t2) as t.
- has_type_cases (induction H) Case;
- inversion Heqt; subst; intros; try solve by inversion.
- Case "T_Abs".
- exists T12...
- Case "T_Sub".
- destruct IHhas_type as [S2 [Hsub Hty]]...
- Qed.
-
-(** Similarly... *)
-
-Lemma typing_inversion_var : forall Gamma x T,
- Gamma |- (tvar x) \in T ->
- exists S,
- Gamma x = Some S /\ S <: T.
-Proof with eauto.
- intros Gamma x T Hty.
- remember (tvar x) as t.
- has_type_cases (induction Hty) Case; intros;
- inversion Heqt; subst; try solve by inversion.
- Case "T_Var".
- exists T...
- Case "T_Sub".
- destruct IHHty as [U [Hctx HsubU]]... Qed.
-
-Lemma typing_inversion_app : forall Gamma t1 t2 T2,
- Gamma |- (tapp t1 t2) \in T2 ->
- exists T1,
- Gamma |- t1 \in (TArrow T1 T2) /\
- Gamma |- t2 \in T1.
-Proof with eauto.
- intros Gamma t1 t2 T2 Hty.
- remember (tapp t1 t2) as t.
- has_type_cases (induction Hty) Case; intros;
- inversion Heqt; subst; try solve by inversion.
- Case "T_App".
- exists T1...
- Case "T_Sub".
- destruct IHHty as [U1 [Hty1 Hty2]]...
-Qed.
-
-Lemma typing_inversion_true : forall Gamma T,
- Gamma |- ttrue \in T ->
- TBool <: T.
-Proof with eauto.
- intros Gamma T Htyp. remember ttrue as tu.
- has_type_cases (induction Htyp) Case;
- inversion Heqtu; subst; intros...
-Qed.
-
-Lemma typing_inversion_false : forall Gamma T,
- Gamma |- tfalse \in T ->
- TBool <: T.
-Proof with eauto.
- intros Gamma T Htyp. remember tfalse as tu.
- has_type_cases (induction Htyp) Case;
- inversion Heqtu; subst; intros...
-Qed.
-
-Lemma typing_inversion_if : forall Gamma t1 t2 t3 T,
- Gamma |- (tif t1 t2 t3) \in T ->
- Gamma |- t1 \in TBool
- /\ Gamma |- t2 \in T
- /\ Gamma |- t3 \in T.
-Proof with eauto.
- intros Gamma t1 t2 t3 T Hty.
- remember (tif t1 t2 t3) as t.
- has_type_cases (induction Hty) Case; intros;
- inversion Heqt; subst; try solve by inversion.
- Case "T_If".
- auto.
- Case "T_Sub".
- destruct (IHHty H0) as [H1 [H2 H3]]...
-Qed.
-
-Lemma typing_inversion_unit : forall Gamma T,
- Gamma |- tunit \in T ->
- TUnit <: T.
-Proof with eauto.
- intros Gamma T Htyp. remember tunit as tu.
- has_type_cases (induction Htyp) Case;
- inversion Heqtu; subst; intros...
-Qed.
-
-
-(** The inversion lemmas for typing and for subtyping between arrow
- types can be packaged up as a useful "combination lemma" telling
- us exactly what we'll actually require below. *)
-
-Lemma abs_arrow : forall x S1 s2 T1 T2,
- empty |- (tabs x S1 s2) \in (TArrow T1 T2) ->
- T1 <: S1
- /\ (extend empty x S1) |- s2 \in T2.
-Proof with eauto.
- intros x S1 s2 T1 T2 Hty.
- apply typing_inversion_abs in Hty.
- inversion Hty as [S2 [Hsub Hty1]].
- apply sub_inversion_arrow in Hsub.
- inversion Hsub as [U1 [U2 [Heq [Hsub1 Hsub2]]]].
- inversion Heq; subst... Qed.
-
-(* ########################################## *)
-(** ** Context Invariance *)
-
-(** The context invariance lemma follows the same pattern as in the
- pure STLC. *)
-
-Inductive appears_free_in : id -> tm -> Prop :=
- | afi_var : forall x,
- appears_free_in x (tvar x)
- | afi_app1 : forall x t1 t2,
- appears_free_in x t1 -> appears_free_in x (tapp t1 t2)
- | afi_app2 : forall x t1 t2,
- appears_free_in x t2 -> appears_free_in x (tapp t1 t2)
- | afi_abs : forall x y T11 t12,
- y <> x ->
- appears_free_in x t12 ->
- appears_free_in x (tabs y T11 t12)
- | afi_if1 : forall x t1 t2 t3,
- appears_free_in x t1 ->
- appears_free_in x (tif t1 t2 t3)
- | afi_if2 : forall x t1 t2 t3,
- appears_free_in x t2 ->
- appears_free_in x (tif t1 t2 t3)
- | afi_if3 : forall x t1 t2 t3,
- appears_free_in x t3 ->
- appears_free_in x (tif t1 t2 t3)
-.
-
-Hint Constructors appears_free_in.
-
-Lemma context_invariance : forall Gamma Gamma' t S,
- Gamma |- t \in S ->
- (forall x, appears_free_in x t -> Gamma x = Gamma' x) ->
- Gamma' |- t \in S.
-Proof with eauto.
- intros. generalize dependent Gamma'.
- has_type_cases (induction H) Case;
- intros Gamma' Heqv...
- Case "T_Var".
- apply T_Var... rewrite <- Heqv...
- Case "T_Abs".
- apply T_Abs... apply IHhas_type. intros x0 Hafi.
- unfold extend. destruct (eq_id_dec x x0)...
-
- Case "T_App".
- apply T_App with T1...
- Case "T_If".
- apply T_If...
-
-Qed.
-
-Lemma free_in_context : forall x t T Gamma,
- appears_free_in x t ->
- Gamma |- t \in T ->
- exists T', Gamma x = Some T'.
-Proof with eauto.
- intros x t T Gamma Hafi Htyp.
- has_type_cases (induction Htyp) Case;
- subst; inversion Hafi; subst...
- Case "T_Abs".
- destruct (IHHtyp H4) as [T Hctx]. exists T.
- unfold extend in Hctx. rewrite neq_id in Hctx... Qed.
-
-(* ########################################## *)
-(** ** Substitution *)
-
-(** The _substitution lemma_ is proved along the same lines as
- for the pure STLC. The only significant change is that there are
- several places where, instead of the built-in [inversion] tactic,
- we need to use the inversion lemmas that we proved above to
- extract structural information from assumptions about the
- well-typedness of subterms. *)
-
-Lemma substitution_preserves_typing : forall Gamma x U v t S,
- (extend Gamma x U) |- t \in S ->
- empty |- v \in U ->
- Gamma |- ([x:=v]t) \in S.
-Proof with eauto.
- intros Gamma x U v t S Htypt Htypv.
- generalize dependent S. generalize dependent Gamma.
- t_cases (induction t) Case; intros; simpl.
- Case "tvar".
- rename i into y.
- destruct (typing_inversion_var _ _ _ Htypt)
- as [T [Hctx Hsub]].
- unfold extend in Hctx.
- destruct (eq_id_dec x y)...
- SCase "x=y".
- subst.
- inversion Hctx; subst. clear Hctx.
- apply context_invariance with empty...
- intros x Hcontra.
- destruct (free_in_context _ _ S empty Hcontra)
- as [T' HT']...
- inversion HT'.
- Case "tapp".
- destruct (typing_inversion_app _ _ _ _ Htypt)
- as [T1 [Htypt1 Htypt2]].
- eapply T_App...
- Case "tabs".
- rename i into y. rename t into T1.
- destruct (typing_inversion_abs _ _ _ _ _ Htypt)
- as [T2 [Hsub Htypt2]].
- apply T_Sub with (TArrow T1 T2)... apply T_Abs...
- destruct (eq_id_dec x y).
- SCase "x=y".
- eapply context_invariance...
- subst.
- intros x Hafi. unfold extend.
- destruct (eq_id_dec y x)...
- SCase "x<>y".
- apply IHt. eapply context_invariance...
- intros z Hafi. unfold extend.
- destruct (eq_id_dec y z)...
- subst. rewrite neq_id...
- Case "ttrue".
- assert (TBool <: S)
- by apply (typing_inversion_true _ _ Htypt)...
- Case "tfalse".
- assert (TBool <: S)
- by apply (typing_inversion_false _ _ Htypt)...
- Case "tif".
- assert ((extend Gamma x U) |- t1 \in TBool
- /\ (extend Gamma x U) |- t2 \in S
- /\ (extend Gamma x U) |- t3 \in S)
- by apply (typing_inversion_if _ _ _ _ _ Htypt).
- inversion H as [H1 [H2 H3]].
- apply IHt1 in H1. apply IHt2 in H2. apply IHt3 in H3.
- auto.
- Case "tunit".
- assert (TUnit <: S)
- by apply (typing_inversion_unit _ _ Htypt)...
-Qed.
-
-(* ########################################## *)
-(** ** Preservation *)
-
-(** The proof of preservation now proceeds pretty much as in earlier
- chapters, using the substitution lemma at the appropriate point
- and again using inversion lemmas from above to extract structural
- information from typing assumptions. *)
-
-(** _Theorem_ (Preservation): If [t], [t'] are terms and [T] is a type
- such that [empty |- t : T] and [t ==> t'], then [empty |- t' :
- T].
-
- _Proof_: Let [t] and [T] be given such that [empty |- t : T]. We
- proceed by induction on the structure of this typing derivation,
- leaving [t'] general. The cases [T_Abs], [T_Unit], [T_True], and
- [T_False] cases are vacuous because abstractions and constants
- don't step. Case [T_Var] is vacuous as well, since the context is
- empty.
-
- - If the final step of the derivation is by [T_App], then there
- are terms [t1] and [t2] and types [T1] and [T2] such that
- [t = t1 t2], [T = T2], [empty |- t1 : T1 -> T2], and
- [empty |- t2 : T1].
-
- By the definition of the step relation, there are three ways
- [t1 t2] can step. Cases [ST_App1] and [ST_App2] follow
- immediately by the induction hypotheses for the typing
- subderivations and a use of [T_App].
-
- Suppose instead [t1 t2] steps by [ST_AppAbs]. Then [t1 =
- \x:S.t12] for some type [S] and term [t12], and [t' =
- [x:=t2]t12].
-
- By lemma [abs_arrow], we have [T1 <: S] and [x:S1 |- s2 : T2].
- It then follows by the substitution lemma
- ([substitution_preserves_typing]) that [empty |- [x:=t2]
- t12 : T2] as desired.
-
- - If the final step of the derivation uses rule [T_If], then
- there are terms [t1], [t2], and [t3] such that [t = if t1 then
- t2 else t3], with [empty |- t1 : Bool] and with [empty |- t2 :
- T] and [empty |- t3 : T]. Moreover, by the induction
- hypothesis, if [t1] steps to [t1'] then [empty |- t1' : Bool].
- There are three cases to consider, depending on which rule was
- used to show [t ==> t'].
-
- - If [t ==> t'] by rule [ST_If], then [t' = if t1' then t2
- else t3] with [t1 ==> t1']. By the induction hypothesis,
- [empty |- t1' : Bool], and so [empty |- t' : T] by [T_If].
-
- - If [t ==> t'] by rule [ST_IfTrue] or [ST_IfFalse], then
- either [t' = t2] or [t' = t3], and [empty |- t' : T]
- follows by assumption.
-
- - If the final step of the derivation is by [T_Sub], then there
- is a type [S] such that [S <: T] and [empty |- t : S]. The
- result is immediate by the induction hypothesis for the typing
- subderivation and an application of [T_Sub]. [] *)
-
-Theorem preservation : forall t t' T,
- empty |- t \in T ->
- t ==> t' ->
- empty |- t' \in T.
-Proof with eauto.
- intros t t' T HT.
- remember empty as Gamma. generalize dependent HeqGamma.
- generalize dependent t'.
- has_type_cases (induction HT) Case;
- intros t' HeqGamma HE; subst; inversion HE; subst...
- Case "T_App".
- inversion HE; subst...
- SCase "ST_AppAbs".
- destruct (abs_arrow _ _ _ _ _ HT1) as [HA1 HA2].
- apply substitution_preserves_typing with T...
-Qed.
-
-(** ** Records, via Products and Top *)
-
-(** This formalization of the STLC with subtyping has omitted record
- types, for brevity. If we want to deal with them more seriously,
- we have two choices.
-
- First, we can treat them as part of the core language, writing
- down proper syntax, typing, and subtyping rules for them. Chapter
- [RecordSub] shows how this extension works.
-
- On the other hand, if we are treating them as a derived form that
- is desugared in the parser, then we shouldn't need any new rules:
- we should just check that the existing rules for subtyping product
- and [Unit] types give rise to reasonable rules for record
- subtyping via this encoding. To do this, we just need to make one
- small change to the encoding described earlier: instead of using
- [Unit] as the base case in the encoding of tuples and the "don't
- care" placeholder in the encoding of records, we use [Top]. So:
-<<
- {a:Nat, b:Nat} ----> {Nat,Nat} i.e. (Nat,(Nat,Top))
- {c:Nat, a:Nat} ----> {Nat,Top,Nat} i.e. (Nat,(Top,(Nat,Top)))
->>
- The encoding of record values doesn't change at all. It is
- easy (and instructive) to check that the subtyping rules above are
- validated by the encoding. For the rest of this chapter, we'll
- follow this encoding-based approach. *)
-
-(* ###################################################### *)
-(** ** Exercises *)
-
-(** **** Exercise: 2 stars (variations) *)
-(** Each part of this problem suggests a different way of
- changing the definition of the STLC with Unit and
- subtyping. (These changes are not cumulative: each part
- starts from the original language.) In each part, list which
- properties (Progress, Preservation, both, or neither) become
- false. If a property becomes false, give a counterexample.
- - Suppose we add the following typing rule:
- Gamma |- t : S1->S2
- S1 <: T1 T1 <: S1 S2 <: T2
- ----------------------------------- (T_Funny1)
- Gamma |- t : T1->T2
-
- - Suppose we add the following reduction rule:
- ------------------ (ST_Funny21)
- unit ==> (\x:Top. x)
-
- - Suppose we add the following subtyping rule:
- -------------- (S_Funny3)
- Unit <: Top->Top
-
- - Suppose we add the following subtyping rule:
- -------------- (S_Funny4)
- Top->Top <: Unit
-
- - Suppose we add the following evaluation rule:
- ----------------- (ST_Funny5)
- (unit t) ==> (t unit)
-
- - Suppose we add the same evaluation rule _and_ a new typing rule:
- ----------------- (ST_Funny5)
- (unit t) ==> (t unit)
-
- ---------------------- (T_Funny6)
- empty |- Unit : Top->Top
-
- - Suppose we _change_ the arrow subtyping rule to:
- S1 <: T1 S2 <: T2
- ----------------------- (S_Arrow')
- S1->S2 <: T1->T2
-
-[]
-*)
-
-(* ###################################################################### *)
-(** * Exercise: Adding Products *)
-
-(** **** Exercise: 4 stars, optional (products) *)
-(** Adding pairs, projections, and product types to the system we have
- defined is a relatively straightforward matter. Carry out this
- extension:
-
- - Add constructors for pairs, first and second projections, and
- product types to the definitions of [ty] and [tm]. (Don't
- forget to add corresponding cases to [T_cases] and [t_cases].)
-
- - Extend the well-formedness relation in the obvious way.
-
- - Extend the operational semantics with the same reduction rules
- as in the last chapter.
-
- - Extend the subtyping relation with this rule:
- S1 <: T1 S2 <: T2
- --------------------- (Sub_Prod)
- S1 * S2 <: T1 * T2
- - Extend the typing relation with the same rules for pairs and
- projections as in the last chapter.
-
- - Extend the proofs of progress, preservation, and all their
- supporting lemmas to deal with the new constructs. (You'll also
- need to add some completely new lemmas.) []
-*)
-
-
-(* $Date: 2013-12-05 11:55:09 -0500 (Thu, 05 Dec 2013) $ *)
-
diff --git a/Symbols.html b/Symbols.html
deleted file mode 100644
index 1d1ffb5..0000000
--- a/Symbols.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
-
-
-
-Symbols: Special symbols
-
-
-
-
-
-
-
-
-
-
-
-
-
-
SymbolsSpecial symbols
-
-
-
-
-
-
-
-
-
-
-(* $Date: 2013-04-01 20:39:05 -0400 (Mon, 01 Apr 2013) $ *)
-
-
-(* This file defines some HTML symbols for use by the coqdoc
- preprocessor. It is not intended to be read by anybody. *)
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Symbols.v b/Symbols.v
deleted file mode 100644
index f477ea2..0000000
--- a/Symbols.v
+++ /dev/null
@@ -1,27 +0,0 @@
-(** * Symbols: Special symbols *)
-
-(* $Date: 2013-04-01 20:39:05 -0400 (Mon, 01 Apr 2013) $ *)
-
-(* This file defines some HTML symbols for use by the coqdoc
- preprocessor. It is not intended to be read by anybody. *)
-
-(** printing -> #→# *)
-(** printing || #⇓# *)
-(** printing ==> #⇒# *)
-(** printing ==>* #⇒*# *)
-(** printing ==>+ #⇒+# *)
-(** printing |- #⊢# *)
-(** printing <- #←# *)
-(** printing <-> #↔# *)
-(** printing forall #∀# *)
-(** printing exists #∃# *)
-(** printing /\ #∧# *)
-(** printing \/ #∨# *)
-(** printing ->> #⇾# *)
-(** printing <<->> #⇿# *)
-(** printing |- #⊢# *)
-(** printing Gamma #Γ# *)
-(** printing Gamma' #Γ'# *)
-(** printing Gamma'' #Γ''# *)
-(** printing |-> #↦# *)
-
diff --git a/Types.html b/Types.html
deleted file mode 100644
index de0b00e..0000000
--- a/Types.html
+++ /dev/null
@@ -1,1450 +0,0 @@
-
-
-
-
-
-Types: Type Systems
-
-
-
-
-
-
-
-Our next major topic is type systems — static program
- analyses that classify expressions according to the "shapes" of
- their results. We'll begin with a typed version of a very simple
- language with just booleans and numbers, to introduce the basic
- ideas of types, typing rules, and the fundamental theorems about
- type systems: type preservation and progress. Then we'll move
- on to the simply typed lambda-calculus, which lives at the core
- of every modern functional programming language (including
- Coq).
-
-
-
-
-
-
-
-
Typed Arithmetic Expressions
-
-
-
- To motivate the discussion of type systems, let's begin as
- usual with an extremely simple toy language. We want it to have
- the potential for programs "going wrong" because of runtime type
- errors, so we need something a tiny bit more complex than the
- language of constants and addition that we used in chapter
- Smallstep: a single kind of data (just numbers) is too simple,
- but just two kinds (numbers and booleans) already gives us enough
- material to tell an interesting story.
-
-
-
- The language definition is completely routine. The only thing to
- notice is that we are not using the asnum/aslist trick that
- we used in chapter HoareList to make all the operations total by
- forcibly coercing the arguments to + (for example) into numbers.
- Instead, we simply let terms get stuck if they try to use an
- operator with the wrong kind of operands: the step relation
- doesn't relate them to anything.
-
-Notice that the step relation doesn't care about whether
- expressions make global sense — it just checks that the operation
- in the next reduction step is being applied to the right kinds
- of operands.
-
-
-
- For example, the term succtrue (i.e., tsuccttrue in the
- formal syntax) cannot take a step, but the almost as obviously
- nonsensical term
-
-
-
-
- succ (iftruethentrueelsetrue)
-
-
-
- can take a step (once, before becoming stuck).
-
-
-
-
-
-
-
-
Normal Forms and Values
-
-
-
- The first interesting thing about the step relation in this
- language is that the strong progress theorem from the Smallstep
- chapter fails! That is, there are terms that are normal
- forms (they can't take a step) but not values (because we have not
- included them in our definition of possible "results of
- evaluation"). Such terms are stuck.
-
-
- However, although values and normal forms are not the same in this
- language, the former set is included in the latter. This is
- important because it shows we did not accidentally define things
- so that some value could still take a step.
-
-
-
Exercise: 3 stars, advanced (value_is_nf)
- Hint: You will reach a point in this proof where you need to
- use an induction to reason about a term that is known to be a
- numeric value. This induction can be performed either over the
- term itself or over the evidence that it is a numeric value. The
- proof goes through in either case, but you will find that one way
- is quite a bit shorter than the other. For the sake of the
- exercise, try to complete the proof both ways.
-
- Using value_is_nf, we can show that the step relation is
- also deterministic...
-
-
-
-
-Theoremstep_deterministic:
- deterministicstep.
-Proofwitheauto.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Typing
-
-
-
- The next critical observation about this language is that,
- although there are stuck terms, they are all "nonsensical", mixing
- booleans and numbers in a way that we don't even want to have a
- meaning. We can easily exclude such ill-typed terms by defining a
- typing relation that relates terms to the types (either numeric
- or boolean) of their final results.
-
-
-
-
-Inductivety : Type :=
- | TBool : ty
- | TNat : ty.
-
-
-
-
-
-In informal notation, the typing relation is often written
- ⊢t∈T, pronounced "t has type T." The ⊢ symbol is
- called a "turnstile". (Below, we're going to see richer typing
- relations where an additional "context" argument is written to the
- left of the turnstile. Here, the context is always empty.)
-
- It's important to realize that the typing relation is a
- conservative (or static) approximation: it does not calculate
- the type of the normal form of a term.
-
-(Since we've included all the constructors of the typing relation
- in the hint database, the auto tactic can actually find this
- proof automatically.)
-
-Examplesucc_hastype_nat__hastype_nat : ∀t,
- ⊢tsucct ∈ TNat→
- ⊢t ∈ TNat.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Canonical forms
-
-
-
- The following two lemmas capture the basic property that defines
- the shape of well-typed values. They say that the definition of value
- and the typing relation agree.
-
- Complete the formal proof of the progress property. (Make sure
- you understand the informal proof fragment in the following
- exercise before starting — this will save you a lot of time.)
-
-
-
-
-
-
-Proofwithauto.
- introstTHT.
- has_type_cases (inductionHT) Case...
- (* The cases that were obviously values, like T_True and
- T_False, were eliminated immediately by auto *)
- Case "T_If".
- right. inversionIHHT1; clearIHHT1.
- SCase "t1 is a value".
- apply (bool_canonicalt1HT1) inH.
- inversionH; subst; clearH.
- ∃t2...
- ∃t3...
- SCase "t1 can take a step".
- inversionHas [t1'H1].
- ∃(tift1't2t3)...
- (* FILL IN HERE *)Admitted.
-
-
- Theorem: If ⊢t∈T, then either t is a value or else
- t⇒t' for some t'.
-
-
- Proof: By induction on a derivation of ⊢t∈T.
-
-
-
-
-
If the last rule in the derivation is T_If, then t=ift1
- thent2elset3, with ⊢t1∈Bool, ⊢t2∈T and ⊢t3
- ∈T. By the IH, either t1 is a value or else t1 can step
- to some t1'.
-
-
-
-
-
If t1 is a value, then by the canonical forms lemmas
- and the fact that ⊢t1∈Bool we have that t1
- is a bvalue — i.e., it is either true or false.
- If t1=true, then t steps to t2 by ST_IfTrue,
- while if t1=false, then t steps to t3 by
- ST_IfFalse. Either way, t can step, which is what
- we wanted to show.
-
-
-
-
-
-
If t1 itself can take a step, then, by ST_If, so can
- t.
-
-
-
-
-
-
-
-
-
- (* FILL IN HERE *)
-☐
-
-
-
- This is more interesting than the strong progress theorem that we
- saw in the Smallstep chapter, where all normal forms were
- values. Here, a term can be stuck, but only if it is ill
- typed.
-
-
-
Exercise: 1 star (step_review)
- Quick review. Answer true or false. In this language...
-
-
-
-
-
Every well-typed normal form is a value.
-
-
-
-
-
-
Every value is a normal form.
-
-
-
-
-
-
The single-step evaluation relation is
- a partial function (i.e., it is deterministic).
-
-
-
-
-
-
The single-step evaluation relation is a total function.
-
-
-
-
-
-
- ☐
-
-
-
-
-
-
-
-
Type Preservation
-
-
-
- The second critical property of typing is that, when a well-typed
- term takes a step, the result is also a well-typed term.
-
-
-
- This theorem is often called the subject reduction property,
- because it tells us what happens when the "subject" of the typing
- relation is reduced. This terminology comes from thinking of
- typing statements as sentences, where the term is the subject and
- the type is the predicate.
-
- Complete the formal proof of the preservation property. (Again,
- make sure you understand the informal proof fragment in the
- following exercise first.)
-
-
-
-
-
-
-Proofwithauto.
- introstt'THTHE.
- generalizedependentt'.
- has_type_cases (inductionHT) Case;
- (* every case needs to introduce a couple of things *)
- introst'HE;
- (* and we can deal with several impossible
- cases all at once *)
- try (solvebyinversion).
- Case "T_If". inversionHE; subst; clearHE.
- SCase "ST_IFTrue". assumption.
- SCase "ST_IfFalse". assumption.
- SCase "ST_If". applyT_If; tryassumption.
- applyIHHT1; assumption.
- (* FILL IN HERE *)Admitted.
-
-
- Proof: By induction on a derivation of ⊢t∈T.
-
-
-
-
-
If the last rule in the derivation is T_If, then t=ift1
- thent2elset3, with ⊢t1∈Bool, ⊢t2∈T and ⊢t3
- ∈T.
-
-
-
- Inspecting the rules for the small-step reduction relation and
- remembering that t has the form if..., we see that the
- only ones that could have been used to prove t⇒t' are
- ST_IfTrue, ST_IfFalse, or ST_If.
-
-
-
-
-
If the last rule was ST_IfTrue, then t'=t2. But we
- know that ⊢t2∈T, so we are done.
-
-
-
-
-
-
If the last rule was ST_IfFalse, then t'=t3. But we
- know that ⊢t3∈T, so we are done.
-
-
-
-
-
-
If the last rule was ST_If, then t'=ift1'thent2
- elset3, where t1⇒t1'. We know ⊢t1∈Bool so,
- by the IH, ⊢t1'∈Bool. The T_If rule then gives us
- ⊢ift1'thent2elset3∈T, as required.
-
-
-
-
-
-
-
-
-
- (* FILL IN HERE *)
-☐
-
-
-
-
Exercise: 3 stars (preservation_alternate_proof)
- Now prove the same property again by induction on the
- evaluation derivation instead of on the typing derivation.
- Begin by carefully reading and thinking about the first few
- lines of the above proof to make sure you understand what
- each one is doing. The set-up for this proof is similar, but
- not exactly the same.
-
-
-
-
-Theorempreservation' : ∀tt'T,
- ⊢t ∈ T→
- t⇒t'→
- ⊢t' ∈ T.
-Proofwitheauto.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Type Soundness
-
-
-
- Putting progress and preservation together, we can see that a
- well-typed term can never reach a stuck state.
-
-
- When experimenting with definitions of programming languages in
- Coq, we often want to see what a particular concrete term steps
- to — i.e., we want to find proofs for goals of the form t⇒*
- t', where t is a completely concrete term and t' is unknown.
- These proofs are simple but repetitive to do by hand. Consider for
- example reducing an arithmetic expression using the small-step
- relation astep.
-
-
-
-
-
-
-Definitionamultistepst := multi (astepst).
-Notation " t '/' st '⇒a×' t' " := (amultistepsttt')
- (atlevel 40, statlevel 39).
-
-We repeatedly apply multi_step until we get to a normal
- form. The proofs that the intermediate steps are possible are
- simple enough that auto, with appropriate hints, can solve
- them.
-
-The following custom TacticNotation definition captures this
- pattern. In addition, before each multi_step we print out the
- current goal, so that the user can follow how the term is being
- evaluated.
-
-The normalize tactic also provides a simple way to calculate
- what the normal form of a term is, by proving a goal with an
- existential variable in it.
-
-
-
-
-Exampleastep_example1''' : ∃e',
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4))) / empty_state
- ⇒a× e'.
-Proof.
- eapplyex_intro. normalize.
-
-
-(* This time, the trace will be:
-
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4)) / empty_state ==>a* ??)
- (multi (astep empty_state) (APlus (ANum 3) (ANum 12)) ??)
- (multi (astep empty_state) (ANum 15) ??)
-
- where ?? is the variable ``guessed'' by eapply.
-*)
-Qed.
-
-
-
-
-
-
Exercise: 1 star (normalize_ex)
-
-
-
-Theoremnormalize_ex : ∃e',
- (AMult (ANum 3) (AMult (ANum 2) (ANum 1))) / empty_state
- ⇒a× e'.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-
-
-☐
-
-
-
Exercise: 1 star, optional (normalize_ex')
- For comparison, prove it using apply instead of eapply.
-
-
-
-
-Theoremnormalize_ex' : ∃e',
- (AMult (ANum 3) (AMult (ANum 2) (ANum 1))) / empty_state
- ⇒a× e'.
-Proof.
- (* FILL IN HERE *)Admitted.
-
-
-
-☐
-
-
-
-
-
-
-
-
Additional Exercises
-
-
-
-
Exercise: 2 stars (subject_expansion)
- Having seen the subject reduction property, it is reasonable to
- wonder whether the opposity property — subject expansion —
- also holds. That is, is it always the case that, if t⇒t'
- and ⊢t'∈T, then ⊢t∈T? If so, prove it. If
- not, give a counter-example. (You do not need to prove your
- counter-example in Coq, but feel free to do so if you like.)
-
-
-
- (* FILL IN HERE *)
-☐
-
-
-
-
Exercise: 2 stars (variation1)
- Suppose, that we add this new rule to the typing relation:
-
-
- Which of the following properties remain true in the presence of
- this rule? For each one, write either "remains true" or
- else "becomes false." If a property becomes false, give a
- counterexample.
-
-
-
-
-
Determinism of step
-
-
-
-
-
-
Progress
-
-
-
-
-
-
Preservation
-
-
-
-
-
-
-☐
-
-
-
-
Exercise: 2 stars (variation2)
- Suppose, instead, that we add this new rule to the step relation:
-
-
-
-
- | ST_Funny1 : ∀t2t3,
- (tifttruet2t3) ⇒t3
-
-
-
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-
-
-☐
-
-
-
-
Exercise: 2 stars, optional (variation5)
- Suppose instead that we add this rule:
-
-
-
-
- | T_Funny4 :
- ⊢tzero ∈ TBool
-
-
-
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-
-
-☐
-
-
-
-
Exercise: 2 stars, optional (variation6)
- Suppose instead that we add this rule:
-
-
-
-
- | T_Funny5 :
- ⊢tpredtzero ∈ TBool
-
-
-
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-
-
-☐
-
-
-
-
Exercise: 3 stars, optional (more_variations)
- Make up some exercises of your own along the same lines as
- the ones above. Try to find ways of selectively breaking
- properties — i.e., ways of changing the definitions that
- break just one of the properties and leave the others alone.
- ☐
-
-
-
-
Exercise: 1 star (remove_predzero)
- The evaluation rule E_PredZero is a bit counter-intuitive: we
- might feel that it makes more sense for the predecessor of zero to
- be undefined, rather than being defined to be zero. Can we
- achieve this simply by removing the rule from the definition of
- step? Would doing so create any problems elsewhere?
-
-
-
-(* FILL IN HERE *)
-☐
-
-
-
Exercise: 4 stars, advanced (prog_pres_bigstep)
- Suppose our evaluation relation is defined in the big-step style.
- What are the appropriate analogs of the progress and preservation
- properties?
-
-
-
-
-
\ No newline at end of file
diff --git a/Types.v b/Types.v
deleted file mode 100644
index cad1546..0000000
--- a/Types.v
+++ /dev/null
@@ -1,787 +0,0 @@
-(** * Types: Type Systems *)
-
-Require Export Smallstep.
-
-Hint Constructors multi.
-
-(** Our next major topic is _type systems_ -- static program
- analyses that classify expressions according to the "shapes" of
- their results. We'll begin with a typed version of a very simple
- language with just booleans and numbers, to introduce the basic
- ideas of types, typing rules, and the fundamental theorems about
- type systems: _type preservation_ and _progress_. Then we'll move
- on to the _simply typed lambda-calculus_, which lives at the core
- of every modern functional programming language (including
- Coq). *)
-
-(* ###################################################################### *)
-(** * Typed Arithmetic Expressions *)
-
-(** To motivate the discussion of type systems, let's begin as
- usual with an extremely simple toy language. We want it to have
- the potential for programs "going wrong" because of runtime type
- errors, so we need something a tiny bit more complex than the
- language of constants and addition that we used in chapter
- [Smallstep]: a single kind of data (just numbers) is too simple,
- but just two kinds (numbers and booleans) already gives us enough
- material to tell an interesting story.
-
- The language definition is completely routine. The only thing to
- notice is that we are _not_ using the [asnum]/[aslist] trick that
- we used in chapter [HoareList] to make all the operations total by
- forcibly coercing the arguments to [+] (for example) into numbers.
- Instead, we simply let terms get stuck if they try to use an
- operator with the wrong kind of operands: the [step] relation
- doesn't relate them to anything. *)
-
-(* ###################################################################### *)
-(** ** Syntax *)
-
-(** Informally:
- t ::= true
- | false
- | if t then t else t
- | 0
- | succ t
- | pred t
- | iszero t
- Formally:
-*)
-
-Inductive tm : Type :=
- | ttrue : tm
- | tfalse : tm
- | tif : tm -> tm -> tm -> tm
- | tzero : tm
- | tsucc : tm -> tm
- | tpred : tm -> tm
- | tiszero : tm -> tm.
-
-(** _Values_ are [true], [false], and numeric values... *)
-
-Inductive bvalue : tm -> Prop :=
- | bv_true : bvalue ttrue
- | bv_false : bvalue tfalse.
-
-Inductive nvalue : tm -> Prop :=
- | nv_zero : nvalue tzero
- | nv_succ : forall t, nvalue t -> nvalue (tsucc t).
-
-Definition value (t:tm) := bvalue t \/ nvalue t.
-
-Hint Constructors bvalue nvalue.
-Hint Unfold value.
-Hint Unfold extend.
-
-(* ###################################################################### *)
-(** ** Operational Semantics *)
-
-(** Informally: *)
-(**
- ------------------------------ (ST_IfTrue)
- if true then t1 else t2 ==> t1
-
- ------------------------------- (ST_IfFalse)
- if false then t1 else t2 ==> t2
-
- t1 ==> t1'
- ------------------------- (ST_If)
- if t1 then t2 else t3 ==>
- if t1' then t2 else t3
-
- t1 ==> t1'
- -------------------- (ST_Succ)
- succ t1 ==> succ t1'
-
- ------------ (ST_PredZero)
- pred 0 ==> 0
-
- numeric value v1
- --------------------- (ST_PredSucc)
- pred (succ v1) ==> v1
-
- t1 ==> t1'
- -------------------- (ST_Pred)
- pred t1 ==> pred t1'
-
- ----------------- (ST_IszeroZero)
- iszero 0 ==> true
-
- numeric value v1
- -------------------------- (ST_IszeroSucc)
- iszero (succ v1) ==> false
-
- t1 ==> t1'
- ------------------------ (ST_Iszero)
- iszero t1 ==> iszero t1'
-*)
-
-(** Formally: *)
-
-Reserved Notation "t1 '==>' t2" (at level 40).
-
-Inductive step : tm -> tm -> Prop :=
- | ST_IfTrue : forall t1 t2,
- (tif ttrue t1 t2) ==> t1
- | ST_IfFalse : forall t1 t2,
- (tif tfalse t1 t2) ==> t2
- | ST_If : forall t1 t1' t2 t3,
- t1 ==> t1' ->
- (tif t1 t2 t3) ==> (tif t1' t2 t3)
- | ST_Succ : forall t1 t1',
- t1 ==> t1' ->
- (tsucc t1) ==> (tsucc t1')
- | ST_PredZero :
- (tpred tzero) ==> tzero
- | ST_PredSucc : forall t1,
- nvalue t1 ->
- (tpred (tsucc t1)) ==> t1
- | ST_Pred : forall t1 t1',
- t1 ==> t1' ->
- (tpred t1) ==> (tpred t1')
- | ST_IszeroZero :
- (tiszero tzero) ==> ttrue
- | ST_IszeroSucc : forall t1,
- nvalue t1 ->
- (tiszero (tsucc t1)) ==> tfalse
- | ST_Iszero : forall t1 t1',
- t1 ==> t1' ->
- (tiszero t1) ==> (tiszero t1')
-
-where "t1 '==>' t2" := (step t1 t2).
-
-Tactic Notation "step_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "ST_IfTrue" | Case_aux c "ST_IfFalse" | Case_aux c "ST_If"
- | Case_aux c "ST_Succ" | Case_aux c "ST_PredZero"
- | Case_aux c "ST_PredSucc" | Case_aux c "ST_Pred"
- | Case_aux c "ST_IszeroZero" | Case_aux c "ST_IszeroSucc"
- | Case_aux c "ST_Iszero" ].
-
-Hint Constructors step.
-(** Notice that the [step] relation doesn't care about whether
- expressions make global sense -- it just checks that the operation
- in the _next_ reduction step is being applied to the right kinds
- of operands.
-
- For example, the term [succ true] (i.e., [tsucc ttrue] in the
- formal syntax) cannot take a step, but the almost as obviously
- nonsensical term
- succ (if true then true else true)
- can take a step (once, before becoming stuck). *)
-
-(* ###################################################################### *)
-(** ** Normal Forms and Values *)
-
-(** The first interesting thing about the [step] relation in this
- language is that the strong progress theorem from the Smallstep
- chapter fails! That is, there are terms that are normal
- forms (they can't take a step) but not values (because we have not
- included them in our definition of possible "results of
- evaluation"). Such terms are _stuck_. *)
-
-Notation step_normal_form := (normal_form step).
-
-Definition stuck (t:tm) : Prop :=
- step_normal_form t /\ ~ value t.
-
-Hint Unfold stuck.
-
-(** **** Exercise: 2 stars (some_term_is_stuck) *)
-Example some_term_is_stuck :
- exists t, stuck t.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** However, although values and normal forms are not the same in this
- language, the former set is included in the latter. This is
- important because it shows we did not accidentally define things
- so that some value could still take a step. *)
-
-(** **** Exercise: 3 stars, advanced (value_is_nf) *)
-(** Hint: You will reach a point in this proof where you need to
- use an induction to reason about a term that is known to be a
- numeric value. This induction can be performed either over the
- term itself or over the evidence that it is a numeric value. The
- proof goes through in either case, but you will find that one way
- is quite a bit shorter than the other. For the sake of the
- exercise, try to complete the proof both ways. *)
-
-Lemma value_is_nf : forall t,
- value t -> step_normal_form t.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(** **** Exercise: 3 stars, optional (step_deterministic) *)
-(** Using [value_is_nf], we can show that the [step] relation is
- also deterministic... *)
-
-Theorem step_deterministic:
- deterministic step.
-Proof with eauto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-
-(* ###################################################################### *)
-(** ** Typing *)
-
-(** The next critical observation about this language is that,
- although there are stuck terms, they are all "nonsensical", mixing
- booleans and numbers in a way that we don't even _want_ to have a
- meaning. We can easily exclude such ill-typed terms by defining a
- _typing relation_ that relates terms to the types (either numeric
- or boolean) of their final results. *)
-
-Inductive ty : Type :=
- | TBool : ty
- | TNat : ty.
-
-(** In informal notation, the typing relation is often written
- [|- t \in T], pronounced "[t] has type [T]." The [|-] symbol is
- called a "turnstile". (Below, we're going to see richer typing
- relations where an additional "context" argument is written to the
- left of the turnstile. Here, the context is always empty.) *)
-(**
- ---------------- (T_True)
- |- true \in Bool
-
- ----------------- (T_False)
- |- false \in Bool
-
- |- t1 \in Bool |- t2 \in T |- t3 \in T
- -------------------------------------------- (T_If)
- |- if t1 then t2 else t3 \in T
-
- ------------ (T_Zero)
- |- 0 \in Nat
-
- |- t1 \in Nat
- ------------------ (T_Succ)
- |- succ t1 \in Nat
-
- |- t1 \in Nat
- ------------------ (T_Pred)
- |- pred t1 \in Nat
-
- |- t1 \in Nat
- --------------------- (T_IsZero)
- |- iszero t1 \in Bool
-*)
-
-Reserved Notation "'|-' t '\in' T" (at level 40).
-
-Inductive has_type : tm -> ty -> Prop :=
- | T_True :
- |- ttrue \in TBool
- | T_False :
- |- tfalse \in TBool
- | T_If : forall t1 t2 t3 T,
- |- t1 \in TBool ->
- |- t2 \in T ->
- |- t3 \in T ->
- |- tif t1 t2 t3 \in T
- | T_Zero :
- |- tzero \in TNat
- | T_Succ : forall t1,
- |- t1 \in TNat ->
- |- tsucc t1 \in TNat
- | T_Pred : forall t1,
- |- t1 \in TNat ->
- |- tpred t1 \in TNat
- | T_Iszero : forall t1,
- |- t1 \in TNat ->
- |- tiszero t1 \in TBool
-
-where "'|-' t '\in' T" := (has_type t T).
-
-Tactic Notation "has_type_cases" tactic(first) ident(c) :=
- first;
- [ Case_aux c "T_True" | Case_aux c "T_False" | Case_aux c "T_If"
- | Case_aux c "T_Zero" | Case_aux c "T_Succ" | Case_aux c "T_Pred"
- | Case_aux c "T_Iszero" ].
-
-Hint Constructors has_type.
-
-(* ###################################################################### *)
-(** *** Examples *)
-
-(** It's important to realize that the typing relation is a
- _conservative_ (or _static_) approximation: it does not calculate
- the type of the normal form of a term. *)
-
-Example has_type_1 :
- |- tif tfalse tzero (tsucc tzero) \in TNat.
-Proof.
- apply T_If.
- apply T_False.
- apply T_Zero.
- apply T_Succ.
- apply T_Zero.
-Qed.
-
-(** (Since we've included all the constructors of the typing relation
- in the hint database, the [auto] tactic can actually find this
- proof automatically.) *)
-
-Example has_type_not :
- ~ (|- tif tfalse tzero ttrue \in TBool).
-Proof.
- intros Contra. solve by inversion 2. Qed.
-
-(** **** Exercise: 1 star, optional (succ_hastype_nat__hastype_nat) *)
-Example succ_hastype_nat__hastype_nat : forall t,
- |- tsucc t \in TNat ->
- |- t \in TNat.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** ** Canonical forms *)
-
-(** The following two lemmas capture the basic property that defines
- the shape of well-typed values. They say that the definition of value
- and the typing relation agree. *)
-
-Lemma bool_canonical : forall t,
- |- t \in TBool -> value t -> bvalue t.
-Proof.
- intros t HT HV.
- inversion HV; auto.
-
- induction H; inversion HT; auto.
-Qed.
-
-Lemma nat_canonical : forall t,
- |- t \in TNat -> value t -> nvalue t.
-Proof.
- intros t HT HV.
- inversion HV.
- inversion H; subst; inversion HT.
-
- auto.
-Qed.
-
-(* ###################################################################### *)
-(** ** Progress *)
-
-(** The typing relation enjoys two critical properties. The first is
- that well-typed normal forms are values (i.e., not stuck). *)
-
-Theorem progress : forall t T,
- |- t \in T ->
- value t \/ exists t', t ==> t'.
-
-(** **** Exercise: 3 stars (finish_progress) *)
-(** Complete the formal proof of the [progress] property. (Make sure
- you understand the informal proof fragment in the following
- exercise before starting -- this will save you a lot of time.) *)
-
-Proof with auto.
- intros t T HT.
- has_type_cases (induction HT) Case...
- (* The cases that were obviously values, like T_True and
- T_False, were eliminated immediately by auto *)
- Case "T_If".
- right. inversion IHHT1; clear IHHT1.
- SCase "t1 is a value".
- apply (bool_canonical t1 HT1) in H.
- inversion H; subst; clear H.
- exists t2...
- exists t3...
- SCase "t1 can take a step".
- inversion H as [t1' H1].
- exists (tif t1' t2 t3)...
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (finish_progress_informal) *)
-(** Complete the corresponding informal proof: *)
-
-(** _Theorem_: If [|- t \in T], then either [t] is a value or else
- [t ==> t'] for some [t']. *)
-
-(** _Proof_: By induction on a derivation of [|- t \in T].
-
- - If the last rule in the derivation is [T_If], then [t = if t1
- then t2 else t3], with [|- t1 \in Bool], [|- t2 \in T] and [|- t3
- \in T]. By the IH, either [t1] is a value or else [t1] can step
- to some [t1'].
-
- - If [t1] is a value, then by the canonical forms lemmas
- and the fact that [|- t1 \in Bool] we have that [t1]
- is a [bvalue] -- i.e., it is either [true] or [false].
- If [t1 = true], then [t] steps to [t2] by [ST_IfTrue],
- while if [t1 = false], then [t] steps to [t3] by
- [ST_IfFalse]. Either way, [t] can step, which is what
- we wanted to show.
-
- - If [t1] itself can take a step, then, by [ST_If], so can
- [t].
-
- (* FILL IN HERE *)
-[]
-*)
-
-(** This is more interesting than the strong progress theorem that we
- saw in the Smallstep chapter, where _all_ normal forms were
- values. Here, a term can be stuck, but only if it is ill
- typed. *)
-
-(** **** Exercise: 1 star (step_review) *)
-(** Quick review. Answer _true_ or _false_. In this language...
- - Every well-typed normal form is a value.
-
- - Every value is a normal form.
-
- - The single-step evaluation relation is
- a partial function (i.e., it is deterministic).
-
- - The single-step evaluation relation is a _total_ function.
-
-*)
-(** [] *)
-
-(* ###################################################################### *)
-(** ** Type Preservation *)
-
-(** The second critical property of typing is that, when a well-typed
- term takes a step, the result is also a well-typed term.
-
- This theorem is often called the _subject reduction_ property,
- because it tells us what happens when the "subject" of the typing
- relation is reduced. This terminology comes from thinking of
- typing statements as sentences, where the term is the subject and
- the type is the predicate. *)
-
-Theorem preservation : forall t t' T,
- |- t \in T ->
- t ==> t' ->
- |- t' \in T.
-
-(** **** Exercise: 2 stars (finish_preservation) *)
-(** Complete the formal proof of the [preservation] property. (Again,
- make sure you understand the informal proof fragment in the
- following exercise first.) *)
-
-Proof with auto.
- intros t t' T HT HE.
- generalize dependent t'.
- has_type_cases (induction HT) Case;
- (* every case needs to introduce a couple of things *)
- intros t' HE;
- (* and we can deal with several impossible
- cases all at once *)
- try (solve by inversion).
- Case "T_If". inversion HE; subst; clear HE.
- SCase "ST_IFTrue". assumption.
- SCase "ST_IfFalse". assumption.
- SCase "ST_If". apply T_If; try assumption.
- apply IHHT1; assumption.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(** **** Exercise: 3 stars, advanced (finish_preservation_informal) *)
-(** Complete the following proof: *)
-
-(** _Theorem_: If [|- t \in T] and [t ==> t'], then [|- t' \in T]. *)
-
-(** _Proof_: By induction on a derivation of [|- t \in T].
-
- - If the last rule in the derivation is [T_If], then [t = if t1
- then t2 else t3], with [|- t1 \in Bool], [|- t2 \in T] and [|- t3
- \in T].
-
- Inspecting the rules for the small-step reduction relation and
- remembering that [t] has the form [if ...], we see that the
- only ones that could have been used to prove [t ==> t'] are
- [ST_IfTrue], [ST_IfFalse], or [ST_If].
-
- - If the last rule was [ST_IfTrue], then [t' = t2]. But we
- know that [|- t2 \in T], so we are done.
-
- - If the last rule was [ST_IfFalse], then [t' = t3]. But we
- know that [|- t3 \in T], so we are done.
-
- - If the last rule was [ST_If], then [t' = if t1' then t2
- else t3], where [t1 ==> t1']. We know [|- t1 \in Bool] so,
- by the IH, [|- t1' \in Bool]. The [T_If] rule then gives us
- [|- if t1' then t2 else t3 \in T], as required.
-
- (* FILL IN HERE *)
-[]
-*)
-
-(** **** Exercise: 3 stars (preservation_alternate_proof) *)
-(** Now prove the same property again by induction on the
- _evaluation_ derivation instead of on the typing derivation.
- Begin by carefully reading and thinking about the first few
- lines of the above proof to make sure you understand what
- each one is doing. The set-up for this proof is similar, but
- not exactly the same. *)
-
-Theorem preservation' : forall t t' T,
- |- t \in T ->
- t ==> t' ->
- |- t' \in T.
-Proof with eauto.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-(* ###################################################################### *)
-(** ** Type Soundness *)
-
-(** Putting progress and preservation together, we can see that a
- well-typed term can _never_ reach a stuck state. *)
-
-Definition multistep := (multi step).
-Notation "t1 '==>*' t2" := (multistep t1 t2) (at level 40).
-
-Corollary soundness : forall t t' T,
- |- t \in T ->
- t ==>* t' ->
- ~(stuck t').
-Proof.
- intros t t' T HT P. induction P; intros [R S].
- destruct (progress x T HT); auto.
- apply IHP. apply (preservation x y T HT H).
- unfold stuck. split; auto. Qed.
-
-
-(* ###################################################################### *)
-(** * Aside: the [normalize] Tactic *)
-
-(** When experimenting with definitions of programming languages in
- Coq, we often want to see what a particular concrete term steps
- to -- i.e., we want to find proofs for goals of the form [t ==>*
- t'], where [t] is a completely concrete term and [t'] is unknown.
- These proofs are simple but repetitive to do by hand. Consider for
- example reducing an arithmetic expression using the small-step
- relation [astep]. *)
-
-
-Definition amultistep st := multi (astep st).
-Notation " t '/' st '==>a*' t' " := (amultistep st t t')
- (at level 40, st at level 39).
-
-Example astep_example1 :
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4))) / empty_state
- ==>a* (ANum 15).
-Proof.
- apply multi_step with (APlus (ANum 3) (ANum 12)).
- apply AS_Plus2.
- apply av_num.
- apply AS_Mult.
- apply multi_step with (ANum 15).
- apply AS_Plus.
- apply multi_refl.
-Qed.
-
-(** We repeatedly apply [multi_step] until we get to a normal
- form. The proofs that the intermediate steps are possible are
- simple enough that [auto], with appropriate hints, can solve
- them. *)
-
-Hint Constructors astep aval.
-Example astep_example1' :
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4))) / empty_state
- ==>a* (ANum 15).
-Proof.
- eapply multi_step. auto. simpl.
- eapply multi_step. auto. simpl.
- apply multi_refl.
-Qed.
-
-
-(** The following custom [Tactic Notation] definition captures this
- pattern. In addition, before each [multi_step] we print out the
- current goal, so that the user can follow how the term is being
- evaluated. *)
-
-Tactic Notation "print_goal" := match goal with |- ?x => idtac x end.
-Tactic Notation "normalize" :=
- repeat (print_goal; eapply multi_step ;
- [ (eauto 10; fail) | (instantiate; simpl)]);
- apply multi_refl.
-
-
-Example astep_example1'' :
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4))) / empty_state
- ==>a* (ANum 15).
-Proof.
- normalize.
- (* At this point in the proof script, the Coq response shows
- a trace of how the expression evaluated.
-
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4)) / empty_state ==>a* ANum 15)
- (multi (astep empty_state) (APlus (ANum 3) (ANum 12)) (ANum 15))
- (multi (astep empty_state) (ANum 15) (ANum 15))
-*)
-Qed.
-
-
-(** The [normalize] tactic also provides a simple way to calculate
- what the normal form of a term is, by proving a goal with an
- existential variable in it. *)
-
-Example astep_example1''' : exists e',
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4))) / empty_state
- ==>a* e'.
-Proof.
- eapply ex_intro. normalize.
-
-(* This time, the trace will be:
-
- (APlus (ANum 3) (AMult (ANum 3) (ANum 4)) / empty_state ==>a* ??)
- (multi (astep empty_state) (APlus (ANum 3) (ANum 12)) ??)
- (multi (astep empty_state) (ANum 15) ??)
-
- where ?? is the variable ``guessed'' by eapply.
-*)
-Qed.
-
-
-(** **** Exercise: 1 star (normalize_ex) *)
-Theorem normalize_ex : exists e',
- (AMult (ANum 3) (AMult (ANum 2) (ANum 1))) / empty_state
- ==>a* e'.
-Proof.
- (* FILL IN HERE *) Admitted.
-
-(** [] *)
-
-(** **** Exercise: 1 star, optional (normalize_ex') *)
-(** For comparison, prove it using [apply] instead of [eapply]. *)
-
-Theorem normalize_ex' : exists e',
- (AMult (ANum 3) (AMult (ANum 2) (ANum 1))) / empty_state
- ==>a* e'.
-Proof.
- (* FILL IN HERE *) Admitted.
-(** [] *)
-
-
-(* ###################################################################### *)
-(** ** Additional Exercises *)
-
-(** **** Exercise: 2 stars (subject_expansion) *)
-(** Having seen the subject reduction property, it is reasonable to
- wonder whether the opposity property -- subject _expansion_ --
- also holds. That is, is it always the case that, if [t ==> t']
- and [|- t' \in T], then [|- t \in T]? If so, prove it. If
- not, give a counter-example. (You do not need to prove your
- counter-example in Coq, but feel free to do so if you like.)
-
- (* FILL IN HERE *)
-[]
-*)
-
-
-
-
-(** **** Exercise: 2 stars (variation1) *)
-(** Suppose, that we add this new rule to the typing relation:
- | T_SuccBool : forall t,
- |- t \in TBool ->
- |- tsucc t \in TBool
- Which of the following properties remain true in the presence of
- this rule? For each one, write either "remains true" or
- else "becomes false." If a property becomes false, give a
- counterexample.
- - Determinism of [step]
-
- - Progress
-
- - Preservation
-
-[]
-*)
-
-(** **** Exercise: 2 stars (variation2) *)
-(** Suppose, instead, that we add this new rule to the [step] relation:
- | ST_Funny1 : forall t2 t3,
- (tif ttrue t2 t3) ==> t3
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (variation3) *)
-(** Suppose instead that we add this rule:
- | ST_Funny2 : forall t1 t2 t2' t3,
- t2 ==> t2' ->
- (tif t1 t2 t3) ==> (tif t1 t2' t3)
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (variation4) *)
-(** Suppose instead that we add this rule:
- | ST_Funny3 :
- (tpred tfalse) ==> (tpred (tpred tfalse))
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (variation5) *)
-(** Suppose instead that we add this rule:
-
- | T_Funny4 :
- |- tzero \in TBool
- ]]
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-[]
-*)
-
-(** **** Exercise: 2 stars, optional (variation6) *)
-(** Suppose instead that we add this rule:
-
- | T_Funny5 :
- |- tpred tzero \in TBool
- ]]
- Which of the above properties become false in the presence of
- this rule? For each one that does, give a counter-example.
-
-[]
-*)
-
-(** **** Exercise: 3 stars, optional (more_variations) *)
-(** Make up some exercises of your own along the same lines as
- the ones above. Try to find ways of selectively breaking
- properties -- i.e., ways of changing the definitions that
- break just one of the properties and leave the others alone.
- []
-*)
-
-(** **** Exercise: 1 star (remove_predzero) *)
-(** The evaluation rule [E_PredZero] is a bit counter-intuitive: we
- might feel that it makes more sense for the predecessor of zero to
- be undefined, rather than being defined to be zero. Can we
- achieve this simply by removing the rule from the definition of
- [step]? Would doing so create any problems elsewhere?
-
-(* FILL IN HERE *)
-[] *)
-
-(** **** Exercise: 4 stars, advanced (prog_pres_bigstep) *)
-(** Suppose our evaluation relation is defined in the big-step style.
- What are the appropriate analogs of the progress and preservation
- properties?
-
-(* FILL IN HERE *)
-[]
-*)
-
-(* $Date: 2014-04-08 23:31:16 -0400 (Tue, 08 Apr 2014) $ *)
diff --git a/coqdoc.css b/coqdoc.css
deleted file mode 100644
index 0de6e40..0000000
--- a/coqdoc.css
+++ /dev/null
@@ -1,389 +0,0 @@
-body { padding: 0px 0px;
- margin: 0px 0px;
- padding-left: 1em;
- background-color: white; }
-
-#page { display: block;
- padding: 0px;
- margin: 0px;
- padding-bottom: 10px; }
-
-#header { display: block;
- position: relative;
- padding: 0;
- margin: 0;
- vertical-align: middle;
- border-bottom-style: solid;
- border-width: thin }
-
-#header h1 { padding: 0;
- margin: 0;}
-
-/* Contents */
-
-#main{ display: block;
- padding: 10px;
- /* font-family: "Palatino Linotype", ‘Book Antiqua’, Palatino, serif; */
- font-family: "Times New Roman", Times, serif;
- /* font-family: Georgia, "Lucida Sans", sans-serif; */
-/* overflow: hidden; seems to break printing in Firefox */
- font-size: 100%;
- line-height: 100% }
-
-#main h1 { line-height: 80% } /* allow for multi-line headers */
-
-#main a.idref:visited {color : #416DFF; text-decoration : none; }
-#main a.idref:link {color : #416DFF; text-decoration : none; }
-#main a.idref:hover {text-decoration : none; }
-#main a.idref:active {text-decoration : none; }
-
-#main a.modref:visited {color : #416DFF; text-decoration : none; }
-#main a.modref:link {color : #416DFF; text-decoration : none; }
-#main a.modref:hover {text-decoration : none; }
-#main a.modref:active {text-decoration : none; }
-
-#main .keyword { color : #cf1d1d }
-#main { color: black }
-
-.section { background-color: rgb(60%,50%,100%);
- padding-top: 13px;
- padding-bottom: 13px;
- padding-left: 8px;
- margin-top: 5px;
- margin-bottom: 5px;
- margin-top: 12px;
- font-size : 175% }
-
-.libtitle { background-color: rgb(60%,50%,100%);
- padding-top: 22px;
- border-style: solid;
- padding-bottom: 22px;
- padding-left: 20px;
- padding-right: 20px;
- margin-top: 5px;
- margin-bottom: 5px;
- max-width: 13.5em; /* 15.5em; */
- text-align: center;
- letter-spacing: 1px;
- font-size : 240% }
-
-.subtitle { display: block;
- padding-top: 10px;
- font-size: 70%; }
-
-h2.section { background-color: rgb(80%,80%,100%);
- padding-left: 8px;
- padding-top: 12px;
- padding-bottom: 10px;
- margin-top: 10px;
- font-size : 130%; }
-
-h3.section { background-color: rgb(90%,90%,100%);
- padding-left: 8px;
- padding-top: 7px;
- padding-bottom: 7px;
- margin-top: 5px;
- font-size : 115% }
-
-h4.section {
-/*
- background-color: rgb(80%,80%,80%);
- max-width: 20em;
- padding-left: 5px;
- padding-top: 5px;
- padding-bottom: 5px;
-*/
- background-color: white;
- padding-left: 0px;
- padding-top: 0px;
- padding-bottom: 0.5em; /* 0px; */
- font-size : 100%;
- font-style : bold;
- text-decoration : underline;
- }
-
-#main .doc { margin: 0px;
- /* font-family: sans-serif; */
- font-size: 100%;
- line-height: 125%;
- max-width: 35em; /* 40em; */
- color: black;
- text-align: justify;
- border-style: plain}
-
-.inlinecode {
- display: inline;
-/* font-size: 125%; */
- color: #444444;
- font-family: monospace }
-
-.doc .inlinecode {
- display: inline;
- font-size: 115%;
- color: rgb(35%,35%,70%);
- font-family: monospace }
-
-.doc .inlinecode .id {
- color: rgb(35%,35%,70%);
-}
-
-.inlinecodenm {
- display: inline;
-/* font-size: 125%; */
- color: #444444;
-}
-
-.doc .inlinecodenm {
- display: inline;
- color: rgb(35%,35%,70%);
-}
-
-.doc .inlinecodenm .id {
- color: rgb(35%,35%,70%);
-}
-
-
-.doc .code {
- display: inline;
- font-size: 110%;
- color: rgb(35%,35%,70%);
- font-family: monospace;
- padding-left: 0px;
- }
-
-.comment {
- display: inline;
- font-family: monospace;
- color: rgb(50%,50%,80%);
-}
-
-.show {
- display: inline;
-/* background-color: rgb(95%,95%,95%); */
- font-family: monospace;
- font-size: 60%;
- padding-top: 0px;
- padding-bottom: 0px;
- padding-left: 10px;
- border: 1px;
- border-style: solid;
- color: rgb(75%,75%,85%);
-}
-
-/* Inline quizzes */
-.quiz:before {
- color: rgb(40%,40%,40%);
- /* content: "- Quick Check -" ; */
- display: block;
- text-align: center;
- margin-bottom: 5px;
-}
-.quiz {
- border: 4px;
- border-color: rgb(80%,80%,80%);
- margin-left: 40px;
- margin-right: 100px;
- padding: 5px;
- padding-left: 8px;
- padding-right: 8px;
- margin-top: 10px;
- margin-bottom: 15px;
- border-style: solid;
-}
-
-/* For textual ones... */
-.show-old {
- display: inline;
- font-family: monospace;
- font-size: 80%;
- padding-top: 0px;
- padding-bottom: 0px;
- padding-left: 3px;
- padding-right: 3px;
- border: 1px;
- margin-top: 5px; /* doesn't work?! */
- border-style: solid;
- color: rgb(75%,75%,85%);
-}
-
-.largebr {
- margin-top: 10px;
-}
-
-.code {
- display: block;
- padding-left: 12px;
- font-size: 110%;
- font-family: monospace;
- }
-
-.code-space {
- margin-top: 0em;
-}
-
-.code-tight {
- margin-top: -0.8em;
-}
-
-/*
-code.br {
- height: 5em;
-}
-*/
-
-table.infrule {
- border: 0px;
- margin-left: 50px;
- margin-top: 10px;
- margin-bottom: 10px;
-}
-
-td.infrule {
- font-family: monospace;
- text-align: center;
-/* color: rgb(35%,35%,70%); */
- padding: 0px;
- line-height: 100%;
-}
-
-tr.infrulemiddle hr {
- margin: 1px 0 1px 0;
-}
-
-.infrulenamecol {
- color: rgb(60%,60%,60%);
- font-size: 80%;
- padding-left: 1em;
- padding-bottom: 0.1em
-}
-
-/* Pied de page */
-
-#footer { font-size: 65%;
- font-family: sans-serif; }
-
-.id { display: inline; }
-
-.id[type="constructor"] {
- color: rgb(60%,0%,0%);
-}
-
-.id[type="var"] {
- color: rgb(40%,0%,40%);
-}
-
-.id[type="variable"] {
- color: rgb(40%,0%,40%);
-}
-
-.id[type="definition"] {
- color: rgb(0%,40%,0%);
-}
-
-.id[type="abbreviation"] {
- color: rgb(0%,40%,0%);
-}
-
-.id[type="lemma"] {
- color: rgb(0%,40%,0%);
-}
-
-.id[type="instance"] {
- color: rgb(0%,40%,0%);
-}
-
-.id[type="projection"] {
- color: rgb(0%,40%,0%);
-}
-
-.id[type="method"] {
- color: rgb(0%,40%,0%);
-}
-
-.id[type="inductive"] {
- color: rgb(0%,0%,80%);
-}
-
-.id[type="record"] {
- color: rgb(0%,0%,80%);
-}
-
-.id[type="class"] {
- color: rgb(0%,0%,80%);
-}
-
-.id[type="keyword"] {
- color : #cf1d1d;
-/* color: black; */
-}
-
-.inlinecode .id {
- color: rgb(0%,0%,0%);
-}
-
-
-/* TOC */
-
-#toc h2 {
- padding: 10px;
- line-height: 120%;
- background-color: rgb(60%,60%,100%);
-}
-
-#toc ul {
- padding-top: 8px;
- margin-bottom: -8px;
-}
-
-#toc li {
- padding-bottom: 8px;
-}
-
-/* Index */
-
-#index {
- margin: 0;
- padding: 0;
- width: 100%;
- font-style : normal;
-}
-
-#index #frontispiece {
- margin: auto;
- padding: 1em;
- width: 700px;
-}
-
-.booktitle { font-size : 600%; line-height: 100%; font-style:bold; }
-.authors { font-size : 200%;
- line-height: 115%; }
-.moreauthors { font-size : 170% }
-.buttons { font-size : 170%;
- margin-left: auto;
- margin-right: auto;
- font-style : bold;
- }
-
-A:link {text-decoration: none; color:black}
-A:visited {text-decoration: none; color:black}
-A:active {text-decoration: none; color:black}
-A:hover {text-decoration: none; color: #555555 }
-
-#index #entrance {
- text-align: center;
-}
-
-#index #footer {
- position: absolute;
- bottom: 0;
- text-align: bottom;
-}
-
-.paragraph {
- height: 0.6em;
-}
-
-ul.doclist {
- margin-top: 0em;
- margin-bottom: 0em;
-}
diff --git a/coqindex.html b/coqindex.html
deleted file mode 100644
index a9dcc87..0000000
--- a/coqindex.html
+++ /dev/null
@@ -1,239 +0,0 @@
-
-
-
-
-
-Index
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/deps.gif b/deps.gif
deleted file mode 100644
index 3333f04..0000000
Binary files a/deps.gif and /dev/null differ
diff --git a/deps.html b/deps.html
deleted file mode 100644
index 18cdf11..0000000
--- a/deps.html
+++ /dev/null
@@ -1,76 +0,0 @@
-X-Powered-By: PHP/5.3.17
-Content-type: text/html
-
-
-
-
-
-
-Chapter Dependencies
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Chapter Dependencies
-
-
-
-
-
-
diff --git a/deps.map b/deps.map
deleted file mode 100644
index dc0d82c..0000000
--- a/deps.map
+++ /dev/null
@@ -1,41 +0,0 @@
-
diff --git a/imp.ml b/imp.ml
deleted file mode 100644
index c5b3401..0000000
--- a/imp.ml
+++ /dev/null
@@ -1,6852 +0,0 @@
-type __ = Obj.t
-let __ = let rec f _ = Obj.repr f in Obj.repr f
-
-type unit0 =
-| Tt
-
-(** val negb : bool -> bool **)
-
-let negb = function
-| true -> false
-| false -> true
-
-type 'a option =
-| Some of 'a
-| None
-
-type ('a, 'b) prod =
-| Pair of 'a * 'b
-
-(** val fst : ('a1, 'a2) prod -> 'a1 **)
-
-let fst = function
-| Pair (x, y) -> x
-
-(** val snd : ('a1, 'a2) prod -> 'a2 **)
-
-let snd = function
-| Pair (x, y) -> y
-
-type 'a list =
-| Nil
-| Cons of 'a * 'a list
-
-(** val app : 'a1 list -> 'a1 list -> 'a1 list **)
-
-let rec app l m =
- match l with
- | Nil -> m
- | Cons (a, l1) -> Cons (a, (app l1 m))
-
-type comparison =
-| Eq
-| Lt
-| Gt
-
-type compareSpecT =
-| CompEqT
-| CompLtT
-| CompGtT
-
-(** val compareSpec2Type : comparison -> compareSpecT **)
-
-let compareSpec2Type = function
-| Eq -> CompEqT
-| Lt -> CompLtT
-| Gt -> CompGtT
-
-type 'a compSpecT = compareSpecT
-
-(** val compSpec2Type : 'a1 -> 'a1 -> comparison -> 'a1 compSpecT **)
-
-let compSpec2Type x y c =
- compareSpec2Type c
-
-type 'a sig0 =
- 'a
- (* singleton inductive, whose constructor was exist *)
-
-type 'a sumor =
-| Inleft of 'a
-| Inright
-
-(** val plus : int -> int -> int **)
-
-let rec plus = ( + )
-
-(** val mult : int -> int -> int **)
-
-let rec mult = ( * )
-
-(** val minus : int -> int -> int **)
-
-let rec minus n0 m =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- n0)
- (fun k ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- n0)
- (fun l ->
- minus k l)
- m)
- n0
-
-(** val nat_iter : int -> ('a1 -> 'a1) -> 'a1 -> 'a1 **)
-
-let rec nat_iter n0 f x =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- x)
- (fun n' ->
- f (nat_iter n' f x))
- n0
-
-type positive =
-| XI of positive
-| XO of positive
-| XH
-
-type n =
-| N0
-| Npos of positive
-
-type reflect =
-| ReflectT
-| ReflectF
-
-(** val iff_reflect : bool -> reflect **)
-
-let iff_reflect = function
-| true -> ReflectT
-| false -> ReflectF
-
-module type TotalOrder' =
- sig
- type t
- end
-
-module MakeOrderTac =
- functor (O:TotalOrder') ->
- struct
-
- end
-
-module MaxLogicalProperties =
- functor (O:TotalOrder') ->
- functor (M:sig
- val max : O.t -> O.t -> O.t
- end) ->
- struct
- module Private_Tac = MakeOrderTac(O)
- end
-
-module Pos =
- struct
- type t = positive
-
- (** val succ : positive -> positive **)
-
- let rec succ = function
- | XI p -> XO (succ p)
- | XO p -> XI p
- | XH -> XO XH
-
- (** val add : positive -> positive -> positive **)
-
- let rec add x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> XO (add_carry p q)
- | XO q -> XI (add p q)
- | XH -> XO (succ p))
- | XO p ->
- (match y with
- | XI q -> XI (add p q)
- | XO q -> XO (add p q)
- | XH -> XI p)
- | XH ->
- (match y with
- | XI q -> XO (succ q)
- | XO q -> XI q
- | XH -> XO XH)
-
- (** val add_carry : positive -> positive -> positive **)
-
- and add_carry x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> XI (add_carry p q)
- | XO q -> XO (add_carry p q)
- | XH -> XI (succ p))
- | XO p ->
- (match y with
- | XI q -> XO (add_carry p q)
- | XO q -> XI (add p q)
- | XH -> XO (succ p))
- | XH ->
- (match y with
- | XI q -> XI (succ q)
- | XO q -> XO (succ q)
- | XH -> XI XH)
-
- (** val pred_double : positive -> positive **)
-
- let rec pred_double = function
- | XI p -> XI (XO p)
- | XO p -> XI (pred_double p)
- | XH -> XH
-
- (** val pred : positive -> positive **)
-
- let pred = function
- | XI p -> XO p
- | XO p -> pred_double p
- | XH -> XH
-
- (** val pred_N : positive -> n **)
-
- let pred_N = function
- | XI p -> Npos (XO p)
- | XO p -> Npos (pred_double p)
- | XH -> N0
-
- type mask =
- | IsNul
- | IsPos of positive
- | IsNeg
-
- (** val mask_rect : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1 **)
-
- let mask_rect f f0 f1 = function
- | IsNul -> f
- | IsPos x -> f0 x
- | IsNeg -> f1
-
- (** val mask_rec : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1 **)
-
- let mask_rec f f0 f1 = function
- | IsNul -> f
- | IsPos x -> f0 x
- | IsNeg -> f1
-
- (** val succ_double_mask : mask -> mask **)
-
- let succ_double_mask = function
- | IsNul -> IsPos XH
- | IsPos p -> IsPos (XI p)
- | IsNeg -> IsNeg
-
- (** val double_mask : mask -> mask **)
-
- let double_mask = function
- | IsPos p -> IsPos (XO p)
- | x0 -> x0
-
- (** val double_pred_mask : positive -> mask **)
-
- let double_pred_mask = function
- | XI p -> IsPos (XO (XO p))
- | XO p -> IsPos (XO (pred_double p))
- | XH -> IsNul
-
- (** val pred_mask : mask -> mask **)
-
- let pred_mask = function
- | IsPos q ->
- (match q with
- | XH -> IsNul
- | _ -> IsPos (pred q))
- | _ -> IsNeg
-
- (** val sub_mask : positive -> positive -> mask **)
-
- let rec sub_mask x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> double_mask (sub_mask p q)
- | XO q -> succ_double_mask (sub_mask p q)
- | XH -> IsPos (XO p))
- | XO p ->
- (match y with
- | XI q -> succ_double_mask (sub_mask_carry p q)
- | XO q -> double_mask (sub_mask p q)
- | XH -> IsPos (pred_double p))
- | XH ->
- (match y with
- | XH -> IsNul
- | _ -> IsNeg)
-
- (** val sub_mask_carry : positive -> positive -> mask **)
-
- and sub_mask_carry x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> succ_double_mask (sub_mask_carry p q)
- | XO q -> double_mask (sub_mask p q)
- | XH -> IsPos (pred_double p))
- | XO p ->
- (match y with
- | XI q -> double_mask (sub_mask_carry p q)
- | XO q -> succ_double_mask (sub_mask_carry p q)
- | XH -> double_pred_mask p)
- | XH -> IsNeg
-
- (** val sub : positive -> positive -> positive **)
-
- let sub x y =
- match sub_mask x y with
- | IsPos z -> z
- | _ -> XH
-
- (** val mul : positive -> positive -> positive **)
-
- let rec mul x y =
- match x with
- | XI p -> add y (XO (mul p y))
- | XO p -> XO (mul p y)
- | XH -> y
-
- (** val iter : positive -> ('a1 -> 'a1) -> 'a1 -> 'a1 **)
-
- let rec iter n0 f x =
- match n0 with
- | XI n' -> f (iter n' f (iter n' f x))
- | XO n' -> iter n' f (iter n' f x)
- | XH -> f x
-
- (** val pow : positive -> positive -> positive **)
-
- let pow x y =
- iter y (mul x) XH
-
- (** val square : positive -> positive **)
-
- let rec square = function
- | XI p0 -> XI (XO (add (square p0) p0))
- | XO p0 -> XO (XO (square p0))
- | XH -> XH
-
- (** val div2 : positive -> positive **)
-
- let div2 = function
- | XI p0 -> p0
- | XO p0 -> p0
- | XH -> XH
-
- (** val div2_up : positive -> positive **)
-
- let div2_up = function
- | XI p0 -> succ p0
- | XO p0 -> p0
- | XH -> XH
-
- (** val size_nat : positive -> int **)
-
- let rec size_nat = function
- | XI p0 -> (fun x -> x + 1) (size_nat p0)
- | XO p0 -> (fun x -> x + 1) (size_nat p0)
- | XH -> (fun x -> x + 1) 0
-
- (** val size : positive -> positive **)
-
- let rec size = function
- | XI p0 -> succ (size p0)
- | XO p0 -> succ (size p0)
- | XH -> XH
-
- (** val compare_cont : positive -> positive -> comparison -> comparison **)
-
- let rec compare_cont x y r =
- match x with
- | XI p ->
- (match y with
- | XI q -> compare_cont p q r
- | XO q -> compare_cont p q Gt
- | XH -> Gt)
- | XO p ->
- (match y with
- | XI q -> compare_cont p q Lt
- | XO q -> compare_cont p q r
- | XH -> Gt)
- | XH ->
- (match y with
- | XH -> r
- | _ -> Lt)
-
- (** val compare : positive -> positive -> comparison **)
-
- let compare x y =
- compare_cont x y Eq
-
- (** val min : positive -> positive -> positive **)
-
- let min p p' =
- match compare p p' with
- | Gt -> p'
- | _ -> p
-
- (** val max : positive -> positive -> positive **)
-
- let max p p' =
- match compare p p' with
- | Gt -> p
- | _ -> p'
-
- (** val eqb : positive -> positive -> bool **)
-
- let rec eqb p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> eqb p0 q0
- | _ -> false)
- | XO p0 ->
- (match q with
- | XO q0 -> eqb p0 q0
- | _ -> false)
- | XH ->
- (match q with
- | XH -> true
- | _ -> false)
-
- (** val leb : positive -> positive -> bool **)
-
- let leb x y =
- match compare x y with
- | Gt -> false
- | _ -> true
-
- (** val ltb : positive -> positive -> bool **)
-
- let ltb x y =
- match compare x y with
- | Lt -> true
- | _ -> false
-
- (** val sqrtrem_step :
- (positive -> positive) -> (positive -> positive) -> (positive, mask)
- prod -> (positive, mask) prod **)
-
- let sqrtrem_step f g = function
- | Pair (s, y) ->
- (match y with
- | IsPos r ->
- let s' = XI (XO s) in
- let r' = g (f r) in
- if leb s' r'
- then Pair ((XI s), (sub_mask r' s'))
- else Pair ((XO s), (IsPos r'))
- | _ -> Pair ((XO s), (sub_mask (g (f XH)) (XO (XO XH)))))
-
- (** val sqrtrem : positive -> (positive, mask) prod **)
-
- let rec sqrtrem = function
- | XI p0 ->
- (match p0 with
- | XI p1 -> sqrtrem_step (fun x -> XI x) (fun x -> XI x) (sqrtrem p1)
- | XO p1 -> sqrtrem_step (fun x -> XO x) (fun x -> XI x) (sqrtrem p1)
- | XH -> Pair (XH, (IsPos (XO XH))))
- | XO p0 ->
- (match p0 with
- | XI p1 -> sqrtrem_step (fun x -> XI x) (fun x -> XO x) (sqrtrem p1)
- | XO p1 -> sqrtrem_step (fun x -> XO x) (fun x -> XO x) (sqrtrem p1)
- | XH -> Pair (XH, (IsPos XH)))
- | XH -> Pair (XH, IsNul)
-
- (** val sqrt : positive -> positive **)
-
- let sqrt p =
- fst (sqrtrem p)
-
- (** val gcdn : int -> positive -> positive -> positive **)
-
- let rec gcdn n0 a b =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun n1 ->
- match a with
- | XI a' ->
- (match b with
- | XI b' ->
- (match compare a' b' with
- | Eq -> a
- | Lt -> gcdn n1 (sub b' a') a
- | Gt -> gcdn n1 (sub a' b') b)
- | XO b0 -> gcdn n1 a b0
- | XH -> XH)
- | XO a0 ->
- (match b with
- | XI p -> gcdn n1 a0 b
- | XO b0 -> XO (gcdn n1 a0 b0)
- | XH -> XH)
- | XH -> XH)
- n0
-
- (** val gcd : positive -> positive -> positive **)
-
- let gcd a b =
- gcdn (plus (size_nat a) (size_nat b)) a b
-
- (** val ggcdn :
- int -> positive -> positive -> (positive, (positive, positive) prod)
- prod **)
-
- let rec ggcdn n0 a b =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> Pair (XH, (Pair (a,
- b))))
- (fun n1 ->
- match a with
- | XI a' ->
- (match b with
- | XI b' ->
- (match compare a' b' with
- | Eq -> Pair (a, (Pair (XH, XH)))
- | Lt ->
- let Pair (g, p) = ggcdn n1 (sub b' a') a in
- let Pair (ba, aa) = p in
- Pair (g, (Pair (aa, (add aa (XO ba)))))
- | Gt ->
- let Pair (g, p) = ggcdn n1 (sub a' b') b in
- let Pair (ab, bb) = p in
- Pair (g, (Pair ((add bb (XO ab)), bb))))
- | XO b0 ->
- let Pair (g, p) = ggcdn n1 a b0 in
- let Pair (aa, bb) = p in Pair (g, (Pair (aa, (XO bb))))
- | XH -> Pair (XH, (Pair (a, XH))))
- | XO a0 ->
- (match b with
- | XI p ->
- let Pair (g, p0) = ggcdn n1 a0 b in
- let Pair (aa, bb) = p0 in Pair (g, (Pair ((XO aa), bb)))
- | XO b0 -> let Pair (g, p) = ggcdn n1 a0 b0 in Pair ((XO g), p)
- | XH -> Pair (XH, (Pair (a, XH))))
- | XH -> Pair (XH, (Pair (XH, b))))
- n0
-
- (** val ggcd :
- positive -> positive -> (positive, (positive, positive) prod) prod **)
-
- let ggcd a b =
- ggcdn (plus (size_nat a) (size_nat b)) a b
-
- (** val coq_Nsucc_double : n -> n **)
-
- let coq_Nsucc_double = function
- | N0 -> Npos XH
- | Npos p -> Npos (XI p)
-
- (** val coq_Ndouble : n -> n **)
-
- let coq_Ndouble = function
- | N0 -> N0
- | Npos p -> Npos (XO p)
-
- (** val coq_lor : positive -> positive -> positive **)
-
- let rec coq_lor p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> XI (coq_lor p0 q0)
- | XO q0 -> XI (coq_lor p0 q0)
- | XH -> p)
- | XO p0 ->
- (match q with
- | XI q0 -> XI (coq_lor p0 q0)
- | XO q0 -> XO (coq_lor p0 q0)
- | XH -> XI p0)
- | XH ->
- (match q with
- | XO q0 -> XI q0
- | _ -> q)
-
- (** val coq_land : positive -> positive -> n **)
-
- let rec coq_land p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> coq_Nsucc_double (coq_land p0 q0)
- | XO q0 -> coq_Ndouble (coq_land p0 q0)
- | XH -> Npos XH)
- | XO p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (coq_land p0 q0)
- | XO q0 -> coq_Ndouble (coq_land p0 q0)
- | XH -> N0)
- | XH ->
- (match q with
- | XO q0 -> N0
- | _ -> Npos XH)
-
- (** val ldiff : positive -> positive -> n **)
-
- let rec ldiff p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (ldiff p0 q0)
- | XO q0 -> coq_Nsucc_double (ldiff p0 q0)
- | XH -> Npos (XO p0))
- | XO p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (ldiff p0 q0)
- | XO q0 -> coq_Ndouble (ldiff p0 q0)
- | XH -> Npos p)
- | XH ->
- (match q with
- | XO q0 -> Npos XH
- | _ -> N0)
-
- (** val coq_lxor : positive -> positive -> n **)
-
- let rec coq_lxor p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (coq_lxor p0 q0)
- | XO q0 -> coq_Nsucc_double (coq_lxor p0 q0)
- | XH -> Npos (XO p0))
- | XO p0 ->
- (match q with
- | XI q0 -> coq_Nsucc_double (coq_lxor p0 q0)
- | XO q0 -> coq_Ndouble (coq_lxor p0 q0)
- | XH -> Npos (XI p0))
- | XH ->
- (match q with
- | XI q0 -> Npos (XO q0)
- | XO q0 -> Npos (XI q0)
- | XH -> N0)
-
- (** val shiftl_nat : positive -> int -> positive **)
-
- let shiftl_nat p n0 =
- nat_iter n0 (fun x -> XO x) p
-
- (** val shiftr_nat : positive -> int -> positive **)
-
- let shiftr_nat p n0 =
- nat_iter n0 div2 p
-
- (** val shiftl : positive -> n -> positive **)
-
- let shiftl p = function
- | N0 -> p
- | Npos n1 -> iter n1 (fun x -> XO x) p
-
- (** val shiftr : positive -> n -> positive **)
-
- let shiftr p = function
- | N0 -> p
- | Npos n1 -> iter n1 div2 p
-
- (** val testbit_nat : positive -> int -> bool **)
-
- let rec testbit_nat p n0 =
- match p with
- | XI p0 ->
- ((fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun n' ->
- testbit_nat p0 n')
- n0)
- | XO p0 ->
- ((fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- false)
- (fun n' ->
- testbit_nat p0 n')
- n0)
- | XH ->
- ((fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun n1 ->
- false)
- n0)
-
- (** val testbit : positive -> n -> bool **)
-
- let rec testbit p n0 =
- match p with
- | XI p0 ->
- (match n0 with
- | N0 -> true
- | Npos n1 -> testbit p0 (pred_N n1))
- | XO p0 ->
- (match n0 with
- | N0 -> false
- | Npos n1 -> testbit p0 (pred_N n1))
- | XH ->
- (match n0 with
- | N0 -> true
- | Npos p0 -> false)
-
- (** val iter_op : ('a1 -> 'a1 -> 'a1) -> positive -> 'a1 -> 'a1 **)
-
- let rec iter_op op p a =
- match p with
- | XI p0 -> op a (iter_op op p0 (op a a))
- | XO p0 -> iter_op op p0 (op a a)
- | XH -> a
-
- (** val to_nat : positive -> int **)
-
- let to_nat x =
- iter_op plus x ((fun x -> x + 1) 0)
-
- (** val of_nat : int -> positive **)
-
- let rec of_nat n0 =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun x ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun n1 ->
- succ (of_nat x))
- x)
- n0
-
- (** val of_succ_nat : int -> positive **)
-
- let rec of_succ_nat n0 =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun x ->
- succ (of_succ_nat x))
- n0
- end
-
-module Coq_Pos =
- struct
- type t = positive
-
- (** val succ : positive -> positive **)
-
- let rec succ = function
- | XI p -> XO (succ p)
- | XO p -> XI p
- | XH -> XO XH
-
- (** val add : positive -> positive -> positive **)
-
- let rec add x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> XO (add_carry p q)
- | XO q -> XI (add p q)
- | XH -> XO (succ p))
- | XO p ->
- (match y with
- | XI q -> XI (add p q)
- | XO q -> XO (add p q)
- | XH -> XI p)
- | XH ->
- (match y with
- | XI q -> XO (succ q)
- | XO q -> XI q
- | XH -> XO XH)
-
- (** val add_carry : positive -> positive -> positive **)
-
- and add_carry x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> XI (add_carry p q)
- | XO q -> XO (add_carry p q)
- | XH -> XI (succ p))
- | XO p ->
- (match y with
- | XI q -> XO (add_carry p q)
- | XO q -> XI (add p q)
- | XH -> XO (succ p))
- | XH ->
- (match y with
- | XI q -> XI (succ q)
- | XO q -> XO (succ q)
- | XH -> XI XH)
-
- (** val pred_double : positive -> positive **)
-
- let rec pred_double = function
- | XI p -> XI (XO p)
- | XO p -> XI (pred_double p)
- | XH -> XH
-
- (** val pred : positive -> positive **)
-
- let pred = function
- | XI p -> XO p
- | XO p -> pred_double p
- | XH -> XH
-
- (** val pred_N : positive -> n **)
-
- let pred_N = function
- | XI p -> Npos (XO p)
- | XO p -> Npos (pred_double p)
- | XH -> N0
-
- type mask = Pos.mask =
- | IsNul
- | IsPos of positive
- | IsNeg
-
- (** val mask_rect : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1 **)
-
- let mask_rect f f0 f1 = function
- | IsNul -> f
- | IsPos x -> f0 x
- | IsNeg -> f1
-
- (** val mask_rec : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1 **)
-
- let mask_rec f f0 f1 = function
- | IsNul -> f
- | IsPos x -> f0 x
- | IsNeg -> f1
-
- (** val succ_double_mask : mask -> mask **)
-
- let succ_double_mask = function
- | IsNul -> IsPos XH
- | IsPos p -> IsPos (XI p)
- | IsNeg -> IsNeg
-
- (** val double_mask : mask -> mask **)
-
- let double_mask = function
- | IsPos p -> IsPos (XO p)
- | x0 -> x0
-
- (** val double_pred_mask : positive -> mask **)
-
- let double_pred_mask = function
- | XI p -> IsPos (XO (XO p))
- | XO p -> IsPos (XO (pred_double p))
- | XH -> IsNul
-
- (** val pred_mask : mask -> mask **)
-
- let pred_mask = function
- | IsPos q ->
- (match q with
- | XH -> IsNul
- | _ -> IsPos (pred q))
- | _ -> IsNeg
-
- (** val sub_mask : positive -> positive -> mask **)
-
- let rec sub_mask x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> double_mask (sub_mask p q)
- | XO q -> succ_double_mask (sub_mask p q)
- | XH -> IsPos (XO p))
- | XO p ->
- (match y with
- | XI q -> succ_double_mask (sub_mask_carry p q)
- | XO q -> double_mask (sub_mask p q)
- | XH -> IsPos (pred_double p))
- | XH ->
- (match y with
- | XH -> IsNul
- | _ -> IsNeg)
-
- (** val sub_mask_carry : positive -> positive -> mask **)
-
- and sub_mask_carry x y =
- match x with
- | XI p ->
- (match y with
- | XI q -> succ_double_mask (sub_mask_carry p q)
- | XO q -> double_mask (sub_mask p q)
- | XH -> IsPos (pred_double p))
- | XO p ->
- (match y with
- | XI q -> double_mask (sub_mask_carry p q)
- | XO q -> succ_double_mask (sub_mask_carry p q)
- | XH -> double_pred_mask p)
- | XH -> IsNeg
-
- (** val sub : positive -> positive -> positive **)
-
- let sub x y =
- match sub_mask x y with
- | IsPos z -> z
- | _ -> XH
-
- (** val mul : positive -> positive -> positive **)
-
- let rec mul x y =
- match x with
- | XI p -> add y (XO (mul p y))
- | XO p -> XO (mul p y)
- | XH -> y
-
- (** val iter : positive -> ('a1 -> 'a1) -> 'a1 -> 'a1 **)
-
- let rec iter n0 f x =
- match n0 with
- | XI n' -> f (iter n' f (iter n' f x))
- | XO n' -> iter n' f (iter n' f x)
- | XH -> f x
-
- (** val pow : positive -> positive -> positive **)
-
- let pow x y =
- iter y (mul x) XH
-
- (** val square : positive -> positive **)
-
- let rec square = function
- | XI p0 -> XI (XO (add (square p0) p0))
- | XO p0 -> XO (XO (square p0))
- | XH -> XH
-
- (** val div2 : positive -> positive **)
-
- let div2 = function
- | XI p0 -> p0
- | XO p0 -> p0
- | XH -> XH
-
- (** val div2_up : positive -> positive **)
-
- let div2_up = function
- | XI p0 -> succ p0
- | XO p0 -> p0
- | XH -> XH
-
- (** val size_nat : positive -> int **)
-
- let rec size_nat = function
- | XI p0 -> (fun x -> x + 1) (size_nat p0)
- | XO p0 -> (fun x -> x + 1) (size_nat p0)
- | XH -> (fun x -> x + 1) 0
-
- (** val size : positive -> positive **)
-
- let rec size = function
- | XI p0 -> succ (size p0)
- | XO p0 -> succ (size p0)
- | XH -> XH
-
- (** val compare_cont : positive -> positive -> comparison -> comparison **)
-
- let rec compare_cont x y r =
- match x with
- | XI p ->
- (match y with
- | XI q -> compare_cont p q r
- | XO q -> compare_cont p q Gt
- | XH -> Gt)
- | XO p ->
- (match y with
- | XI q -> compare_cont p q Lt
- | XO q -> compare_cont p q r
- | XH -> Gt)
- | XH ->
- (match y with
- | XH -> r
- | _ -> Lt)
-
- (** val compare : positive -> positive -> comparison **)
-
- let compare x y =
- compare_cont x y Eq
-
- (** val min : positive -> positive -> positive **)
-
- let min p p' =
- match compare p p' with
- | Gt -> p'
- | _ -> p
-
- (** val max : positive -> positive -> positive **)
-
- let max p p' =
- match compare p p' with
- | Gt -> p
- | _ -> p'
-
- (** val eqb : positive -> positive -> bool **)
-
- let rec eqb p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> eqb p0 q0
- | _ -> false)
- | XO p0 ->
- (match q with
- | XO q0 -> eqb p0 q0
- | _ -> false)
- | XH ->
- (match q with
- | XH -> true
- | _ -> false)
-
- (** val leb : positive -> positive -> bool **)
-
- let leb x y =
- match compare x y with
- | Gt -> false
- | _ -> true
-
- (** val ltb : positive -> positive -> bool **)
-
- let ltb x y =
- match compare x y with
- | Lt -> true
- | _ -> false
-
- (** val sqrtrem_step :
- (positive -> positive) -> (positive -> positive) -> (positive, mask)
- prod -> (positive, mask) prod **)
-
- let sqrtrem_step f g = function
- | Pair (s, y) ->
- (match y with
- | IsPos r ->
- let s' = XI (XO s) in
- let r' = g (f r) in
- if leb s' r'
- then Pair ((XI s), (sub_mask r' s'))
- else Pair ((XO s), (IsPos r'))
- | _ -> Pair ((XO s), (sub_mask (g (f XH)) (XO (XO XH)))))
-
- (** val sqrtrem : positive -> (positive, mask) prod **)
-
- let rec sqrtrem = function
- | XI p0 ->
- (match p0 with
- | XI p1 -> sqrtrem_step (fun x -> XI x) (fun x -> XI x) (sqrtrem p1)
- | XO p1 -> sqrtrem_step (fun x -> XO x) (fun x -> XI x) (sqrtrem p1)
- | XH -> Pair (XH, (IsPos (XO XH))))
- | XO p0 ->
- (match p0 with
- | XI p1 -> sqrtrem_step (fun x -> XI x) (fun x -> XO x) (sqrtrem p1)
- | XO p1 -> sqrtrem_step (fun x -> XO x) (fun x -> XO x) (sqrtrem p1)
- | XH -> Pair (XH, (IsPos XH)))
- | XH -> Pair (XH, IsNul)
-
- (** val sqrt : positive -> positive **)
-
- let sqrt p =
- fst (sqrtrem p)
-
- (** val gcdn : int -> positive -> positive -> positive **)
-
- let rec gcdn n0 a b =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun n1 ->
- match a with
- | XI a' ->
- (match b with
- | XI b' ->
- (match compare a' b' with
- | Eq -> a
- | Lt -> gcdn n1 (sub b' a') a
- | Gt -> gcdn n1 (sub a' b') b)
- | XO b0 -> gcdn n1 a b0
- | XH -> XH)
- | XO a0 ->
- (match b with
- | XI p -> gcdn n1 a0 b
- | XO b0 -> XO (gcdn n1 a0 b0)
- | XH -> XH)
- | XH -> XH)
- n0
-
- (** val gcd : positive -> positive -> positive **)
-
- let gcd a b =
- gcdn (plus (size_nat a) (size_nat b)) a b
-
- (** val ggcdn :
- int -> positive -> positive -> (positive, (positive, positive) prod)
- prod **)
-
- let rec ggcdn n0 a b =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> Pair (XH, (Pair (a,
- b))))
- (fun n1 ->
- match a with
- | XI a' ->
- (match b with
- | XI b' ->
- (match compare a' b' with
- | Eq -> Pair (a, (Pair (XH, XH)))
- | Lt ->
- let Pair (g, p) = ggcdn n1 (sub b' a') a in
- let Pair (ba, aa) = p in
- Pair (g, (Pair (aa, (add aa (XO ba)))))
- | Gt ->
- let Pair (g, p) = ggcdn n1 (sub a' b') b in
- let Pair (ab, bb) = p in
- Pair (g, (Pair ((add bb (XO ab)), bb))))
- | XO b0 ->
- let Pair (g, p) = ggcdn n1 a b0 in
- let Pair (aa, bb) = p in Pair (g, (Pair (aa, (XO bb))))
- | XH -> Pair (XH, (Pair (a, XH))))
- | XO a0 ->
- (match b with
- | XI p ->
- let Pair (g, p0) = ggcdn n1 a0 b in
- let Pair (aa, bb) = p0 in Pair (g, (Pair ((XO aa), bb)))
- | XO b0 -> let Pair (g, p) = ggcdn n1 a0 b0 in Pair ((XO g), p)
- | XH -> Pair (XH, (Pair (a, XH))))
- | XH -> Pair (XH, (Pair (XH, b))))
- n0
-
- (** val ggcd :
- positive -> positive -> (positive, (positive, positive) prod) prod **)
-
- let ggcd a b =
- ggcdn (plus (size_nat a) (size_nat b)) a b
-
- (** val coq_Nsucc_double : n -> n **)
-
- let coq_Nsucc_double = function
- | N0 -> Npos XH
- | Npos p -> Npos (XI p)
-
- (** val coq_Ndouble : n -> n **)
-
- let coq_Ndouble = function
- | N0 -> N0
- | Npos p -> Npos (XO p)
-
- (** val coq_lor : positive -> positive -> positive **)
-
- let rec coq_lor p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> XI (coq_lor p0 q0)
- | XO q0 -> XI (coq_lor p0 q0)
- | XH -> p)
- | XO p0 ->
- (match q with
- | XI q0 -> XI (coq_lor p0 q0)
- | XO q0 -> XO (coq_lor p0 q0)
- | XH -> XI p0)
- | XH ->
- (match q with
- | XO q0 -> XI q0
- | _ -> q)
-
- (** val coq_land : positive -> positive -> n **)
-
- let rec coq_land p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> coq_Nsucc_double (coq_land p0 q0)
- | XO q0 -> coq_Ndouble (coq_land p0 q0)
- | XH -> Npos XH)
- | XO p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (coq_land p0 q0)
- | XO q0 -> coq_Ndouble (coq_land p0 q0)
- | XH -> N0)
- | XH ->
- (match q with
- | XO q0 -> N0
- | _ -> Npos XH)
-
- (** val ldiff : positive -> positive -> n **)
-
- let rec ldiff p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (ldiff p0 q0)
- | XO q0 -> coq_Nsucc_double (ldiff p0 q0)
- | XH -> Npos (XO p0))
- | XO p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (ldiff p0 q0)
- | XO q0 -> coq_Ndouble (ldiff p0 q0)
- | XH -> Npos p)
- | XH ->
- (match q with
- | XO q0 -> Npos XH
- | _ -> N0)
-
- (** val coq_lxor : positive -> positive -> n **)
-
- let rec coq_lxor p q =
- match p with
- | XI p0 ->
- (match q with
- | XI q0 -> coq_Ndouble (coq_lxor p0 q0)
- | XO q0 -> coq_Nsucc_double (coq_lxor p0 q0)
- | XH -> Npos (XO p0))
- | XO p0 ->
- (match q with
- | XI q0 -> coq_Nsucc_double (coq_lxor p0 q0)
- | XO q0 -> coq_Ndouble (coq_lxor p0 q0)
- | XH -> Npos (XI p0))
- | XH ->
- (match q with
- | XI q0 -> Npos (XO q0)
- | XO q0 -> Npos (XI q0)
- | XH -> N0)
-
- (** val shiftl_nat : positive -> int -> positive **)
-
- let shiftl_nat p n0 =
- nat_iter n0 (fun x -> XO x) p
-
- (** val shiftr_nat : positive -> int -> positive **)
-
- let shiftr_nat p n0 =
- nat_iter n0 div2 p
-
- (** val shiftl : positive -> n -> positive **)
-
- let shiftl p = function
- | N0 ->
- p
- | Npos n1 ->
- iter
- n1
- (fun x ->
- XO
- x)
- p
-
- (** val shiftr :
- positive
- ->
- n
- ->
- positive **)
-
- let shiftr p = function
- | N0 ->
- p
- | Npos n1 ->
- iter
- n1
- div2
- p
-
- (** val testbit_nat :
- positive
- ->
- int
- ->
- bool **)
-
- let rec testbit_nat p n0 =
- match p with
- | XI p0 ->
- ((fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun n' ->
- testbit_nat
- p0
- n')
- n0)
- | XO p0 ->
- ((fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- false)
- (fun n' ->
- testbit_nat
- p0
- n')
- n0)
- | XH ->
- ((fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun n1 ->
- false)
- n0)
-
- (** val testbit :
- positive
- ->
- n
- ->
- bool **)
-
- let rec testbit p n0 =
- match p with
- | XI p0 ->
- (match n0 with
- | N0 ->
- true
- | Npos n1 ->
- testbit
- p0
- (pred_N
- n1))
- | XO p0 ->
- (match n0 with
- | N0 ->
- false
- | Npos n1 ->
- testbit
- p0
- (pred_N
- n1))
- | XH ->
- (match n0 with
- | N0 ->
- true
- | Npos p0 ->
- false)
-
- (** val iter_op :
- ('a1
- ->
- 'a1
- ->
- 'a1)
- ->
- positive
- ->
- 'a1
- ->
- 'a1 **)
-
- let rec iter_op op p a =
- match p with
- | XI p0 ->
- op
- a
- (iter_op
- op
- p0
- (op
- a
- a))
- | XO p0 ->
- iter_op
- op
- p0
- (op
- a
- a)
- | XH ->
- a
-
- (** val to_nat :
- positive
- ->
- int **)
-
- let to_nat x =
- iter_op
- plus
- x
- ((fun x -> x + 1)
- 0)
-
- (** val of_nat :
- int
- ->
- positive **)
-
- let rec of_nat n0 =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun x ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun n1 ->
- succ
- (of_nat
- x))
- x)
- n0
-
- (** val of_succ_nat :
- int
- ->
- positive **)
-
- let rec of_succ_nat n0 =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- XH)
- (fun x ->
- succ
- (of_succ_nat
- x))
- n0
-
- (** val eq_dec :
- positive
- ->
- positive
- ->
- bool **)
-
- let rec eq_dec p y0 =
- match p with
- | XI p0 ->
- (match y0 with
- | XI p1 ->
- eq_dec
- p0
- p1
- | _ ->
- false)
- | XO p0 ->
- (match y0 with
- | XO p1 ->
- eq_dec
- p0
- p1
- | _ ->
- false)
- | XH ->
- (match y0 with
- | XH ->
- true
- | _ ->
- false)
-
- (** val peano_rect :
- 'a1
- ->
- (positive
- ->
- 'a1
- ->
- 'a1)
- ->
- positive
- ->
- 'a1 **)
-
- let rec peano_rect a f p =
- let f2 =
- peano_rect
- (f
- XH
- a)
- (fun p0 x ->
- f
- (succ
- (XO
- p0))
- (f
- (XO
- p0)
- x))
- in
- (match p with
- | XI q ->
- f
- (XO
- q)
- (f2
- q)
- | XO q ->
- f2
- q
- | XH ->
- a)
-
- (** val peano_rec :
- 'a1
- ->
- (positive
- ->
- 'a1
- ->
- 'a1)
- ->
- positive
- ->
- 'a1 **)
-
- let peano_rec =
- peano_rect
-
- type coq_PeanoView =
- | PeanoOne
- | PeanoSucc of positive
- * coq_PeanoView
-
- (** val coq_PeanoView_rect :
- 'a1
- ->
- (positive
- ->
- coq_PeanoView
- ->
- 'a1
- ->
- 'a1)
- ->
- positive
- ->
- coq_PeanoView
- ->
- 'a1 **)
-
- let rec coq_PeanoView_rect f f0 p = function
- | PeanoOne ->
- f
- | PeanoSucc (p1,
- p2) ->
- f0
- p1
- p2
- (coq_PeanoView_rect
- f
- f0
- p1
- p2)
-
- (** val coq_PeanoView_rec :
- 'a1
- ->
- (positive
- ->
- coq_PeanoView
- ->
- 'a1
- ->
- 'a1)
- ->
- positive
- ->
- coq_PeanoView
- ->
- 'a1 **)
-
- let rec coq_PeanoView_rec f f0 p = function
- | PeanoOne ->
- f
- | PeanoSucc (p1,
- p2) ->
- f0
- p1
- p2
- (coq_PeanoView_rec
- f
- f0
- p1
- p2)
-
- (** val peanoView_xO :
- positive
- ->
- coq_PeanoView
- ->
- coq_PeanoView **)
-
- let rec peanoView_xO p = function
- | PeanoOne ->
- PeanoSucc
- (XH,
- PeanoOne)
- | PeanoSucc (p0,
- q0) ->
- PeanoSucc
- ((succ
- (XO
- p0)),
- (PeanoSucc
- ((XO
- p0),
- (peanoView_xO
- p0
- q0))))
-
- (** val peanoView_xI :
- positive
- ->
- coq_PeanoView
- ->
- coq_PeanoView **)
-
- let rec peanoView_xI p = function
- | PeanoOne ->
- PeanoSucc
- ((succ
- XH),
- (PeanoSucc
- (XH,
- PeanoOne)))
- | PeanoSucc (p0,
- q0) ->
- PeanoSucc
- ((succ
- (XI
- p0)),
- (PeanoSucc
- ((XI
- p0),
- (peanoView_xI
- p0
- q0))))
-
- (** val peanoView :
- positive
- ->
- coq_PeanoView **)
-
- let rec peanoView = function
- | XI p0 ->
- peanoView_xI
- p0
- (peanoView
- p0)
- | XO p0 ->
- peanoView_xO
- p0
- (peanoView
- p0)
- | XH ->
- PeanoOne
-
- (** val coq_PeanoView_iter :
- 'a1
- ->
- (positive
- ->
- 'a1
- ->
- 'a1)
- ->
- positive
- ->
- coq_PeanoView
- ->
- 'a1 **)
-
- let rec coq_PeanoView_iter a f p = function
- | PeanoOne ->
- a
- | PeanoSucc (p0,
- q0) ->
- f
- p0
- (coq_PeanoView_iter
- a
- f
- p0
- q0)
-
- (** val eqb_spec :
- positive
- ->
- positive
- ->
- reflect **)
-
- let eqb_spec x y =
- iff_reflect
- (eqb
- x
- y)
-
- (** val switch_Eq :
- comparison
- ->
- comparison
- ->
- comparison **)
-
- let switch_Eq c = function
- | Eq ->
- c
- | x ->
- x
-
- (** val mask2cmp :
- mask
- ->
- comparison **)
-
- let mask2cmp = function
- | IsNul ->
- Eq
- | IsPos p0 ->
- Gt
- | IsNeg ->
- Lt
-
- (** val leb_spec0 :
- positive
- ->
- positive
- ->
- reflect **)
-
- let leb_spec0 x y =
- iff_reflect
- (leb
- x
- y)
-
- (** val ltb_spec0 :
- positive
- ->
- positive
- ->
- reflect **)
-
- let ltb_spec0 x y =
- iff_reflect
- (ltb
- x
- y)
-
- module Private_Tac =
- struct
-
- end
-
- module Private_Rev =
- struct
- module ORev =
- struct
- type t
- =
- positive
- end
-
- module MRev =
- struct
- (** val max :
- positive
- ->
- positive
- ->
- positive **)
-
- let max x y =
- min
- y
- x
- end
-
- module MPRev = MaxLogicalProperties(ORev)(MRev)
- end
-
- module Private_Dec =
- struct
- (** val max_case_strong :
- positive
- ->
- positive
- ->
- (positive
- ->
- positive
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let max_case_strong n0 m compat hl hr =
- let c =
- compSpec2Type
- n0
- m
- (compare
- n0
- m)
- in
- (match c with
- | CompGtT ->
- compat
- n0
- (max
- n0
- m)
- __
- (hl
- __)
- | _ ->
- compat
- m
- (max
- n0
- m)
- __
- (hr
- __))
-
- (** val max_case :
- positive
- ->
- positive
- ->
- (positive
- ->
- positive
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let max_case n0 m x x0 x1 =
- max_case_strong
- n0
- m
- x
- (fun _ ->
- x0)
- (fun _ ->
- x1)
-
- (** val max_dec :
- positive
- ->
- positive
- ->
- bool **)
-
- let max_dec n0 m =
- max_case
- n0
- m
- (fun x y _ h0 ->
- h0)
- true
- false
-
- (** val min_case_strong :
- positive
- ->
- positive
- ->
- (positive
- ->
- positive
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let min_case_strong n0 m compat hl hr =
- let c =
- compSpec2Type
- n0
- m
- (compare
- n0
- m)
- in
- (match c with
- | CompGtT ->
- compat
- m
- (min
- n0
- m)
- __
- (hr
- __)
- | _ ->
- compat
- n0
- (min
- n0
- m)
- __
- (hl
- __))
-
- (** val min_case :
- positive
- ->
- positive
- ->
- (positive
- ->
- positive
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let min_case n0 m x x0 x1 =
- min_case_strong
- n0
- m
- x
- (fun _ ->
- x0)
- (fun _ ->
- x1)
-
- (** val min_dec :
- positive
- ->
- positive
- ->
- bool **)
-
- let min_dec n0 m =
- min_case
- n0
- m
- (fun x y _ h0 ->
- h0)
- true
- false
- end
-
- (** val max_case_strong :
- positive
- ->
- positive
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let max_case_strong n0 m x x0 =
- Private_Dec.max_case_strong
- n0
- m
- (fun x1 y _ x2 ->
- x2)
- x
- x0
-
- (** val max_case :
- positive
- ->
- positive
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let max_case n0 m x x0 =
- max_case_strong
- n0
- m
- (fun _ ->
- x)
- (fun _ ->
- x0)
-
- (** val max_dec :
- positive
- ->
- positive
- ->
- bool **)
-
- let max_dec =
- Private_Dec.max_dec
-
- (** val min_case_strong :
- positive
- ->
- positive
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let min_case_strong n0 m x x0 =
- Private_Dec.min_case_strong
- n0
- m
- (fun x1 y _ x2 ->
- x2)
- x
- x0
-
- (** val min_case :
- positive
- ->
- positive
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let min_case n0 m x x0 =
- min_case_strong
- n0
- m
- (fun _ ->
- x)
- (fun _ ->
- x0)
-
- (** val min_dec :
- positive
- ->
- positive
- ->
- bool **)
-
- let min_dec =
- Private_Dec.min_dec
- end
-
-module N =
- struct
- type t
- =
- n
-
- (** val zero :
- n **)
-
- let zero =
- N0
-
- (** val one :
- n **)
-
- let one =
- Npos
- XH
-
- (** val two :
- n **)
-
- let two =
- Npos
- (XO
- XH)
-
- (** val succ_double :
- n
- ->
- n **)
-
- let succ_double = function
- | N0 ->
- Npos
- XH
- | Npos p ->
- Npos
- (XI
- p)
-
- (** val double :
- n
- ->
- n **)
-
- let double = function
- | N0 ->
- N0
- | Npos p ->
- Npos
- (XO
- p)
-
- (** val succ :
- n
- ->
- n **)
-
- let succ = function
- | N0 ->
- Npos
- XH
- | Npos p ->
- Npos
- (Coq_Pos.succ
- p)
-
- (** val pred :
- n
- ->
- n **)
-
- let pred = function
- | N0 ->
- N0
- | Npos p ->
- Coq_Pos.pred_N
- p
-
- (** val succ_pos :
- n
- ->
- positive **)
-
- let succ_pos = function
- | N0 ->
- XH
- | Npos p ->
- Coq_Pos.succ
- p
-
- (** val add :
- n
- ->
- n
- ->
- n **)
-
- let add n0 m =
- match n0 with
- | N0 ->
- m
- | Npos p ->
- (match m with
- | N0 ->
- n0
- | Npos q ->
- Npos
- (Coq_Pos.add
- p
- q))
-
- (** val sub :
- n
- ->
- n
- ->
- n **)
-
- let sub n0 m =
- match n0 with
- | N0 ->
- N0
- | Npos n' ->
- (match m with
- | N0 ->
- n0
- | Npos m' ->
- (match Coq_Pos.sub_mask
- n'
- m' with
- | Coq_Pos.IsPos p ->
- Npos
- p
- | _ ->
- N0))
-
- (** val mul :
- n
- ->
- n
- ->
- n **)
-
- let mul n0 m =
- match n0 with
- | N0 ->
- N0
- | Npos p ->
- (match m with
- | N0 ->
- N0
- | Npos q ->
- Npos
- (Coq_Pos.mul
- p
- q))
-
- (** val compare :
- n
- ->
- n
- ->
- comparison **)
-
- let compare n0 m =
- match n0 with
- | N0 ->
- (match m with
- | N0 ->
- Eq
- | Npos m' ->
- Lt)
- | Npos n' ->
- (match m with
- | N0 ->
- Gt
- | Npos m' ->
- Coq_Pos.compare
- n'
- m')
-
- (** val eqb :
- n
- ->
- n
- ->
- bool **)
-
- let rec eqb n0 m =
- match n0 with
- | N0 ->
- (match m with
- | N0 ->
- true
- | Npos p ->
- false)
- | Npos p ->
- (match m with
- | N0 ->
- false
- | Npos q ->
- Coq_Pos.eqb
- p
- q)
-
- (** val leb :
- n
- ->
- n
- ->
- bool **)
-
- let leb x y =
- match compare
- x
- y with
- | Gt ->
- false
- | _ ->
- true
-
- (** val ltb :
- n
- ->
- n
- ->
- bool **)
-
- let ltb x y =
- match compare
- x
- y with
- | Lt ->
- true
- | _ ->
- false
-
- (** val min :
- n
- ->
- n
- ->
- n **)
-
- let min n0 n' =
- match compare
- n0
- n' with
- | Gt ->
- n'
- | _ ->
- n0
-
- (** val max :
- n
- ->
- n
- ->
- n **)
-
- let max n0 n' =
- match compare
- n0
- n' with
- | Gt ->
- n0
- | _ ->
- n'
-
- (** val div2 :
- n
- ->
- n **)
-
- let div2 = function
- | N0 ->
- N0
- | Npos p0 ->
- (match p0 with
- | XI p ->
- Npos
- p
- | XO p ->
- Npos
- p
- | XH ->
- N0)
-
- (** val even :
- n
- ->
- bool **)
-
- let even = function
- | N0 ->
- true
- | Npos p ->
- (match p with
- | XO p0 ->
- true
- | _ ->
- false)
-
- (** val odd :
- n
- ->
- bool **)
-
- let odd n0 =
- negb
- (even
- n0)
-
- (** val pow :
- n
- ->
- n
- ->
- n **)
-
- let pow n0 = function
- | N0 ->
- Npos
- XH
- | Npos p0 ->
- (match n0 with
- | N0 ->
- N0
- | Npos q ->
- Npos
- (Coq_Pos.pow
- q
- p0))
-
- (** val square :
- n
- ->
- n **)
-
- let square = function
- | N0 ->
- N0
- | Npos p ->
- Npos
- (Coq_Pos.square
- p)
-
- (** val log2 :
- n
- ->
- n **)
-
- let log2 = function
- | N0 ->
- N0
- | Npos p0 ->
- (match p0 with
- | XI p ->
- Npos
- (Coq_Pos.size
- p)
- | XO p ->
- Npos
- (Coq_Pos.size
- p)
- | XH ->
- N0)
-
- (** val size :
- n
- ->
- n **)
-
- let size = function
- | N0 ->
- N0
- | Npos p ->
- Npos
- (Coq_Pos.size
- p)
-
- (** val size_nat :
- n
- ->
- int **)
-
- let size_nat = function
- | N0 ->
- 0
- | Npos p ->
- Coq_Pos.size_nat
- p
-
- (** val pos_div_eucl :
- positive
- ->
- n
- ->
- (n,
- n)
- prod **)
-
- let rec pos_div_eucl a b =
- match a with
- | XI a' ->
- let Pair (q,
- r) =
- pos_div_eucl
- a'
- b
- in
- let r' =
- succ_double
- r
- in
- if leb
- b
- r'
- then Pair
- ((succ_double
- q),
- (sub
- r'
- b))
- else Pair
- ((double
- q),
- r')
- | XO a' ->
- let Pair (q,
- r) =
- pos_div_eucl
- a'
- b
- in
- let r' =
- double
- r
- in
- if leb
- b
- r'
- then Pair
- ((succ_double
- q),
- (sub
- r'
- b))
- else Pair
- ((double
- q),
- r')
- | XH ->
- (match b with
- | N0 ->
- Pair
- (N0,
- (Npos
- XH))
- | Npos p ->
- (match p with
- | XH ->
- Pair
- ((Npos
- XH),
- N0)
- | _ ->
- Pair
- (N0,
- (Npos
- XH))))
-
- (** val div_eucl :
- n
- ->
- n
- ->
- (n,
- n)
- prod **)
-
- let div_eucl a b =
- match a with
- | N0 ->
- Pair
- (N0,
- N0)
- | Npos na ->
- (match b with
- | N0 ->
- Pair
- (N0,
- a)
- | Npos p ->
- pos_div_eucl
- na
- b)
-
- (** val div :
- n
- ->
- n
- ->
- n **)
-
- let div a b =
- fst
- (div_eucl
- a
- b)
-
- (** val modulo :
- n
- ->
- n
- ->
- n **)
-
- let modulo a b =
- snd
- (div_eucl
- a
- b)
-
- (** val gcd :
- n
- ->
- n
- ->
- n **)
-
- let gcd a b =
- match a with
- | N0 ->
- b
- | Npos p ->
- (match b with
- | N0 ->
- a
- | Npos q ->
- Npos
- (Coq_Pos.gcd
- p
- q))
-
- (** val ggcd :
- n
- ->
- n
- ->
- (n,
- (n,
- n)
- prod)
- prod **)
-
- let ggcd a b =
- match a with
- | N0 ->
- Pair
- (b,
- (Pair
- (N0,
- (Npos
- XH))))
- | Npos p ->
- (match b with
- | N0 ->
- Pair
- (a,
- (Pair
- ((Npos
- XH),
- N0)))
- | Npos q ->
- let Pair (g,
- p0) =
- Coq_Pos.ggcd
- p
- q
- in
- let Pair (aa,
- bb) =
- p0
- in
- Pair
- ((Npos
- g),
- (Pair
- ((Npos
- aa),
- (Npos
- bb)))))
-
- (** val sqrtrem :
- n
- ->
- (n,
- n)
- prod **)
-
- let sqrtrem = function
- | N0 ->
- Pair
- (N0,
- N0)
- | Npos p ->
- let Pair (s,
- m) =
- Coq_Pos.sqrtrem
- p
- in
- (match m with
- | Coq_Pos.IsPos r ->
- Pair
- ((Npos
- s),
- (Npos
- r))
- | _ ->
- Pair
- ((Npos
- s),
- N0))
-
- (** val sqrt :
- n
- ->
- n **)
-
- let sqrt = function
- | N0 ->
- N0
- | Npos p ->
- Npos
- (Coq_Pos.sqrt
- p)
-
- (** val coq_lor :
- n
- ->
- n
- ->
- n **)
-
- let coq_lor n0 m =
- match n0 with
- | N0 ->
- m
- | Npos p ->
- (match m with
- | N0 ->
- n0
- | Npos q ->
- Npos
- (Coq_Pos.coq_lor
- p
- q))
-
- (** val coq_land :
- n
- ->
- n
- ->
- n **)
-
- let coq_land n0 m =
- match n0 with
- | N0 ->
- N0
- | Npos p ->
- (match m with
- | N0 ->
- N0
- | Npos q ->
- Coq_Pos.coq_land
- p
- q)
-
- (** val ldiff :
- n
- ->
- n
- ->
- n **)
-
- let rec ldiff n0 m =
- match n0 with
- | N0 ->
- N0
- | Npos p ->
- (match m with
- | N0 ->
- n0
- | Npos q ->
- Coq_Pos.ldiff
- p
- q)
-
- (** val coq_lxor :
- n
- ->
- n
- ->
- n **)
-
- let coq_lxor n0 m =
- match n0 with
- | N0 ->
- m
- | Npos p ->
- (match m with
- | N0 ->
- n0
- | Npos q ->
- Coq_Pos.coq_lxor
- p
- q)
-
- (** val shiftl_nat :
- n
- ->
- int
- ->
- n **)
-
- let shiftl_nat a n0 =
- nat_iter
- n0
- double
- a
-
- (** val shiftr_nat :
- n
- ->
- int
- ->
- n **)
-
- let shiftr_nat a n0 =
- nat_iter
- n0
- div2
- a
-
- (** val shiftl :
- n
- ->
- n
- ->
- n **)
-
- let shiftl a n0 =
- match a with
- | N0 ->
- N0
- | Npos a0 ->
- Npos
- (Coq_Pos.shiftl
- a0
- n0)
-
- (** val shiftr :
- n
- ->
- n
- ->
- n **)
-
- let shiftr a = function
- | N0 ->
- a
- | Npos p ->
- Coq_Pos.iter
- p
- div2
- a
-
- (** val testbit_nat :
- n
- ->
- int
- ->
- bool **)
-
- let testbit_nat = function
- | N0 ->
- (fun x ->
- false)
- | Npos p ->
- Coq_Pos.testbit_nat
- p
-
- (** val testbit :
- n
- ->
- n
- ->
- bool **)
-
- let testbit a n0 =
- match a with
- | N0 ->
- false
- | Npos p ->
- Coq_Pos.testbit
- p
- n0
-
- (** val to_nat :
- n
- ->
- int **)
-
- let to_nat = function
- | N0 ->
- 0
- | Npos p ->
- Coq_Pos.to_nat
- p
-
- (** val of_nat :
- int
- ->
- n **)
-
- let of_nat n0 =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- N0)
- (fun n' ->
- Npos
- (Coq_Pos.of_succ_nat
- n'))
- n0
-
- (** val iter :
- n
- ->
- ('a1
- ->
- 'a1)
- ->
- 'a1
- ->
- 'a1 **)
-
- let iter n0 f x =
- match n0 with
- | N0 ->
- x
- | Npos p ->
- Coq_Pos.iter
- p
- f
- x
-
- (** val eq_dec :
- n
- ->
- n
- ->
- bool **)
-
- let eq_dec n0 m =
- match n0 with
- | N0 ->
- (match m with
- | N0 ->
- true
- | Npos p ->
- false)
- | Npos x ->
- (match m with
- | N0 ->
- false
- | Npos p0 ->
- Coq_Pos.eq_dec
- x
- p0)
-
- (** val discr :
- n
- ->
- positive
- sumor **)
-
- let discr = function
- | N0 ->
- Inright
- | Npos p ->
- Inleft
- p
-
- (** val binary_rect :
- 'a1
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- n
- ->
- 'a1 **)
-
- let binary_rect f0 f2 fS2 n0 =
- let f2' =
- fun p ->
- f2
- (Npos
- p)
- in
- let fS2' =
- fun p ->
- fS2
- (Npos
- p)
- in
- (match n0 with
- | N0 ->
- f0
- | Npos p ->
- let rec f = function
- | XI p1 ->
- fS2'
- p1
- (f
- p1)
- | XO p1 ->
- f2'
- p1
- (f
- p1)
- | XH ->
- fS2
- N0
- f0
- in f
- p)
-
- (** val binary_rec :
- 'a1
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- n
- ->
- 'a1 **)
-
- let binary_rec =
- binary_rect
-
- (** val peano_rect :
- 'a1
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- n
- ->
- 'a1 **)
-
- let peano_rect f0 f n0 =
- let f' =
- fun p ->
- f
- (Npos
- p)
- in
- (match n0 with
- | N0 ->
- f0
- | Npos p ->
- Coq_Pos.peano_rect
- (f
- N0
- f0)
- f'
- p)
-
- (** val peano_rec :
- 'a1
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- n
- ->
- 'a1 **)
-
- let peano_rec =
- peano_rect
-
- (** val leb_spec0 :
- n
- ->
- n
- ->
- reflect **)
-
- let leb_spec0 x y =
- iff_reflect
- (leb
- x
- y)
-
- (** val ltb_spec0 :
- n
- ->
- n
- ->
- reflect **)
-
- let ltb_spec0 x y =
- iff_reflect
- (ltb
- x
- y)
-
- module Private_BootStrap =
- struct
-
- end
-
- (** val recursion :
- 'a1
- ->
- (n
- ->
- 'a1
- ->
- 'a1)
- ->
- n
- ->
- 'a1 **)
-
- let recursion x =
- peano_rect
- x
-
- module Private_OrderTac =
- struct
- module Elts =
- struct
- type t
- =
- n
- end
-
- module Tac = MakeOrderTac(Elts)
- end
-
- module Private_NZPow =
- struct
-
- end
-
- module Private_NZSqrt =
- struct
-
- end
-
- (** val sqrt_up :
- n
- ->
- n **)
-
- let sqrt_up a =
- match compare
- N0
- a with
- | Lt ->
- succ
- (sqrt
- (pred
- a))
- | _ ->
- N0
-
- (** val log2_up :
- n
- ->
- n **)
-
- let log2_up a =
- match compare
- (Npos
- XH)
- a with
- | Lt ->
- succ
- (log2
- (pred
- a))
- | _ ->
- N0
-
- module Private_NZDiv =
- struct
-
- end
-
- (** val lcm :
- n
- ->
- n
- ->
- n **)
-
- let lcm a b =
- mul
- a
- (div
- b
- (gcd
- a
- b))
-
- (** val eqb_spec :
- n
- ->
- n
- ->
- reflect **)
-
- let eqb_spec x y =
- iff_reflect
- (eqb
- x
- y)
-
- (** val b2n :
- bool
- ->
- n **)
-
- let b2n = function
- | true ->
- Npos
- XH
- | false ->
- N0
-
- (** val setbit :
- n
- ->
- n
- ->
- n **)
-
- let setbit a n0 =
- coq_lor
- a
- (shiftl
- (Npos
- XH)
- n0)
-
- (** val clearbit :
- n
- ->
- n
- ->
- n **)
-
- let clearbit a n0 =
- ldiff
- a
- (shiftl
- (Npos
- XH)
- n0)
-
- (** val ones :
- n
- ->
- n **)
-
- let ones n0 =
- pred
- (shiftl
- (Npos
- XH)
- n0)
-
- (** val lnot :
- n
- ->
- n
- ->
- n **)
-
- let lnot a n0 =
- coq_lxor
- a
- (ones
- n0)
-
- module Private_Tac =
- struct
-
- end
-
- module Private_Rev =
- struct
- module ORev =
- struct
- type t
- =
- n
- end
-
- module MRev =
- struct
- (** val max :
- n
- ->
- n
- ->
- n **)
-
- let max x y =
- min
- y
- x
- end
-
- module MPRev = MaxLogicalProperties(ORev)(MRev)
- end
-
- module Private_Dec =
- struct
- (** val max_case_strong :
- n
- ->
- n
- ->
- (n
- ->
- n
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let max_case_strong n0 m compat hl hr =
- let c =
- compSpec2Type
- n0
- m
- (compare
- n0
- m)
- in
- (match c with
- | CompGtT ->
- compat
- n0
- (max
- n0
- m)
- __
- (hl
- __)
- | _ ->
- compat
- m
- (max
- n0
- m)
- __
- (hr
- __))
-
- (** val max_case :
- n
- ->
- n
- ->
- (n
- ->
- n
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let max_case n0 m x x0 x1 =
- max_case_strong
- n0
- m
- x
- (fun _ ->
- x0)
- (fun _ ->
- x1)
-
- (** val max_dec :
- n
- ->
- n
- ->
- bool **)
-
- let max_dec n0 m =
- max_case
- n0
- m
- (fun x y _ h0 ->
- h0)
- true
- false
-
- (** val min_case_strong :
- n
- ->
- n
- ->
- (n
- ->
- n
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let min_case_strong n0 m compat hl hr =
- let c =
- compSpec2Type
- n0
- m
- (compare
- n0
- m)
- in
- (match c with
- | CompGtT ->
- compat
- m
- (min
- n0
- m)
- __
- (hr
- __)
- | _ ->
- compat
- n0
- (min
- n0
- m)
- __
- (hl
- __))
-
- (** val min_case :
- n
- ->
- n
- ->
- (n
- ->
- n
- ->
- __
- ->
- 'a1
- ->
- 'a1)
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let min_case n0 m x x0 x1 =
- min_case_strong
- n0
- m
- x
- (fun _ ->
- x0)
- (fun _ ->
- x1)
-
- (** val min_dec :
- n
- ->
- n
- ->
- bool **)
-
- let min_dec n0 m =
- min_case
- n0
- m
- (fun x y _ h0 ->
- h0)
- true
- false
- end
-
- (** val max_case_strong :
- n
- ->
- n
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let max_case_strong n0 m x x0 =
- Private_Dec.max_case_strong
- n0
- m
- (fun x1 y _ x2 ->
- x2)
- x
- x0
-
- (** val max_case :
- n
- ->
- n
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let max_case n0 m x x0 =
- max_case_strong
- n0
- m
- (fun _ ->
- x)
- (fun _ ->
- x0)
-
- (** val max_dec :
- n
- ->
- n
- ->
- bool **)
-
- let max_dec =
- Private_Dec.max_dec
-
- (** val min_case_strong :
- n
- ->
- n
- ->
- (__
- ->
- 'a1)
- ->
- (__
- ->
- 'a1)
- ->
- 'a1 **)
-
- let min_case_strong n0 m x x0 =
- Private_Dec.min_case_strong
- n0
- m
- (fun x1 y _ x2 ->
- x2)
- x
- x0
-
- (** val min_case :
- n
- ->
- n
- ->
- 'a1
- ->
- 'a1
- ->
- 'a1 **)
-
- let min_case n0 m x x0 =
- min_case_strong
- n0
- m
- (fun _ ->
- x)
- (fun _ ->
- x0)
-
- (** val min_dec :
- n
- ->
- n
- ->
- bool **)
-
- let min_dec =
- Private_Dec.min_dec
- end
-
-(** val eq_nat_dec :
- int
- ->
- int
- ->
- bool **)
-
-let rec eq_nat_dec n0 m =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun m0 ->
- false)
- m)
- (fun n1 ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- false)
- (fun m0 ->
- eq_nat_dec
- n1
- m0)
- m)
- n0
-
-(** val beq_nat :
- int
- ->
- int
- ->
- bool **)
-
-let rec beq_nat = ( = )
-
-(** val rev :
- 'a1
- list
- ->
- 'a1
- list **)
-
-let rec rev = function
-| Nil ->
- Nil
-| Cons (x,
- l') ->
- app
- (rev
- l')
- (Cons
- (x,
- Nil))
-
-(** val map :
- ('a1
- ->
- 'a2)
- ->
- 'a1
- list
- ->
- 'a2
- list **)
-
-let rec map f = function
-| Nil ->
- Nil
-| Cons (a,
- t0) ->
- Cons
- ((f
- a),
- (map
- f
- t0))
-
-(** val fold_left :
- ('a1
- ->
- 'a2
- ->
- 'a1)
- ->
- 'a2
- list
- ->
- 'a1
- ->
- 'a1 **)
-
-let rec fold_left f l a0 =
- match l with
- | Nil ->
- a0
- | Cons (b,
- t0) ->
- fold_left
- f
- t0
- (f
- a0
- b)
-
-(** val fold_right :
- ('a2
- ->
- 'a1
- ->
- 'a1)
- ->
- 'a1
- ->
- 'a2
- list
- ->
- 'a1 **)
-
-let rec fold_right f a0 = function
-| Nil ->
- a0
-| Cons (b,
- t0) ->
- f
- b
- (fold_right
- f
- a0
- t0)
-
-(** val forallb :
- ('a1
- ->
- bool)
- ->
- 'a1
- list
- ->
- bool **)
-
-let rec forallb f = function
-| Nil ->
- true
-| Cons (a,
- l0) ->
- if f
- a
- then forallb
- f
- l0
- else false
-
-(** val n_of_digits :
- bool
- list
- ->
- n **)
-
-let rec n_of_digits = function
-| Nil ->
- N0
-| Cons (b,
- l') ->
- N.add
- (if b
- then Npos
- XH
- else N0)
- (N.mul
- (Npos
- (XO
- XH))
- (n_of_digits
- l'))
-
-(** val n_of_ascii :
- char
- ->
- n **)
-
-let n_of_ascii a =
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun a0 a1 a2 a3 a4 a5 a6 a7 ->
- n_of_digits
- (Cons
- (a0,
- (Cons
- (a1,
- (Cons
- (a2,
- (Cons
- (a3,
- (Cons
- (a4,
- (Cons
- (a5,
- (Cons
- (a6,
- (Cons
- (a7,
- Nil)))))))))))))))))
- a
-
-(** val nat_of_ascii :
- char
- ->
- int **)
-
-let nat_of_ascii a =
- N.to_nat
- (n_of_ascii
- a)
-
-type string =
-| EmptyString
-| String of char
- * string
-
-(** val string_dec :
- string
- ->
- string
- ->
- bool **)
-
-let rec string_dec s s0 =
- match s with
- | EmptyString ->
- (match s0 with
- | EmptyString ->
- true
- | String (a,
- s1) ->
- false)
- | String (a,
- s1) ->
- (match s0 with
- | EmptyString ->
- false
- | String (a0,
- s2) ->
- if (=)
- a
- a0
- then string_dec
- s1
- s2
- else false)
-
-(** val append :
- string
- ->
- string
- ->
- string **)
-
-let rec append s1 s2 =
- match s1 with
- | EmptyString ->
- s2
- | String (c,
- s1') ->
- String
- (c,
- (append
- s1'
- s2))
-
-(** val ble_nat :
- int
- ->
- int
- ->
- bool **)
-
-let rec ble_nat n0 m =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun n' ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- false)
- (fun m' ->
- ble_nat
- n'
- m')
- m)
- n0
-
-type id =
- int
- (* singleton inductive, whose constructor was Id *)
-
-(** val eq_id_dec :
- id
- ->
- id
- ->
- bool **)
-
-let eq_id_dec id1 id2 =
- eq_nat_dec
- id1
- id2
-
-type state
- =
- id
- ->
- int
-
-(** val empty_state :
- state **)
-
-let empty_state x =
- 0
-
-(** val update :
- state
- ->
- id
- ->
- int
- ->
- state **)
-
-let update st x n0 x' =
- if eq_id_dec
- x
- x'
- then n0
- else st
- x'
-
-type aexp =
-| ANum of int
-| AId of id
-| APlus of aexp
- * aexp
-| AMinus of aexp
- * aexp
-| AMult of aexp
- * aexp
-
-type bexp =
-| BTrue
-| BFalse
-| BEq of aexp
- * aexp
-| BLe of aexp
- * aexp
-| BNot of bexp
-| BAnd of bexp
- * bexp
-
-(** val aeval :
- state
- ->
- aexp
- ->
- int **)
-
-let rec aeval st = function
-| ANum n0 ->
- n0
-| AId x ->
- st
- x
-| APlus (a1,
- a2) ->
- plus
- (aeval
- st
- a1)
- (aeval
- st
- a2)
-| AMinus (a1,
- a2) ->
- minus
- (aeval
- st
- a1)
- (aeval
- st
- a2)
-| AMult (a1,
- a2) ->
- mult
- (aeval
- st
- a1)
- (aeval
- st
- a2)
-
-(** val beval :
- state
- ->
- bexp
- ->
- bool **)
-
-let rec beval st = function
-| BTrue ->
- true
-| BFalse ->
- false
-| BEq (a1,
- a2) ->
- beq_nat
- (aeval
- st
- a1)
- (aeval
- st
- a2)
-| BLe (a1,
- a2) ->
- ble_nat
- (aeval
- st
- a1)
- (aeval
- st
- a2)
-| BNot b1 ->
- negb
- (beval
- st
- b1)
-| BAnd (b1,
- b2) ->
- if beval
- st
- b1
- then beval
- st
- b2
- else false
-
-type com =
-| CSkip
-| CAss of id
- * aexp
-| CSeq of com
- * com
-| CIf of bexp
- * com
- * com
-| CWhile of bexp
- * com
-
-(** val ceval_step :
- state
- ->
- com
- ->
- int
- ->
- state
- option **)
-
-let rec ceval_step st c i =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- None)
- (fun i' ->
- match c with
- | CSkip ->
- Some
- st
- | CAss (l,
- a1) ->
- Some
- (update
- st
- l
- (aeval
- st
- a1))
- | CSeq (c1,
- c2) ->
- (match ceval_step
- st
- c1
- i' with
- | Some st' ->
- ceval_step
- st'
- c2
- i'
- | None ->
- None)
- | CIf (b,
- c1,
- c2) ->
- if beval
- st
- b
- then ceval_step
- st
- c1
- i'
- else ceval_step
- st
- c2
- i'
- | CWhile (b1,
- c1) ->
- if beval
- st
- b1
- then (match ceval_step
- st
- c1
- i' with
- | Some st' ->
- ceval_step
- st'
- c
- i'
- | None ->
- None)
- else Some
- st)
- i
-
-(** val isWhite :
- char
- ->
- bool **)
-
-let isWhite c =
- let n0 =
- nat_of_ascii
- c
- in
- if if beq_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0))))))))))))))))))))))))))))))))
- then true
- else beq_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0)))))))))
- then true
- else if beq_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0))))))))))
- then true
- else beq_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0)))))))))))))
-
-(** val isLowerAlpha :
- char
- ->
- bool **)
-
-let isLowerAlpha c =
- let n0 =
- nat_of_ascii
- c
- in
- if ble_nat
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- n0
- then ble_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- else false
-
-(** val isAlpha :
- char
- ->
- bool **)
-
-let isAlpha c =
- let n0 =
- nat_of_ascii
- c
- in
- if if ble_nat
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- n0
- then ble_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- else false
- then true
- else if ble_nat
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- n0
- then ble_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- else false
-
-(** val isDigit :
- char
- ->
- bool **)
-
-let isDigit c =
- let n0 =
- nat_of_ascii
- c
- in
- if ble_nat
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0))))))))))))))))))))))))))))))))))))))))))))))))
- n0
- then ble_nat
- n0
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- ((fun x -> x + 1)
- 0)))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- else false
-
-type chartype =
-| White
-| Alpha
-| Digit
-| Other
-
-(** val classifyChar :
- char
- ->
- chartype **)
-
-let classifyChar c =
- if isWhite
- c
- then White
- else if isAlpha
- c
- then Alpha
- else if isDigit
- c
- then Digit
- else Other
-
-(** val list_of_string :
- string
- ->
- char
- list **)
-
-let rec list_of_string = function
-| EmptyString ->
- Nil
-| String (c,
- s0) ->
- Cons
- (c,
- (list_of_string
- s0))
-
-(** val string_of_list :
- char
- list
- ->
- string **)
-
-let rec string_of_list xs =
- fold_right
- (fun x x0 ->
- String
- (x,
- x0))
- EmptyString
- xs
-
-type token
- =
- string
-
-(** val tokenize_helper :
- chartype
- ->
- char
- list
- ->
- char
- list
- ->
- char
- list
- list **)
-
-let rec tokenize_helper cls acc xs =
- let tk =
- match acc with
- | Nil ->
- Nil
- | Cons (a,
- l) ->
- Cons
- ((rev
- acc),
- Nil)
- in
- (match xs with
- | Nil ->
- tk
- | Cons (x,
- xs') ->
- (match cls with
- | White ->
- (match classifyChar
- x with
- | White ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b1
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b2
- then if b3
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b4
- then if b5
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b6
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else app
- tk
- (Cons
- ((Cons
- (')',
- Nil)),
- (tokenize_helper
- Other
- Nil
- xs')))
- else app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b0
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b1
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b2
- then if b3
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b4
- then if b5
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else if b6
- then app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else app
- tk
- (Cons
- ((Cons
- ('(',
- Nil)),
- (tokenize_helper
- Other
- Nil
- xs')))
- else app
- tk
- (tokenize_helper
- White
- Nil
- xs')
- else app
- tk
- (tokenize_helper
- White
- Nil
- xs'))
- x
- | Other ->
- let tp =
- Other
- in
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b1
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b2
- then if b3
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b4
- then if b5
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b6
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (Cons
- ((Cons
- (')',
- Nil)),
- (tokenize_helper
- Other
- Nil
- xs')))
- else app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b0
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b1
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b2
- then if b3
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b4
- then if b5
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else if b6
- then app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (Cons
- ((Cons
- ('(',
- Nil)),
- (tokenize_helper
- Other
- Nil
- xs')))
- else app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (tokenize_helper
- tp
- (Cons
- (x,
- Nil))
- xs'))
- x
- | x0 ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b1
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b2
- then if b3
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b4
- then if b5
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b6
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (Cons
- ((Cons
- (')',
- Nil)),
- (tokenize_helper
- Other
- Nil
- xs')))
- else app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b0
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b1
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b2
- then if b3
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b4
- then if b5
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else if b6
- then app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (Cons
- ((Cons
- ('(',
- Nil)),
- (tokenize_helper
- Other
- Nil
- xs')))
- else app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs')
- else app
- tk
- (tokenize_helper
- x0
- (Cons
- (x,
- Nil))
- xs'))
- x)
- | Alpha ->
- (match classifyChar
- x with
- | White ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper White Nil xs')
- else if b1
- then app tk (tokenize_helper White Nil xs')
- else if b2
- then if b3
- then app tk (tokenize_helper White Nil xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper White Nil
- xs')
- else if b6
- then app tk
- (tokenize_helper White
- Nil xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper White Nil xs')
- else app tk (tokenize_helper White Nil xs')
- else if b0
- then app tk (tokenize_helper White Nil xs')
- else if b1
- then app tk (tokenize_helper White Nil xs')
- else if b2
- then if b3
- then app tk (tokenize_helper White Nil xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper White Nil
- xs')
- else if b6
- then app tk
- (tokenize_helper White
- Nil xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper White Nil xs')
- else app tk (tokenize_helper White Nil xs'))
- x
- | Alpha ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then tokenize_helper Alpha (Cons (x, acc)) xs'
- else if b1
- then tokenize_helper Alpha (Cons (x, acc)) xs'
- else if b2
- then if b3
- then tokenize_helper Alpha (Cons (x, acc))
- xs'
- else if b4
- then if b5
- then tokenize_helper Alpha (Cons
- (x, acc)) xs'
- else if b6
- then tokenize_helper Alpha
- (Cons (x, acc)) xs'
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else tokenize_helper Alpha (Cons (x,
- acc)) xs'
- else tokenize_helper Alpha (Cons (x, acc)) xs'
- else if b0
- then tokenize_helper Alpha (Cons (x, acc)) xs'
- else if b1
- then tokenize_helper Alpha (Cons (x, acc)) xs'
- else if b2
- then if b3
- then tokenize_helper Alpha (Cons (x, acc))
- xs'
- else if b4
- then if b5
- then tokenize_helper Alpha (Cons
- (x, acc)) xs'
- else if b6
- then tokenize_helper Alpha
- (Cons (x, acc)) xs'
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else tokenize_helper Alpha (Cons (x,
- acc)) xs'
- else tokenize_helper Alpha (Cons (x, acc)) xs')
- x
- | Digit ->
- let tp = Digit in
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs'))
- x
- | Other ->
- let tp = Other in
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs'))
- x)
- | Digit ->
- (match classifyChar x with
- | White ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper White Nil xs')
- else if b1
- then app tk (tokenize_helper White Nil xs')
- else if b2
- then if b3
- then app tk (tokenize_helper White Nil xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper White Nil
- xs')
- else if b6
- then app tk
- (tokenize_helper White
- Nil xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper White Nil xs')
- else app tk (tokenize_helper White Nil xs')
- else if b0
- then app tk (tokenize_helper White Nil xs')
- else if b1
- then app tk (tokenize_helper White Nil xs')
- else if b2
- then if b3
- then app tk (tokenize_helper White Nil xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper White Nil
- xs')
- else if b6
- then app tk
- (tokenize_helper White
- Nil xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper White Nil xs')
- else app tk (tokenize_helper White Nil xs'))
- x
- | Alpha ->
- let tp = Alpha in
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs'))
- x
- | Digit ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then tokenize_helper Digit (Cons (x, acc)) xs'
- else if b1
- then tokenize_helper Digit (Cons (x, acc)) xs'
- else if b2
- then if b3
- then tokenize_helper Digit (Cons (x, acc))
- xs'
- else if b4
- then if b5
- then tokenize_helper Digit (Cons
- (x, acc)) xs'
- else if b6
- then tokenize_helper Digit
- (Cons (x, acc)) xs'
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else tokenize_helper Digit (Cons (x,
- acc)) xs'
- else tokenize_helper Digit (Cons (x, acc)) xs'
- else if b0
- then tokenize_helper Digit (Cons (x, acc)) xs'
- else if b1
- then tokenize_helper Digit (Cons (x, acc)) xs'
- else if b2
- then if b3
- then tokenize_helper Digit (Cons (x, acc))
- xs'
- else if b4
- then if b5
- then tokenize_helper Digit (Cons
- (x, acc)) xs'
- else if b6
- then tokenize_helper Digit
- (Cons (x, acc)) xs'
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else tokenize_helper Digit (Cons (x,
- acc)) xs'
- else tokenize_helper Digit (Cons (x, acc)) xs')
- x
- | Other ->
- let tp = Other in
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b0
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper tp (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper tp (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper tp (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper tp
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper tp (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper tp (Cons (x, Nil)) xs'))
- x)
- | Other ->
- (match classifyChar x with
- | White ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper White Nil xs')
- else if b1
- then app tk (tokenize_helper White Nil xs')
- else if b2
- then if b3
- then app tk (tokenize_helper White Nil xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper White Nil
- xs')
- else if b6
- then app tk
- (tokenize_helper White
- Nil xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper White Nil xs')
- else app tk (tokenize_helper White Nil xs')
- else if b0
- then app tk (tokenize_helper White Nil xs')
- else if b1
- then app tk (tokenize_helper White Nil xs')
- else if b2
- then if b3
- then app tk (tokenize_helper White Nil xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper White Nil
- xs')
- else if b6
- then app tk
- (tokenize_helper White
- Nil xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper White Nil xs')
- else app tk (tokenize_helper White Nil xs'))
- x
- | Other ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then tokenize_helper Other (Cons (x, acc)) xs'
- else if b1
- then tokenize_helper Other (Cons (x, acc)) xs'
- else if b2
- then if b3
- then tokenize_helper Other (Cons (x, acc))
- xs'
- else if b4
- then if b5
- then tokenize_helper Other (Cons
- (x, acc)) xs'
- else if b6
- then tokenize_helper Other
- (Cons (x, acc)) xs'
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else tokenize_helper Other (Cons (x,
- acc)) xs'
- else tokenize_helper Other (Cons (x, acc)) xs'
- else if b0
- then tokenize_helper Other (Cons (x, acc)) xs'
- else if b1
- then tokenize_helper Other (Cons (x, acc)) xs'
- else if b2
- then if b3
- then tokenize_helper Other (Cons (x, acc))
- xs'
- else if b4
- then if b5
- then tokenize_helper Other (Cons
- (x, acc)) xs'
- else if b6
- then tokenize_helper Other
- (Cons (x, acc)) xs'
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else tokenize_helper Other (Cons (x,
- acc)) xs'
- else tokenize_helper Other (Cons (x, acc)) xs')
- x
- | x0 ->
- (* If this appears, you're using Ascii internals. Please don't *) (fun f c -> let n = Char.code c in let h i = (n land (1 lsl i)) <> 0 in f (h 0) (h 1) (h 2) (h 3) (h 4) (h 5) (h 6) (h 7))
- (fun b b0 b1 b2 b3 b4 b5 b6 ->
- if b
- then if b0
- then app tk (tokenize_helper x0 (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper x0 (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper x0 (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper x0 (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper x0
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- (')', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper x0 (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper x0 (Cons (x, Nil)) xs')
- else if b0
- then app tk (tokenize_helper x0 (Cons (x, Nil)) xs')
- else if b1
- then app tk (tokenize_helper x0 (Cons (x, Nil)) xs')
- else if b2
- then if b3
- then app tk
- (tokenize_helper x0 (Cons (x, Nil))
- xs')
- else if b4
- then if b5
- then app tk
- (tokenize_helper x0 (Cons
- (x, Nil)) xs')
- else if b6
- then app tk
- (tokenize_helper x0
- (Cons (x, Nil)) xs')
- else app tk (Cons ((Cons
- ('(', Nil)),
- (tokenize_helper Other
- Nil xs')))
- else app tk
- (tokenize_helper x0 (Cons (x,
- Nil)) xs')
- else app tk
- (tokenize_helper x0 (Cons (x, Nil)) xs'))
- x)))
-
-(** val tokenize : string -> string list **)
-
-let tokenize s =
- map string_of_list (tokenize_helper White Nil (list_of_string s))
-
-type 'x optionE =
-| SomeE of 'x
-| NoneE of string
-
-(** val build_symtable : token list -> int -> token -> int **)
-
-let rec build_symtable xs n0 =
- match xs with
- | Nil -> (fun s -> n0)
- | Cons (x, xs0) ->
- if forallb isLowerAlpha (list_of_string x)
- then (fun s ->
- if string_dec s x
- then n0
- else build_symtable xs0 ((fun x -> x + 1) n0) s)
- else build_symtable xs0 n0
-
-type 't parser0 = token list -> ('t, token list) prod optionE
-
-(** val many_helper :
- 'a1 parser0 -> 'a1 list -> int -> token list -> ('a1 list, token list)
- prod optionE **)
-
-let rec many_helper p acc steps xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match p xs with
- | SomeE p0 ->
- let Pair (t0, xs') = p0 in many_helper p (Cons (t0, acc)) steps' xs'
- | NoneE s -> SomeE (Pair ((rev acc), xs)))
- steps
-
-(** val many : 'a1 parser0 -> int -> 'a1 list parser0 **)
-
-let rec many p steps =
- many_helper p Nil steps
-
-(** val firstExpect : token -> 'a1 parser0 -> 'a1 parser0 **)
-
-let firstExpect t0 p = function
-| Nil ->
- NoneE
- (append (String ('e', (String ('x', (String ('p', (String ('e', (String
- ('c', (String ('t', (String ('e', (String ('d', (String (' ', (String
- ('\'', EmptyString))))))))))))))))))))
- (append t0 (String ('\'', (String ('.', EmptyString))))))
-| Cons (x, xs') ->
- if string_dec x t0
- then p xs'
- else NoneE
- (append (String ('e', (String ('x', (String ('p', (String ('e',
- (String ('c', (String ('t', (String ('e', (String ('d', (String
- (' ', (String ('\'', EmptyString))))))))))))))))))))
- (append t0 (String ('\'', (String ('.', EmptyString))))))
-
-(** val expect : token -> unit0 parser0 **)
-
-let expect t0 =
- firstExpect t0 (fun xs -> SomeE (Pair (Tt, xs)))
-
-(** val parseIdentifier :
- (string -> int) -> token list -> (id, token list) prod optionE **)
-
-let parseIdentifier symtable = function
-| Nil ->
- NoneE (String ('E', (String ('x', (String ('p', (String ('e', (String ('c',
- (String ('t', (String ('e', (String ('d', (String (' ', (String ('i',
- (String ('d', (String ('e', (String ('n', (String ('t', (String ('i',
- (String ('f', (String ('i', (String ('e', (String ('r',
- EmptyString))))))))))))))))))))))))))))))))))))))
-| Cons (x, xs') ->
- if forallb isLowerAlpha (list_of_string x)
- then SomeE (Pair ((symtable x), xs'))
- else NoneE
- (append (String ('I', (String ('l', (String ('l', (String ('e',
- (String ('g', (String ('a', (String ('l', (String (' ', (String
- ('i', (String ('d', (String ('e', (String ('n', (String ('t',
- (String ('i', (String ('f', (String ('i', (String ('e', (String
- ('r', (String (':', (String ('\'',
- EmptyString))))))))))))))))))))))))))))))))))))))))
- (append x (String ('\'', EmptyString))))
-
-(** val parseNumber : token list -> (int, token list) prod optionE **)
-
-let parseNumber = function
-| Nil ->
- NoneE (String ('E', (String ('x', (String ('p', (String ('e', (String ('c',
- (String ('t', (String ('e', (String ('d', (String (' ', (String ('n',
- (String ('u', (String ('m', (String ('b', (String ('e', (String ('r',
- EmptyString))))))))))))))))))))))))))))))
-| Cons (x, xs') ->
- if forallb isDigit (list_of_string x)
- then SomeE (Pair
- ((fold_left (fun n0 d ->
- plus
- (mult ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) 0)))))))))) n0)
- (minus (nat_of_ascii d) (nat_of_ascii '0'))) (list_of_string x)
- 0), xs'))
- else NoneE (String ('E', (String ('x', (String ('p', (String ('e', (String
- ('c', (String ('t', (String ('e', (String ('d', (String (' ',
- (String ('n', (String ('u', (String ('m', (String ('b', (String
- ('e', (String ('r', EmptyString))))))))))))))))))))))))))))))
-
-(** val parsePrimaryExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE **)
-
-let rec parsePrimaryExp steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match parseIdentifier symtable xs with
- | SomeE p -> let Pair (i, rest) = p in SomeE (Pair ((AId i), rest))
- | NoneE err ->
- (match parseNumber xs with
- | SomeE p -> let Pair (n0, rest) = p in SomeE (Pair ((ANum n0), rest))
- | NoneE err0 ->
- (match firstExpect (String ('(', EmptyString))
- (parseSumExp steps' symtable) xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match expect (String (')', EmptyString)) rest with
- | SomeE p0 ->
- let Pair (u, rest') = p0 in SomeE (Pair (e, rest'))
- | NoneE err1 -> NoneE err1)
- | NoneE err1 -> NoneE err1)))
- steps
-
-(** val parseProductExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE **)
-
-and parseProductExp steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match parsePrimaryExp steps' symtable xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match many
- (firstExpect (String ('*', EmptyString))
- (parsePrimaryExp steps' symtable)) steps' rest with
- | SomeE p0 ->
- let Pair (es, rest') = p0 in
- SomeE (Pair ((fold_left (fun x x0 -> AMult (x, x0)) es e), rest'))
- | NoneE err -> NoneE err)
- | NoneE err -> NoneE err)
- steps
-
-(** val parseSumExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE **)
-
-and parseSumExp steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match parseProductExp steps' symtable xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match many (fun xs0 ->
- match firstExpect (String ('+', EmptyString))
- (parseProductExp steps' symtable) xs0 with
- | SomeE p0 ->
- let Pair (e0, rest') = p0 in
- SomeE (Pair ((Pair (true, e0)), rest'))
- | NoneE err ->
- (match firstExpect (String ('-', EmptyString))
- (parseProductExp steps' symtable) xs0 with
- | SomeE p0 ->
- let Pair (e0, rest') = p0 in
- SomeE (Pair ((Pair (false, e0)), rest'))
- | NoneE err0 -> NoneE err0)) steps' rest with
- | SomeE p0 ->
- let Pair (es, rest') = p0 in
- SomeE (Pair
- ((fold_left (fun e0 term ->
- let Pair (y, e1) = term in
- if y then APlus (e0, e1) else AMinus (e0, e1)) es e), rest'))
- | NoneE err -> NoneE err)
- | NoneE err -> NoneE err)
- steps
-
-(** val parseAExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE **)
-
-let parseAExp =
- parseSumExp
-
-(** val parseAtomicExp :
- int -> (string -> int) -> token list -> (bexp, token list) prod optionE **)
-
-let rec parseAtomicExp steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match expect (String ('t', (String ('r', (String ('u', (String ('e',
- EmptyString)))))))) xs with
- | SomeE p -> let Pair (u, rest) = p in SomeE (Pair (BTrue, rest))
- | NoneE err ->
- (match expect (String ('f', (String ('a', (String ('l', (String ('s',
- (String ('e', EmptyString)))))))))) xs with
- | SomeE p -> let Pair (u, rest) = p in SomeE (Pair (BFalse, rest))
- | NoneE err0 ->
- (match firstExpect (String ('n', (String ('o', (String ('t',
- EmptyString)))))) (parseAtomicExp steps' symtable) xs with
- | SomeE p ->
- let Pair (e, rest) = p in SomeE (Pair ((BNot e), rest))
- | NoneE err1 ->
- (match firstExpect (String ('(', EmptyString))
- (parseConjunctionExp steps' symtable) xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match expect (String (')', EmptyString)) rest with
- | SomeE p0 ->
- let Pair (u, rest') = p0 in SomeE (Pair (e, rest'))
- | NoneE err2 -> NoneE err2)
- | NoneE err2 ->
- (match parseProductExp steps' symtable xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match firstExpect (String ('=', (String ('=',
- EmptyString)))) (parseAExp steps' symtable) rest with
- | SomeE p0 ->
- let Pair (e', rest') = p0 in
- SomeE (Pair ((BEq (e, e')), rest'))
- | NoneE err3 ->
- (match firstExpect (String ('<', (String ('=',
- EmptyString)))) (parseAExp steps' symtable)
- rest with
- | SomeE p0 ->
- let Pair (e', rest') = p0 in
- SomeE (Pair ((BLe (e, e')), rest'))
- | NoneE err4 ->
- NoneE (String ('E', (String ('x', (String ('p',
- (String ('e', (String ('c', (String ('t', (String
- ('e', (String ('d', (String (' ', (String ('\'',
- (String ('=', (String ('=', (String ('\'', (String
- (' ', (String ('o', (String ('r', (String (' ',
- (String ('\'', (String ('<', (String ('=', (String
- ('\'', (String (' ', (String ('a', (String ('f',
- (String ('t', (String ('e', (String ('r', (String
- (' ', (String ('a', (String ('r', (String ('i',
- (String ('t', (String ('h', (String ('m', (String
- ('e', (String ('t', (String ('i', (String ('c',
- (String (' ', (String ('e', (String ('x', (String
- ('p', (String ('r', (String ('e', (String ('s',
- (String ('s', (String ('i', (String ('o', (String
- ('n',
- EmptyString))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
- | NoneE err3 -> NoneE err3)))))
- steps
-
-(** val parseConjunctionExp :
- int -> (string -> int) -> token list -> (bexp, token list) prod optionE **)
-
-and parseConjunctionExp steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match parseAtomicExp steps' symtable xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match many
- (firstExpect (String ('&', (String ('&', EmptyString))))
- (parseAtomicExp steps' symtable)) steps' rest with
- | SomeE p0 ->
- let Pair (es, rest') = p0 in
- SomeE (Pair ((fold_left (fun x x0 -> BAnd (x, x0)) es e), rest'))
- | NoneE err -> NoneE err)
- | NoneE err -> NoneE err)
- steps
-
-(** val parseBExp :
- int -> (string -> int) -> token list -> (bexp, token list) prod optionE **)
-
-let parseBExp =
- parseConjunctionExp
-
-(** val parseSimpleCommand :
- int -> (string -> int) -> token list -> (com, token list) prod optionE **)
-
-let rec parseSimpleCommand steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match expect (String ('S', (String ('K', (String ('I', (String ('P',
- EmptyString)))))))) xs with
- | SomeE p -> let Pair (u, rest) = p in SomeE (Pair (CSkip, rest))
- | NoneE err ->
- (match firstExpect (String ('I', (String ('F', EmptyString))))
- (parseBExp steps' symtable) xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match firstExpect (String ('T', (String ('H', (String ('E', (String
- ('N', EmptyString))))))))
- (parseSequencedCommand steps' symtable) rest with
- | SomeE p0 ->
- let Pair (c, rest') = p0 in
- (match firstExpect (String ('E', (String ('L', (String ('S',
- (String ('E', EmptyString))))))))
- (parseSequencedCommand steps' symtable) rest' with
- | SomeE p1 ->
- let Pair (c', rest'') = p1 in
- (match expect (String ('E', (String ('N', (String ('D',
- EmptyString)))))) rest'' with
- | SomeE p2 ->
- let Pair (u, rest''') = p2 in
- SomeE (Pair ((CIf (e, c, c')), rest'''))
- | NoneE err0 -> NoneE err0)
- | NoneE err0 -> NoneE err0)
- | NoneE err0 -> NoneE err0)
- | NoneE err0 ->
- (match firstExpect (String ('W', (String ('H', (String ('I', (String
- ('L', (String ('E', EmptyString))))))))))
- (parseBExp steps' symtable) xs with
- | SomeE p ->
- let Pair (e, rest) = p in
- (match firstExpect (String ('D', (String ('O', EmptyString))))
- (parseSequencedCommand steps' symtable) rest with
- | SomeE p0 ->
- let Pair (c, rest') = p0 in
- (match expect (String ('E', (String ('N', (String ('D',
- EmptyString)))))) rest' with
- | SomeE p1 ->
- let Pair (u, rest'') = p1 in
- SomeE (Pair ((CWhile (e, c)), rest''))
- | NoneE err1 -> NoneE err1)
- | NoneE err1 -> NoneE err1)
- | NoneE err1 ->
- (match parseIdentifier symtable xs with
- | SomeE p ->
- let Pair (i, rest) = p in
- (match firstExpect (String (':', (String ('=', EmptyString))))
- (parseAExp steps' symtable) rest with
- | SomeE p0 ->
- let Pair (e, rest') = p0 in
- SomeE (Pair ((CAss (i, e)), rest'))
- | NoneE err2 -> NoneE err2)
- | NoneE err2 -> NoneE err2))))
- steps
-
-(** val parseSequencedCommand :
- int -> (string -> int) -> token list -> (com, token list) prod optionE **)
-
-and parseSequencedCommand steps symtable xs =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ -> NoneE (String ('T', (String ('o', (String ('o', (String (' ',
- (String ('m', (String ('a', (String ('n', (String ('y', (String (' ',
- (String ('r', (String ('e', (String ('c', (String ('u', (String ('r',
- (String ('s', (String ('i', (String ('v', (String ('e', (String (' ',
- (String ('c', (String ('a', (String ('l', (String ('l', (String ('s',
- EmptyString)))))))))))))))))))))))))))))))))))))))))))))))))
- (fun steps' ->
- match parseSimpleCommand steps' symtable xs with
- | SomeE p ->
- let Pair (c, rest) = p in
- (match firstExpect (String (';', (String (';', EmptyString))))
- (parseSequencedCommand steps' symtable) rest with
- | SomeE p0 ->
- let Pair (c', rest') = p0 in SomeE (Pair ((CSeq (c, c')), rest'))
- | NoneE err -> SomeE (Pair (c, rest)))
- | NoneE err -> NoneE err)
- steps
-
-(** val bignumber : int **)
-
-let bignumber =
- (fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1) ((fun x -> x + 1)
- 0)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
-
-(** val parse : string -> (com, token list) prod optionE **)
-
-let parse str =
- let tokens = tokenize str in
- parseSequencedCommand bignumber (build_symtable tokens 0) tokens
-
diff --git a/imp.mli b/imp.mli
deleted file mode 100644
index 0280ad5..0000000
--- a/imp.mli
+++ /dev/null
@@ -1,809 +0,0 @@
-type __ = Obj.t
-
-type unit0 =
-| Tt
-
-val negb : bool -> bool
-
-type 'a option =
-| Some of 'a
-| None
-
-type ('a, 'b) prod =
-| Pair of 'a * 'b
-
-val fst : ('a1, 'a2) prod -> 'a1
-
-val snd : ('a1, 'a2) prod -> 'a2
-
-type 'a list =
-| Nil
-| Cons of 'a * 'a list
-
-val app : 'a1 list -> 'a1 list -> 'a1 list
-
-type comparison =
-| Eq
-| Lt
-| Gt
-
-type compareSpecT =
-| CompEqT
-| CompLtT
-| CompGtT
-
-val compareSpec2Type : comparison -> compareSpecT
-
-type 'a compSpecT = compareSpecT
-
-val compSpec2Type : 'a1 -> 'a1 -> comparison -> 'a1 compSpecT
-
-type 'a sig0 =
- 'a
- (* singleton inductive, whose constructor was exist *)
-
-type 'a sumor =
-| Inleft of 'a
-| Inright
-
-val plus : int -> int -> int
-
-val mult : int -> int -> int
-
-val minus : int -> int -> int
-
-val nat_iter : int -> ('a1 -> 'a1) -> 'a1 -> 'a1
-
-type positive =
-| XI of positive
-| XO of positive
-| XH
-
-type n =
-| N0
-| Npos of positive
-
-type reflect =
-| ReflectT
-| ReflectF
-
-val iff_reflect : bool -> reflect
-
-module type TotalOrder' =
- sig
- type t
- end
-
-module MakeOrderTac :
- functor (O:TotalOrder') ->
- sig
-
- end
-
-module MaxLogicalProperties :
- functor (O:TotalOrder') ->
- functor (M:sig
- val max : O.t -> O.t -> O.t
- end) ->
- sig
- module Private_Tac :
- sig
-
- end
- end
-
-module Pos :
- sig
- type t = positive
-
- val succ : positive -> positive
-
- val add : positive -> positive -> positive
-
- val add_carry : positive -> positive -> positive
-
- val pred_double : positive -> positive
-
- val pred : positive -> positive
-
- val pred_N : positive -> n
-
- type mask =
- | IsNul
- | IsPos of positive
- | IsNeg
-
- val mask_rect : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1
-
- val mask_rec : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1
-
- val succ_double_mask : mask -> mask
-
- val double_mask : mask -> mask
-
- val double_pred_mask : positive -> mask
-
- val pred_mask : mask -> mask
-
- val sub_mask : positive -> positive -> mask
-
- val sub_mask_carry : positive -> positive -> mask
-
- val sub : positive -> positive -> positive
-
- val mul : positive -> positive -> positive
-
- val iter : positive -> ('a1 -> 'a1) -> 'a1 -> 'a1
-
- val pow : positive -> positive -> positive
-
- val square : positive -> positive
-
- val div2 : positive -> positive
-
- val div2_up : positive -> positive
-
- val size_nat : positive -> int
-
- val size : positive -> positive
-
- val compare_cont : positive -> positive -> comparison -> comparison
-
- val compare : positive -> positive -> comparison
-
- val min : positive -> positive -> positive
-
- val max : positive -> positive -> positive
-
- val eqb : positive -> positive -> bool
-
- val leb : positive -> positive -> bool
-
- val ltb : positive -> positive -> bool
-
- val sqrtrem_step :
- (positive -> positive) -> (positive -> positive) -> (positive, mask) prod
- -> (positive, mask) prod
-
- val sqrtrem : positive -> (positive, mask) prod
-
- val sqrt : positive -> positive
-
- val gcdn : int -> positive -> positive -> positive
-
- val gcd : positive -> positive -> positive
-
- val ggcdn :
- int -> positive -> positive -> (positive, (positive, positive) prod) prod
-
- val ggcd :
- positive -> positive -> (positive, (positive, positive) prod) prod
-
- val coq_Nsucc_double : n -> n
-
- val coq_Ndouble : n -> n
-
- val coq_lor : positive -> positive -> positive
-
- val coq_land : positive -> positive -> n
-
- val ldiff : positive -> positive -> n
-
- val coq_lxor : positive -> positive -> n
-
- val shiftl_nat : positive -> int -> positive
-
- val shiftr_nat : positive -> int -> positive
-
- val shiftl : positive -> n -> positive
-
- val shiftr : positive -> n -> positive
-
- val testbit_nat : positive -> int -> bool
-
- val testbit : positive -> n -> bool
-
- val iter_op : ('a1 -> 'a1 -> 'a1) -> positive -> 'a1 -> 'a1
-
- val to_nat : positive -> int
-
- val of_nat : int -> positive
-
- val of_succ_nat : int -> positive
- end
-
-module Coq_Pos :
- sig
- type t = positive
-
- val succ : positive -> positive
-
- val add : positive -> positive -> positive
-
- val add_carry : positive -> positive -> positive
-
- val pred_double : positive -> positive
-
- val pred : positive -> positive
-
- val pred_N : positive -> n
-
- type mask = Pos.mask =
- | IsNul
- | IsPos of positive
- | IsNeg
-
- val mask_rect : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1
-
- val mask_rec : 'a1 -> (positive -> 'a1) -> 'a1 -> mask -> 'a1
-
- val succ_double_mask : mask -> mask
-
- val double_mask : mask -> mask
-
- val double_pred_mask : positive -> mask
-
- val pred_mask : mask -> mask
-
- val sub_mask : positive -> positive -> mask
-
- val sub_mask_carry : positive -> positive -> mask
-
- val sub : positive -> positive -> positive
-
- val mul : positive -> positive -> positive
-
- val iter : positive -> ('a1 -> 'a1) -> 'a1 -> 'a1
-
- val pow : positive -> positive -> positive
-
- val square : positive -> positive
-
- val div2 : positive -> positive
-
- val div2_up : positive -> positive
-
- val size_nat : positive -> int
-
- val size : positive -> positive
-
- val compare_cont : positive -> positive -> comparison -> comparison
-
- val compare : positive -> positive -> comparison
-
- val min : positive -> positive -> positive
-
- val max : positive -> positive -> positive
-
- val eqb : positive -> positive -> bool
-
- val leb : positive -> positive -> bool
-
- val ltb : positive -> positive -> bool
-
- val sqrtrem_step :
- (positive -> positive) -> (positive -> positive) -> (positive, mask) prod
- -> (positive, mask) prod
-
- val sqrtrem : positive -> (positive, mask) prod
-
- val sqrt : positive -> positive
-
- val gcdn : int -> positive -> positive -> positive
-
- val gcd : positive -> positive -> positive
-
- val ggcdn :
- int -> positive -> positive -> (positive, (positive, positive) prod) prod
-
- val ggcd :
- positive -> positive -> (positive, (positive, positive) prod) prod
-
- val coq_Nsucc_double : n -> n
-
- val coq_Ndouble : n -> n
-
- val coq_lor : positive -> positive -> positive
-
- val coq_land : positive -> positive -> n
-
- val ldiff : positive -> positive -> n
-
- val coq_lxor : positive -> positive -> n
-
- val shiftl_nat : positive -> int -> positive
-
- val shiftr_nat : positive -> int -> positive
-
- val shiftl : positive -> n -> positive
-
- val shiftr : positive -> n -> positive
-
- val testbit_nat : positive -> int -> bool
-
- val testbit : positive -> n -> bool
-
- val iter_op : ('a1 -> 'a1 -> 'a1) -> positive -> 'a1 -> 'a1
-
- val to_nat : positive -> int
-
- val of_nat : int -> positive
-
- val of_succ_nat : int -> positive
-
- val eq_dec : positive -> positive -> bool
-
- val peano_rect : 'a1 -> (positive -> 'a1 -> 'a1) -> positive -> 'a1
-
- val peano_rec : 'a1 -> (positive -> 'a1 -> 'a1) -> positive -> 'a1
-
- type coq_PeanoView =
- | PeanoOne
- | PeanoSucc of positive * coq_PeanoView
-
- val coq_PeanoView_rect :
- 'a1 -> (positive -> coq_PeanoView -> 'a1 -> 'a1) -> positive ->
- coq_PeanoView -> 'a1
-
- val coq_PeanoView_rec :
- 'a1 -> (positive -> coq_PeanoView -> 'a1 -> 'a1) -> positive ->
- coq_PeanoView -> 'a1
-
- val peanoView_xO : positive -> coq_PeanoView -> coq_PeanoView
-
- val peanoView_xI : positive -> coq_PeanoView -> coq_PeanoView
-
- val peanoView : positive -> coq_PeanoView
-
- val coq_PeanoView_iter :
- 'a1 -> (positive -> 'a1 -> 'a1) -> positive -> coq_PeanoView -> 'a1
-
- val eqb_spec : positive -> positive -> reflect
-
- val switch_Eq : comparison -> comparison -> comparison
-
- val mask2cmp : mask -> comparison
-
- val leb_spec0 : positive -> positive -> reflect
-
- val ltb_spec0 : positive -> positive -> reflect
-
- module Private_Tac :
- sig
-
- end
-
- module Private_Rev :
- sig
- module ORev :
- sig
- type t = positive
- end
-
- module MRev :
- sig
- val max : positive -> positive -> positive
- end
-
- module MPRev :
- sig
- module Private_Tac :
- sig
-
- end
- end
- end
-
- module Private_Dec :
- sig
- val max_case_strong :
- positive -> positive -> (positive -> positive -> __ -> 'a1 -> 'a1) ->
- (__ -> 'a1) -> (__ -> 'a1) -> 'a1
-
- val max_case :
- positive -> positive -> (positive -> positive -> __ -> 'a1 -> 'a1) ->
- 'a1 -> 'a1 -> 'a1
-
- val max_dec : positive -> positive -> bool
-
- val min_case_strong :
- positive -> positive -> (positive -> positive -> __ -> 'a1 -> 'a1) ->
- (__ -> 'a1) -> (__ -> 'a1) -> 'a1
-
- val min_case :
- positive -> positive -> (positive -> positive -> __ -> 'a1 -> 'a1) ->
- 'a1 -> 'a1 -> 'a1
-
- val min_dec : positive -> positive -> bool
- end
-
- val max_case_strong :
- positive -> positive -> (__ -> 'a1) -> (__ -> 'a1) -> 'a1
-
- val max_case : positive -> positive -> 'a1 -> 'a1 -> 'a1
-
- val max_dec : positive -> positive -> bool
-
- val min_case_strong :
- positive -> positive -> (__ -> 'a1) -> (__ -> 'a1) -> 'a1
-
- val min_case : positive -> positive -> 'a1 -> 'a1 -> 'a1
-
- val min_dec : positive -> positive -> bool
- end
-
-module N :
- sig
- type t = n
-
- val zero : n
-
- val one : n
-
- val two : n
-
- val succ_double : n -> n
-
- val double : n -> n
-
- val succ : n -> n
-
- val pred : n -> n
-
- val succ_pos : n -> positive
-
- val add : n -> n -> n
-
- val sub : n -> n -> n
-
- val mul : n -> n -> n
-
- val compare : n -> n -> comparison
-
- val eqb : n -> n -> bool
-
- val leb : n -> n -> bool
-
- val ltb : n -> n -> bool
-
- val min : n -> n -> n
-
- val max : n -> n -> n
-
- val div2 : n -> n
-
- val even : n -> bool
-
- val odd : n -> bool
-
- val pow : n -> n -> n
-
- val square : n -> n
-
- val log2 : n -> n
-
- val size : n -> n
-
- val size_nat : n -> int
-
- val pos_div_eucl : positive -> n -> (n, n) prod
-
- val div_eucl : n -> n -> (n, n) prod
-
- val div : n -> n -> n
-
- val modulo : n -> n -> n
-
- val gcd : n -> n -> n
-
- val ggcd : n -> n -> (n, (n, n) prod) prod
-
- val sqrtrem : n -> (n, n) prod
-
- val sqrt : n -> n
-
- val coq_lor : n -> n -> n
-
- val coq_land : n -> n -> n
-
- val ldiff : n -> n -> n
-
- val coq_lxor : n -> n -> n
-
- val shiftl_nat : n -> int -> n
-
- val shiftr_nat : n -> int -> n
-
- val shiftl : n -> n -> n
-
- val shiftr : n -> n -> n
-
- val testbit_nat : n -> int -> bool
-
- val testbit : n -> n -> bool
-
- val to_nat : n -> int
-
- val of_nat : int -> n
-
- val iter : n -> ('a1 -> 'a1) -> 'a1 -> 'a1
-
- val eq_dec : n -> n -> bool
-
- val discr : n -> positive sumor
-
- val binary_rect : 'a1 -> (n -> 'a1 -> 'a1) -> (n -> 'a1 -> 'a1) -> n -> 'a1
-
- val binary_rec : 'a1 -> (n -> 'a1 -> 'a1) -> (n -> 'a1 -> 'a1) -> n -> 'a1
-
- val peano_rect : 'a1 -> (n -> 'a1 -> 'a1) -> n -> 'a1
-
- val peano_rec : 'a1 -> (n -> 'a1 -> 'a1) -> n -> 'a1
-
- val leb_spec0 : n -> n -> reflect
-
- val ltb_spec0 : n -> n -> reflect
-
- module Private_BootStrap :
- sig
-
- end
-
- val recursion : 'a1 -> (n -> 'a1 -> 'a1) -> n -> 'a1
-
- module Private_OrderTac :
- sig
- module Elts :
- sig
- type t = n
- end
-
- module Tac :
- sig
-
- end
- end
-
- module Private_NZPow :
- sig
-
- end
-
- module Private_NZSqrt :
- sig
-
- end
-
- val sqrt_up : n -> n
-
- val log2_up : n -> n
-
- module Private_NZDiv :
- sig
-
- end
-
- val lcm : n -> n -> n
-
- val eqb_spec : n -> n -> reflect
-
- val b2n : bool -> n
-
- val setbit : n -> n -> n
-
- val clearbit : n -> n -> n
-
- val ones : n -> n
-
- val lnot : n -> n -> n
-
- module Private_Tac :
- sig
-
- end
-
- module Private_Rev :
- sig
- module ORev :
- sig
- type t = n
- end
-
- module MRev :
- sig
- val max : n -> n -> n
- end
-
- module MPRev :
- sig
- module Private_Tac :
- sig
-
- end
- end
- end
-
- module Private_Dec :
- sig
- val max_case_strong :
- n -> n -> (n -> n -> __ -> 'a1 -> 'a1) -> (__ -> 'a1) -> (__ -> 'a1) ->
- 'a1
-
- val max_case :
- n -> n -> (n -> n -> __ -> 'a1 -> 'a1) -> 'a1 -> 'a1 -> 'a1
-
- val max_dec : n -> n -> bool
-
- val min_case_strong :
- n -> n -> (n -> n -> __ -> 'a1 -> 'a1) -> (__ -> 'a1) -> (__ -> 'a1) ->
- 'a1
-
- val min_case :
- n -> n -> (n -> n -> __ -> 'a1 -> 'a1) -> 'a1 -> 'a1 -> 'a1
-
- val min_dec : n -> n -> bool
- end
-
- val max_case_strong : n -> n -> (__ -> 'a1) -> (__ -> 'a1) -> 'a1
-
- val max_case : n -> n -> 'a1 -> 'a1 -> 'a1
-
- val max_dec : n -> n -> bool
-
- val min_case_strong : n -> n -> (__ -> 'a1) -> (__ -> 'a1) -> 'a1
-
- val min_case : n -> n -> 'a1 -> 'a1 -> 'a1
-
- val min_dec : n -> n -> bool
- end
-
-val eq_nat_dec : int -> int -> bool
-
-val beq_nat : int -> int -> bool
-
-val rev : 'a1 list -> 'a1 list
-
-val map : ('a1 -> 'a2) -> 'a1 list -> 'a2 list
-
-val fold_left : ('a1 -> 'a2 -> 'a1) -> 'a2 list -> 'a1 -> 'a1
-
-val fold_right : ('a2 -> 'a1 -> 'a1) -> 'a1 -> 'a2 list -> 'a1
-
-val forallb : ('a1 -> bool) -> 'a1 list -> bool
-
-val n_of_digits : bool list -> n
-
-val n_of_ascii : char -> n
-
-val nat_of_ascii : char -> int
-
-type string =
-| EmptyString
-| String of char * string
-
-val string_dec : string -> string -> bool
-
-val append : string -> string -> string
-
-val ble_nat : int -> int -> bool
-
-type id =
- int
- (* singleton inductive, whose constructor was Id *)
-
-val eq_id_dec : id -> id -> bool
-
-type state = id -> int
-
-val empty_state : state
-
-val update : state -> id -> int -> state
-
-type aexp =
-| ANum of int
-| AId of id
-| APlus of aexp * aexp
-| AMinus of aexp * aexp
-| AMult of aexp * aexp
-
-type bexp =
-| BTrue
-| BFalse
-| BEq of aexp * aexp
-| BLe of aexp * aexp
-| BNot of bexp
-| BAnd of bexp * bexp
-
-val aeval : state -> aexp -> int
-
-val beval : state -> bexp -> bool
-
-type com =
-| CSkip
-| CAss of id * aexp
-| CSeq of com * com
-| CIf of bexp * com * com
-| CWhile of bexp * com
-
-val ceval_step : state -> com -> int -> state option
-
-val isWhite : char -> bool
-
-val isLowerAlpha : char -> bool
-
-val isAlpha : char -> bool
-
-val isDigit : char -> bool
-
-type chartype =
-| White
-| Alpha
-| Digit
-| Other
-
-val classifyChar : char -> chartype
-
-val list_of_string : string -> char list
-
-val string_of_list : char list -> string
-
-type token = string
-
-val tokenize_helper : chartype -> char list -> char list -> char list list
-
-val tokenize : string -> string list
-
-type 'x optionE =
-| SomeE of 'x
-| NoneE of string
-
-val build_symtable : token list -> int -> token -> int
-
-type 't parser0 = token list -> ('t, token list) prod optionE
-
-val many_helper :
- 'a1 parser0 -> 'a1 list -> int -> token list -> ('a1 list, token list) prod
- optionE
-
-val many : 'a1 parser0 -> int -> 'a1 list parser0
-
-val firstExpect : token -> 'a1 parser0 -> 'a1 parser0
-
-val expect : token -> unit0 parser0
-
-val parseIdentifier :
- (string -> int) -> token list -> (id, token list) prod optionE
-
-val parseNumber : token list -> (int, token list) prod optionE
-
-val parsePrimaryExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE
-
-val parseProductExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE
-
-val parseSumExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE
-
-val parseAExp :
- int -> (string -> int) -> token list -> (aexp, token list) prod optionE
-
-val parseAtomicExp :
- int -> (string -> int) -> token list -> (bexp, token list) prod optionE
-
-val parseConjunctionExp :
- int -> (string -> int) -> token list -> (bexp, token list) prod optionE
-
-val parseBExp :
- int -> (string -> int) -> token list -> (bexp, token list) prod optionE
-
-val parseSimpleCommand :
- int -> (string -> int) -> token list -> (com, token list) prod optionE
-
-val parseSequencedCommand :
- int -> (string -> int) -> token list -> (com, token list) prod optionE
-
-val bignumber : int
-
-val parse : string -> (com, token list) prod optionE
-
diff --git a/imp1.ml b/imp1.ml
deleted file mode 100644
index e141d30..0000000
--- a/imp1.ml
+++ /dev/null
@@ -1,168 +0,0 @@
-type bool =
-| True
-| False
-
-(** val negb : bool -> bool **)
-
-let negb = function
-| True -> False
-| False -> True
-
-type nat =
-| O
-| S of nat
-
-type 'a option =
-| Some of 'a
-| None
-
-type sumbool =
-| Left
-| Right
-
-(** val plus : nat -> nat -> nat **)
-
-let rec plus n m =
- match n with
- | O -> m
- | S p -> S (plus p m)
-
-(** val mult : nat -> nat -> nat **)
-
-let rec mult n m =
- match n with
- | O -> O
- | S p -> plus m (mult p m)
-
-(** val minus : nat -> nat -> nat **)
-
-let rec minus n m =
- match n with
- | O -> n
- | S k ->
- (match m with
- | O -> n
- | S l -> minus k l)
-
-(** val eq_nat_dec : nat -> nat -> sumbool **)
-
-let rec eq_nat_dec n m =
- match n with
- | O ->
- (match m with
- | O -> Left
- | S m0 -> Right)
- | S n0 ->
- (match m with
- | O -> Right
- | S m0 -> eq_nat_dec n0 m0)
-
-(** val beq_nat : nat -> nat -> bool **)
-
-let rec beq_nat n m =
- match n with
- | O ->
- (match m with
- | O -> True
- | S n0 -> False)
- | S n1 ->
- (match m with
- | O -> False
- | S m1 -> beq_nat n1 m1)
-
-(** val ble_nat : nat -> nat -> bool **)
-
-let rec ble_nat n m =
- match n with
- | O -> True
- | S n' ->
- (match m with
- | O -> False
- | S m' -> ble_nat n' m')
-
-type id =
- nat
- (* singleton inductive, whose constructor was Id *)
-
-(** val eq_id_dec : id -> id -> sumbool **)
-
-let eq_id_dec id1 id2 =
- eq_nat_dec id1 id2
-
-type state = id -> nat
-
-(** val update : state -> id -> nat -> state **)
-
-let update st x n x' =
- match eq_id_dec x x' with
- | Left -> n
- | Right -> st x'
-
-type aexp =
-| ANum of nat
-| AId of id
-| APlus of aexp * aexp
-| AMinus of aexp * aexp
-| AMult of aexp * aexp
-
-type bexp =
-| BTrue
-| BFalse
-| BEq of aexp * aexp
-| BLe of aexp * aexp
-| BNot of bexp
-| BAnd of bexp * bexp
-
-(** val aeval : state -> aexp -> nat **)
-
-let rec aeval st = function
-| ANum n -> n
-| AId x -> st x
-| APlus (a1, a2) -> plus (aeval st a1) (aeval st a2)
-| AMinus (a1, a2) -> minus (aeval st a1) (aeval st a2)
-| AMult (a1, a2) -> mult (aeval st a1) (aeval st a2)
-
-(** val beval : state -> bexp -> bool **)
-
-let rec beval st = function
-| BTrue -> True
-| BFalse -> False
-| BEq (a1, a2) -> beq_nat (aeval st a1) (aeval st a2)
-| BLe (a1, a2) -> ble_nat (aeval st a1) (aeval st a2)
-| BNot b1 -> negb (beval st b1)
-| BAnd (b1, b2) ->
- (match beval st b1 with
- | True -> beval st b2
- | False -> False)
-
-type com =
-| CSkip
-| CAss of id * aexp
-| CSeq of com * com
-| CIf of bexp * com * com
-| CWhile of bexp * com
-
-(** val ceval_step : state -> com -> nat -> state option **)
-
-let rec ceval_step st c = function
-| O -> None
-| S i' ->
- (match c with
- | CSkip -> Some st
- | CAss (l, a1) -> Some (update st l (aeval st a1))
- | CSeq (c1, c2) ->
- (match ceval_step st c1 i' with
- | Some st' -> ceval_step st' c2 i'
- | None -> None)
- | CIf (b, c1, c2) ->
- (match beval st b with
- | True -> ceval_step st c1 i'
- | False -> ceval_step st c2 i')
- | CWhile (b1, c1) ->
- (match beval st b1 with
- | True ->
- (match ceval_step st c1 i' with
- | Some st' -> ceval_step st' c i'
- | None -> None)
- | False -> Some st))
-
diff --git a/imp1.mli b/imp1.mli
deleted file mode 100644
index ae13b58..0000000
--- a/imp1.mli
+++ /dev/null
@@ -1,68 +0,0 @@
-type bool =
-| True
-| False
-
-val negb : bool -> bool
-
-type nat =
-| O
-| S of nat
-
-type 'a option =
-| Some of 'a
-| None
-
-type sumbool =
-| Left
-| Right
-
-val plus : nat -> nat -> nat
-
-val mult : nat -> nat -> nat
-
-val minus : nat -> nat -> nat
-
-val eq_nat_dec : nat -> nat -> sumbool
-
-val beq_nat : nat -> nat -> bool
-
-val ble_nat : nat -> nat -> bool
-
-type id =
- nat
- (* singleton inductive, whose constructor was Id *)
-
-val eq_id_dec : id -> id -> sumbool
-
-type state = id -> nat
-
-val update : state -> id -> nat -> state
-
-type aexp =
-| ANum of nat
-| AId of id
-| APlus of aexp * aexp
-| AMinus of aexp * aexp
-| AMult of aexp * aexp
-
-type bexp =
-| BTrue
-| BFalse
-| BEq of aexp * aexp
-| BLe of aexp * aexp
-| BNot of bexp
-| BAnd of bexp * bexp
-
-val aeval : state -> aexp -> nat
-
-val beval : state -> bexp -> bool
-
-type com =
-| CSkip
-| CAss of id * aexp
-| CSeq of com * com
-| CIf of bexp * com * com
-| CWhile of bexp * com
-
-val ceval_step : state -> com -> nat -> state option
-
diff --git a/imp2.ml b/imp2.ml
deleted file mode 100644
index abb86bd..0000000
--- a/imp2.ml
+++ /dev/null
@@ -1,167 +0,0 @@
-(** val negb : bool -> bool **)
-
-let negb = function
-| true -> false
-| false -> true
-
-type 'a option =
-| Some of 'a
-| None
-
-type sumbool =
-| Left
-| Right
-
-(** val plus : int -> int -> int **)
-
-let rec plus = ( + )
-
-(** val mult : int -> int -> int **)
-
-let rec mult = ( * )
-
-(** val minus : int -> int -> int **)
-
-let rec minus n m =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- n)
- (fun k ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- n)
- (fun l ->
- minus k l)
- m)
- n
-
-(** val eq_nat_dec : int -> int -> sumbool **)
-
-let rec eq_nat_dec n m =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- Left)
- (fun m0 ->
- Right)
- m)
- (fun n0 ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- Right)
- (fun m0 ->
- eq_nat_dec n0 m0)
- m)
- n
-
-(** val beq_nat : int -> int -> bool **)
-
-let rec beq_nat = ( = )
-
-(** val ble_nat : int -> int -> bool **)
-
-let rec ble_nat n m =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- true)
- (fun n' ->
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- false)
- (fun m' ->
- ble_nat n' m')
- m)
- n
-
-type id =
- int
- (* singleton inductive, whose constructor was Id *)
-
-(** val eq_id_dec : id -> id -> sumbool **)
-
-let eq_id_dec id1 id2 =
- eq_nat_dec id1 id2
-
-type state = id -> int
-
-(** val update : state -> id -> int -> state **)
-
-let update st x n x' =
- match eq_id_dec x x' with
- | Left -> n
- | Right -> st x'
-
-type aexp =
-| ANum of int
-| AId of id
-| APlus of aexp * aexp
-| AMinus of aexp * aexp
-| AMult of aexp * aexp
-
-type bexp =
-| BTrue
-| BFalse
-| BEq of aexp * aexp
-| BLe of aexp * aexp
-| BNot of bexp
-| BAnd of bexp * bexp
-
-(** val aeval : state -> aexp -> int **)
-
-let rec aeval st = function
-| ANum n -> n
-| AId x -> st x
-| APlus (a1, a2) -> plus (aeval st a1) (aeval st a2)
-| AMinus (a1, a2) -> minus (aeval st a1) (aeval st a2)
-| AMult (a1, a2) -> mult (aeval st a1) (aeval st a2)
-
-(** val beval : state -> bexp -> bool **)
-
-let rec beval st = function
-| BTrue -> true
-| BFalse -> false
-| BEq (a1, a2) -> beq_nat (aeval st a1) (aeval st a2)
-| BLe (a1, a2) -> ble_nat (aeval st a1) (aeval st a2)
-| BNot b1 -> negb (beval st b1)
-| BAnd (b1, b2) -> if beval st b1 then beval st b2 else false
-
-type com =
-| CSkip
-| CAss of id * aexp
-| CSeq of com * com
-| CIf of bexp * com * com
-| CWhile of bexp * com
-
-(** val ceval_step : state -> com -> int -> state option **)
-
-let rec ceval_step st c i =
- (fun zero succ n ->
- if n=0 then zero () else succ (n-1))
- (fun _ ->
- None)
- (fun i' ->
- match c with
- | CSkip -> Some st
- | CAss (l, a1) -> Some (update st l (aeval st a1))
- | CSeq (c1, c2) ->
- (match ceval_step st c1 i' with
- | Some st' -> ceval_step st' c2 i'
- | None -> None)
- | CIf (b, c1, c2) ->
- if beval st b then ceval_step st c1 i' else ceval_step st c2 i'
- | CWhile (b1, c1) ->
- if beval st b1
- then (match ceval_step st c1 i' with
- | Some st' -> ceval_step st' c i'
- | None -> None)
- else Some st)
- i
-
diff --git a/imp2.mli b/imp2.mli
deleted file mode 100644
index 5b12dd9..0000000
--- a/imp2.mli
+++ /dev/null
@@ -1,60 +0,0 @@
-val negb : bool -> bool
-
-type 'a option =
-| Some of 'a
-| None
-
-type sumbool =
-| Left
-| Right
-
-val plus : int -> int -> int
-
-val mult : int -> int -> int
-
-val minus : int -> int -> int
-
-val eq_nat_dec : int -> int -> sumbool
-
-val beq_nat : int -> int -> bool
-
-val ble_nat : int -> int -> bool
-
-type id =
- int
- (* singleton inductive, whose constructor was Id *)
-
-val eq_id_dec : id -> id -> sumbool
-
-type state = id -> int
-
-val update : state -> id -> int -> state
-
-type aexp =
-| ANum of int
-| AId of id
-| APlus of aexp * aexp
-| AMinus of aexp * aexp
-| AMult of aexp * aexp
-
-type bexp =
-| BTrue
-| BFalse
-| BEq of aexp * aexp
-| BLe of aexp * aexp
-| BNot of bexp
-| BAnd of bexp * bexp
-
-val aeval : state -> aexp -> int
-
-val beval : state -> bexp -> bool
-
-type com =
-| CSkip
-| CAss of id * aexp
-| CSeq of com * com
-| CIf of bexp * com * com
-| CWhile of bexp * com
-
-val ceval_step : state -> com -> int -> state option
-
diff --git a/impdriver.ml b/impdriver.ml
deleted file mode 100644
index 8a32855..0000000
--- a/impdriver.ml
+++ /dev/null
@@ -1,39 +0,0 @@
-open Imp
-
-let explode s =
- let rec exp i l =
- if i < 0 then l else exp (i - 1) (s.[i] :: l) in
- exp (String.length s - 1) [];;
-
-let tweak_string s =
- let ss = explode s in
- let rec loop = function
- [] -> EmptyString
- | h::t -> String (h, loop t)
- in loop ss;;
-
-let test s =
- print_endline s;
- let parse_res = parse (tweak_string s) in
- (match parse_res with
- NoneE _ -> print_endline ("Syntax error");
- | SomeE (Pair (c, _)) ->
- let fuel = 1000 in
- match (ceval_step empty_state c fuel) with
- None -> print_endline ("Still running after " ^ string_of_int fuel ^ " steps")
- | Some res ->
- print_endline ("Result: [" ^ string_of_int (res 0)
- ^ " " ^ string_of_int (res 1)
- ^ " " ^ string_of_int (res 2)
- ^ " " ^ string_of_int (res 3)
- ^ " ...]"));
- print_newline();
-;;
-
-test "true";;
-test "SKIP";;
-test "SKIP;SKIP";;
-test "WHILE true DO SKIP END";;
-test "x:=3";;
-test "x:=3; WHILE 0<=x DO SKIP END";;
-test "x:=3; WHILE 1<=x DO y:=y+1; x:=x-1 END";;
diff --git a/index-bg.jpg b/index-bg.jpg
deleted file mode 100644
index a0547ef..0000000
Binary files a/index-bg.jpg and /dev/null differ
diff --git a/index.html b/index.html
deleted file mode 100644
index 9388e49..0000000
--- a/index.html
+++ /dev/null
@@ -1,93 +0,0 @@
-
-
-
-
-
-
-Software Foundations
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Software Foundations
-
-
-
-
-
- Benjamin C. Pierce
- Chris Casinghino
- Marco Gaboardi
- Michael Greenberg
- Cătălin Hriţcu
- Vilhelm Sjberg
- Brent Yorgey
-
-
-
-
- with
- Loris D'Antoni,
- Andrew W. Appel,
- Arthur Azevedo de Amorim,
- Arthur Chargueraud,
- Anthony Cowley,
- Jeffrey Foster,
- Dmitri Garbuzov,
- Michael Hicks,
- Ranjit Jhala,
- Greg Morrisett,
- Jennifer Paykin,
- Mukund Raghothaman,
- Chung-chieh Shan,
- Leonid Spesivtsev,
- Andrew Tolmach,
- and
- Steve Zdancewic
-
-
-
-
-
-
diff --git a/jquery-1.8.3.js b/jquery-1.8.3.js
deleted file mode 100644
index a86bf79..0000000
--- a/jquery-1.8.3.js
+++ /dev/null
@@ -1,9472 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.8.3
- * http://jquery.com/
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- *
- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)
- */
-(function( window, undefined ) {
-var
- // A central reference to the root jQuery(document)
- rootjQuery,
-
- // The deferred used on DOM ready
- readyList,
-
- // Use the correct document accordingly with window argument (sandbox)
- document = window.document,
- location = window.location,
- navigator = window.navigator,
-
- // Map over jQuery in case of overwrite
- _jQuery = window.jQuery,
-
- // Map over the $ in case of overwrite
- _$ = window.$,
-
- // Save a reference to some core methods
- core_push = Array.prototype.push,
- core_slice = Array.prototype.slice,
- core_indexOf = Array.prototype.indexOf,
- core_toString = Object.prototype.toString,
- core_hasOwn = Object.prototype.hasOwnProperty,
- core_trim = String.prototype.trim,
-
- // Define a local copy of jQuery
- jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context, rootjQuery );
- },
-
- // Used for matching numbers
- core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
-
- // Used for detecting and trimming whitespace
- core_rnotwhite = /\S/,
- core_rspace = /\s+/,
-
- // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
- rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-
- // A simple way to check for HTML strings
- // Prioritize #id over to avoid XSS via location.hash (#9521)
- rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
-
- // Match a standalone tag
- rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
-
- // JSON RegExp
- rvalidchars = /^[\],:{}\s]*$/,
- rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
- rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
- rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
-
- // Matches dashed string for camelizing
- rmsPrefix = /^-ms-/,
- rdashAlpha = /-([\da-z])/gi,
-
- // Used by jQuery.camelCase as callback to replace()
- fcamelCase = function( all, letter ) {
- return ( letter + "" ).toUpperCase();
- },
-
- // The ready event handler and self cleanup method
- DOMContentLoaded = function() {
- if ( document.addEventListener ) {
- document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
- jQuery.ready();
- } else if ( document.readyState === "complete" ) {
- // we're here because readyState === "complete" in oldIE
- // which is good enough for us to call the dom ready!
- document.detachEvent( "onreadystatechange", DOMContentLoaded );
- jQuery.ready();
- }
- },
-
- // [[Class]] -> type pairs
- class2type = {};
-
-jQuery.fn = jQuery.prototype = {
- constructor: jQuery,
- init: function( selector, context, rootjQuery ) {
- var match, elem, ret, doc;
-
- // Handle $(""), $(null), $(undefined), $(false)
- if ( !selector ) {
- return this;
- }
-
- // Handle $(DOMElement)
- if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
- }
-
- // Handle HTML strings
- if ( typeof selector === "string" ) {
- if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
- // Assume that strings that start and end with <> are HTML and skip the regex check
- match = [ null, selector, null ];
-
- } else {
- match = rquickExpr.exec( selector );
- }
-
- // Match html or make sure no context is specified for #id
- if ( match && (match[1] || !context) ) {
-
- // HANDLE: $(html) -> $(array)
- if ( match[1] ) {
- context = context instanceof jQuery ? context[0] : context;
- doc = ( context && context.nodeType ? context.ownerDocument || context : document );
-
- // scripts is true for back-compat
- selector = jQuery.parseHTML( match[1], doc, true );
- if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
- this.attr.call( selector, context, true );
- }
-
- return jQuery.merge( this, selector );
-
- // HANDLE: $(#id)
- } else {
- elem = document.getElementById( match[2] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id !== match[2] ) {
- return rootjQuery.find( selector );
- }
-
- // Otherwise, we inject the element directly into the jQuery object
- this.length = 1;
- this[0] = elem;
- }
-
- this.context = document;
- this.selector = selector;
- return this;
- }
-
- // HANDLE: $(expr, $(...))
- } else if ( !context || context.jquery ) {
- return ( context || rootjQuery ).find( selector );
-
- // HANDLE: $(expr, context)
- // (which is just equivalent to: $(context).find(expr)
- } else {
- return this.constructor( context ).find( selector );
- }
-
- // HANDLE: $(function)
- // Shortcut for document ready
- } else if ( jQuery.isFunction( selector ) ) {
- return rootjQuery.ready( selector );
- }
-
- if ( selector.selector !== undefined ) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
-
- return jQuery.makeArray( selector, this );
- },
-
- // Start with an empty selector
- selector: "",
-
- // The current version of jQuery being used
- jquery: "1.8.3",
-
- // The default length of a jQuery object is 0
- length: 0,
-
- // The number of elements contained in the matched element set
- size: function() {
- return this.length;
- },
-
- toArray: function() {
- return core_slice.call( this );
- },
-
- // Get the Nth element in the matched element set OR
- // Get the whole matched element set as a clean array
- get: function( num ) {
- return num == null ?
-
- // Return a 'clean' array
- this.toArray() :
-
- // Return just the object
- ( num < 0 ? this[ this.length + num ] : this[ num ] );
- },
-
- // Take an array of elements and push it onto the stack
- // (returning the new matched element set)
- pushStack: function( elems, name, selector ) {
-
- // Build a new jQuery matched element set
- var ret = jQuery.merge( this.constructor(), elems );
-
- // Add the old object onto the stack (as a reference)
- ret.prevObject = this;
-
- ret.context = this.context;
-
- if ( name === "find" ) {
- ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
- } else if ( name ) {
- ret.selector = this.selector + "." + name + "(" + selector + ")";
- }
-
- // Return the newly-formed element set
- return ret;
- },
-
- // Execute a callback for every element in the matched set.
- // (You can seed the arguments with an array of args, but this is
- // only used internally.)
- each: function( callback, args ) {
- return jQuery.each( this, callback, args );
- },
-
- ready: function( fn ) {
- // Add the callback
- jQuery.ready.promise().done( fn );
-
- return this;
- },
-
- eq: function( i ) {
- i = +i;
- return i === -1 ?
- this.slice( i ) :
- this.slice( i, i + 1 );
- },
-
- first: function() {
- return this.eq( 0 );
- },
-
- last: function() {
- return this.eq( -1 );
- },
-
- slice: function() {
- return this.pushStack( core_slice.apply( this, arguments ),
- "slice", core_slice.call(arguments).join(",") );
- },
-
- map: function( callback ) {
- return this.pushStack( jQuery.map(this, function( elem, i ) {
- return callback.call( elem, i, elem );
- }));
- },
-
- end: function() {
- return this.prevObject || this.constructor(null);
- },
-
- // For internal use only.
- // Behaves like an Array's method, not like a jQuery method.
- push: core_push,
- sort: [].sort,
- splice: [].splice
-};
-
-// Give the init function the jQuery prototype for later instantiation
-jQuery.fn.init.prototype = jQuery.fn;
-
-jQuery.extend = jQuery.fn.extend = function() {
- var options, name, src, copy, copyIsArray, clone,
- target = arguments[0] || {},
- i = 1,
- length = arguments.length,
- deep = false;
-
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
- target = arguments[1] || {};
- // skip the boolean and the target
- i = 2;
- }
-
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
- target = {};
- }
-
- // extend jQuery itself if only one argument is passed
- if ( length === i ) {
- target = this;
- --i;
- }
-
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
-
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
-
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && jQuery.isArray(src) ? src : [];
-
- } else {
- clone = src && jQuery.isPlainObject(src) ? src : {};
- }
-
- // Never move original objects, clone them
- target[ name ] = jQuery.extend( deep, clone, copy );
-
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
-
- // Return the modified object
- return target;
-};
-
-jQuery.extend({
- noConflict: function( deep ) {
- if ( window.$ === jQuery ) {
- window.$ = _$;
- }
-
- if ( deep && window.jQuery === jQuery ) {
- window.jQuery = _jQuery;
- }
-
- return jQuery;
- },
-
- // Is the DOM ready to be used? Set to true once it occurs.
- isReady: false,
-
- // A counter to track how many items to wait for before
- // the ready event fires. See #6781
- readyWait: 1,
-
- // Hold (or release) the ready event
- holdReady: function( hold ) {
- if ( hold ) {
- jQuery.readyWait++;
- } else {
- jQuery.ready( true );
- }
- },
-
- // Handle when the DOM is ready
- ready: function( wait ) {
-
- // Abort if there are pending holds or we're already ready
- if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
- return;
- }
-
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready, 1 );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.trigger ) {
- jQuery( document ).trigger("ready").off("ready");
- }
- },
-
- // See test/unit/core.js for details concerning isFunction.
- // Since version 1.3, DOM methods and functions like alert
- // aren't supported. They return false on IE (#2968).
- isFunction: function( obj ) {
- return jQuery.type(obj) === "function";
- },
-
- isArray: Array.isArray || function( obj ) {
- return jQuery.type(obj) === "array";
- },
-
- isWindow: function( obj ) {
- return obj != null && obj == obj.window;
- },
-
- isNumeric: function( obj ) {
- return !isNaN( parseFloat(obj) ) && isFinite( obj );
- },
-
- type: function( obj ) {
- return obj == null ?
- String( obj ) :
- class2type[ core_toString.call(obj) ] || "object";
- },
-
- isPlainObject: function( obj ) {
- // Must be an Object.
- // Because of IE, we also have to check the presence of the constructor property.
- // Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
- return false;
- }
-
- try {
- // Not own constructor property must be Object
- if ( obj.constructor &&
- !core_hasOwn.call(obj, "constructor") &&
- !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
- return false;
- }
- } catch ( e ) {
- // IE8,9 Will throw exceptions on certain host objects #9897
- return false;
- }
-
- // Own properties are enumerated firstly, so to speed up,
- // if last one is own, then all properties are own.
-
- var key;
- for ( key in obj ) {}
-
- return key === undefined || core_hasOwn.call( obj, key );
- },
-
- isEmptyObject: function( obj ) {
- var name;
- for ( name in obj ) {
- return false;
- }
- return true;
- },
-
- error: function( msg ) {
- throw new Error( msg );
- },
-
- // data: string of html
- // context (optional): If specified, the fragment will be created in this context, defaults to document
- // scripts (optional): If true, will include scripts passed in the html string
- parseHTML: function( data, context, scripts ) {
- var parsed;
- if ( !data || typeof data !== "string" ) {
- return null;
- }
- if ( typeof context === "boolean" ) {
- scripts = context;
- context = 0;
- }
- context = context || document;
-
- // Single tag
- if ( (parsed = rsingleTag.exec( data )) ) {
- return [ context.createElement( parsed[1] ) ];
- }
-
- parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
- return jQuery.merge( [],
- (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
- },
-
- parseJSON: function( data ) {
- if ( !data || typeof data !== "string") {
- return null;
- }
-
- // Make sure leading/trailing whitespace is removed (IE can't handle it)
- data = jQuery.trim( data );
-
- // Attempt to parse using the native JSON parser first
- if ( window.JSON && window.JSON.parse ) {
- return window.JSON.parse( data );
- }
-
- // Make sure the incoming data is actual JSON
- // Logic borrowed from http://json.org/json2.js
- if ( rvalidchars.test( data.replace( rvalidescape, "@" )
- .replace( rvalidtokens, "]" )
- .replace( rvalidbraces, "")) ) {
-
- return ( new Function( "return " + data ) )();
-
- }
- jQuery.error( "Invalid JSON: " + data );
- },
-
- // Cross-browser xml parsing
- parseXML: function( data ) {
- var xml, tmp;
- if ( !data || typeof data !== "string" ) {
- return null;
- }
- try {
- if ( window.DOMParser ) { // Standard
- tmp = new DOMParser();
- xml = tmp.parseFromString( data , "text/xml" );
- } else { // IE
- xml = new ActiveXObject( "Microsoft.XMLDOM" );
- xml.async = "false";
- xml.loadXML( data );
- }
- } catch( e ) {
- xml = undefined;
- }
- if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
- jQuery.error( "Invalid XML: " + data );
- }
- return xml;
- },
-
- noop: function() {},
-
- // Evaluates a script in a global context
- // Workarounds based on findings by Jim Driscoll
- // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
- globalEval: function( data ) {
- if ( data && core_rnotwhite.test( data ) ) {
- // We use execScript on Internet Explorer
- // We use an anonymous function so that context is window
- // rather than jQuery in Firefox
- ( window.execScript || function( data ) {
- window[ "eval" ].call( window, data );
- } )( data );
- }
- },
-
- // Convert dashed to camelCase; used by the css and data modules
- // Microsoft forgot to hump their vendor prefix (#9572)
- camelCase: function( string ) {
- return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
- },
-
- nodeName: function( elem, name ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
- },
-
- // args is for internal usage only
- each: function( obj, callback, args ) {
- var name,
- i = 0,
- length = obj.length,
- isObj = length === undefined || jQuery.isFunction( obj );
-
- if ( args ) {
- if ( isObj ) {
- for ( name in obj ) {
- if ( callback.apply( obj[ name ], args ) === false ) {
- break;
- }
- }
- } else {
- for ( ; i < length; ) {
- if ( callback.apply( obj[ i++ ], args ) === false ) {
- break;
- }
- }
- }
-
- // A special, fast, case for the most common use of each
- } else {
- if ( isObj ) {
- for ( name in obj ) {
- if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
- break;
- }
- }
- } else {
- for ( ; i < length; ) {
- if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
- break;
- }
- }
- }
- }
-
- return obj;
- },
-
- // Use native String.trim function wherever possible
- trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
- function( text ) {
- return text == null ?
- "" :
- core_trim.call( text );
- } :
-
- // Otherwise use our own trimming functionality
- function( text ) {
- return text == null ?
- "" :
- ( text + "" ).replace( rtrim, "" );
- },
-
- // results is for internal usage only
- makeArray: function( arr, results ) {
- var type,
- ret = results || [];
-
- if ( arr != null ) {
- // The window, strings (and functions) also have 'length'
- // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
- type = jQuery.type( arr );
-
- if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
- core_push.call( ret, arr );
- } else {
- jQuery.merge( ret, arr );
- }
- }
-
- return ret;
- },
-
- inArray: function( elem, arr, i ) {
- var len;
-
- if ( arr ) {
- if ( core_indexOf ) {
- return core_indexOf.call( arr, elem, i );
- }
-
- len = arr.length;
- i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
-
- for ( ; i < len; i++ ) {
- // Skip accessing in sparse arrays
- if ( i in arr && arr[ i ] === elem ) {
- return i;
- }
- }
- }
-
- return -1;
- },
-
- merge: function( first, second ) {
- var l = second.length,
- i = first.length,
- j = 0;
-
- if ( typeof l === "number" ) {
- for ( ; j < l; j++ ) {
- first[ i++ ] = second[ j ];
- }
-
- } else {
- while ( second[j] !== undefined ) {
- first[ i++ ] = second[ j++ ];
- }
- }
-
- first.length = i;
-
- return first;
- },
-
- grep: function( elems, callback, inv ) {
- var retVal,
- ret = [],
- i = 0,
- length = elems.length;
- inv = !!inv;
-
- // Go through the array, only saving the items
- // that pass the validator function
- for ( ; i < length; i++ ) {
- retVal = !!callback( elems[ i ], i );
- if ( inv !== retVal ) {
- ret.push( elems[ i ] );
- }
- }
-
- return ret;
- },
-
- // arg is for internal usage only
- map: function( elems, callback, arg ) {
- var value, key,
- ret = [],
- i = 0,
- length = elems.length,
- // jquery objects are treated as arrays
- isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
-
- // Go through the array, translating each of the items to their
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
-
- // Go through every key on the object,
- } else {
- for ( key in elems ) {
- value = callback( elems[ key ], key, arg );
-
- if ( value != null ) {
- ret[ ret.length ] = value;
- }
- }
- }
-
- // Flatten any nested arrays
- return ret.concat.apply( [], ret );
- },
-
- // A global GUID counter for objects
- guid: 1,
-
- // Bind a function to a context, optionally partially applying any
- // arguments.
- proxy: function( fn, context ) {
- var tmp, args, proxy;
-
- if ( typeof context === "string" ) {
- tmp = fn[ context ];
- context = fn;
- fn = tmp;
- }
-
- // Quick check to determine if target is callable, in the spec
- // this throws a TypeError, but we will just return undefined.
- if ( !jQuery.isFunction( fn ) ) {
- return undefined;
- }
-
- // Simulated bind
- args = core_slice.call( arguments, 2 );
- proxy = function() {
- return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
- };
-
- // Set the guid of unique handler to the same of original handler, so it can be removed
- proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
- return proxy;
- },
-
- // Multifunctional method to get and set values of a collection
- // The value/s can optionally be executed if it's a function
- access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
- var exec,
- bulk = key == null,
- i = 0,
- length = elems.length;
-
- // Sets many values
- if ( key && typeof key === "object" ) {
- for ( i in key ) {
- jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
- }
- chainable = 1;
-
- // Sets one value
- } else if ( value !== undefined ) {
- // Optionally, function values get executed if exec is true
- exec = pass === undefined && jQuery.isFunction( value );
-
- if ( bulk ) {
- // Bulk operations only iterate when executing function values
- if ( exec ) {
- exec = fn;
- fn = function( elem, key, value ) {
- return exec.call( jQuery( elem ), value );
- };
-
- // Otherwise they run against the entire set
- } else {
- fn.call( elems, value );
- fn = null;
- }
- }
-
- if ( fn ) {
- for (; i < length; i++ ) {
- fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
- }
- }
-
- chainable = 1;
- }
-
- return chainable ?
- elems :
-
- // Gets
- bulk ?
- fn.call( elems ) :
- length ? fn( elems[0], key ) : emptyGet;
- },
-
- now: function() {
- return ( new Date() ).getTime();
- }
-});
-
-jQuery.ready.promise = function( obj ) {
- if ( !readyList ) {
-
- readyList = jQuery.Deferred();
-
- // Catch cases where $(document).ready() is called after the browser event has already occurred.
- // we once tried to use readyState "interactive" here, but it caused issues like the one
- // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
- setTimeout( jQuery.ready, 1 );
-
- // Standards-based browsers support DOMContentLoaded
- } else if ( document.addEventListener ) {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
-
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", jQuery.ready, false );
-
- // If IE event model is used
- } else {
- // Ensure firing before onload, maybe late but safe also for iframes
- document.attachEvent( "onreadystatechange", DOMContentLoaded );
-
- // A fallback to window.onload, that will always work
- window.attachEvent( "onload", jQuery.ready );
-
- // If IE and not a frame
- // continually check to see if the document is ready
- var top = false;
-
- try {
- top = window.frameElement == null && document.documentElement;
- } catch(e) {}
-
- if ( top && top.doScroll ) {
- (function doScrollCheck() {
- if ( !jQuery.isReady ) {
-
- try {
- // Use the trick by Diego Perini
- // http://javascript.nwbox.com/IEContentLoaded/
- top.doScroll("left");
- } catch(e) {
- return setTimeout( doScrollCheck, 50 );
- }
-
- // and execute any waiting functions
- jQuery.ready();
- }
- })();
- }
- }
- }
- return readyList.promise( obj );
-};
-
-// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
-});
-
-// All jQuery objects should point back to these
-rootjQuery = jQuery(document);
-// String to Object options format cache
-var optionsCache = {};
-
-// Convert String-formatted options into Object-formatted ones and store in cache
-function createOptions( options ) {
- var object = optionsCache[ options ] = {};
- jQuery.each( options.split( core_rspace ), function( _, flag ) {
- object[ flag ] = true;
- });
- return object;
-}
-
-/*
- * Create a callback list using the following parameters:
- *
- * options: an optional list of space-separated options that will change how
- * the callback list behaves or a more traditional option object
- *
- * By default a callback list will act like an event callback list and can be
- * "fired" multiple times.
- *
- * Possible options:
- *
- * once: will ensure the callback list can only be fired once (like a Deferred)
- *
- * memory: will keep track of previous values and will call any callback added
- * after the list has been fired right away with the latest "memorized"
- * values (like a Deferred)
- *
- * unique: will ensure a callback can only be added once (no duplicate in the list)
- *
- * stopOnFalse: interrupt callings when a callback returns false
- *
- */
-jQuery.Callbacks = function( options ) {
-
- // Convert options from String-formatted to Object-formatted if needed
- // (we check in cache first)
- options = typeof options === "string" ?
- ( optionsCache[ options ] || createOptions( options ) ) :
- jQuery.extend( {}, options );
-
- var // Last fire value (for non-forgettable lists)
- memory,
- // Flag to know if list was already fired
- fired,
- // Flag to know if list is currently firing
- firing,
- // First callback to fire (used internally by add and fireWith)
- firingStart,
- // End of the loop when firing
- firingLength,
- // Index of currently firing callback (modified by remove if needed)
- firingIndex,
- // Actual callback list
- list = [],
- // Stack of fire calls for repeatable lists
- stack = !options.once && [],
- // Fire callbacks
- fire = function( data ) {
- memory = options.memory && data;
- fired = true;
- firingIndex = firingStart || 0;
- firingStart = 0;
- firingLength = list.length;
- firing = true;
- for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
- memory = false; // To prevent further calls using add
- break;
- }
- }
- firing = false;
- if ( list ) {
- if ( stack ) {
- if ( stack.length ) {
- fire( stack.shift() );
- }
- } else if ( memory ) {
- list = [];
- } else {
- self.disable();
- }
- }
- },
- // Actual Callbacks object
- self = {
- // Add a callback or a collection of callbacks to the list
- add: function() {
- if ( list ) {
- // First, we save the current length
- var start = list.length;
- (function add( args ) {
- jQuery.each( args, function( _, arg ) {
- var type = jQuery.type( arg );
- if ( type === "function" ) {
- if ( !options.unique || !self.has( arg ) ) {
- list.push( arg );
- }
- } else if ( arg && arg.length && type !== "string" ) {
- // Inspect recursively
- add( arg );
- }
- });
- })( arguments );
- // Do we need to add the callbacks to the
- // current firing batch?
- if ( firing ) {
- firingLength = list.length;
- // With memory, if we're not firing then
- // we should call right away
- } else if ( memory ) {
- firingStart = start;
- fire( memory );
- }
- }
- return this;
- },
- // Remove a callback from the list
- remove: function() {
- if ( list ) {
- jQuery.each( arguments, function( _, arg ) {
- var index;
- while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
- list.splice( index, 1 );
- // Handle firing indexes
- if ( firing ) {
- if ( index <= firingLength ) {
- firingLength--;
- }
- if ( index <= firingIndex ) {
- firingIndex--;
- }
- }
- }
- });
- }
- return this;
- },
- // Control if a given callback is in the list
- has: function( fn ) {
- return jQuery.inArray( fn, list ) > -1;
- },
- // Remove all callbacks from the list
- empty: function() {
- list = [];
- return this;
- },
- // Have the list do nothing anymore
- disable: function() {
- list = stack = memory = undefined;
- return this;
- },
- // Is it disabled?
- disabled: function() {
- return !list;
- },
- // Lock the list in its current state
- lock: function() {
- stack = undefined;
- if ( !memory ) {
- self.disable();
- }
- return this;
- },
- // Is it locked?
- locked: function() {
- return !stack;
- },
- // Call all callbacks with the given context and arguments
- fireWith: function( context, args ) {
- args = args || [];
- args = [ context, args.slice ? args.slice() : args ];
- if ( list && ( !fired || stack ) ) {
- if ( firing ) {
- stack.push( args );
- } else {
- fire( args );
- }
- }
- return this;
- },
- // Call all the callbacks with the given arguments
- fire: function() {
- self.fireWith( this, arguments );
- return this;
- },
- // To know if the callbacks have already been called at least once
- fired: function() {
- return !!fired;
- }
- };
-
- return self;
-};
-jQuery.extend({
-
- Deferred: function( func ) {
- var tuples = [
- // action, add listener, listener list, final state
- [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
- [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
- [ "notify", "progress", jQuery.Callbacks("memory") ]
- ],
- state = "pending",
- promise = {
- state: function() {
- return state;
- },
- always: function() {
- deferred.done( arguments ).fail( arguments );
- return this;
- },
- then: function( /* fnDone, fnFail, fnProgress */ ) {
- var fns = arguments;
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( tuples, function( i, tuple ) {
- var action = tuple[ 0 ],
- fn = fns[ i ];
- // deferred[ done | fail | progress ] for forwarding actions to newDefer
- deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
- function() {
- var returned = fn.apply( this, arguments );
- if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise()
- .done( newDefer.resolve )
- .fail( newDefer.reject )
- .progress( newDefer.notify );
- } else {
- newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
- }
- } :
- newDefer[ action ]
- );
- });
- fns = null;
- }).promise();
- },
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- return obj != null ? jQuery.extend( obj, promise ) : promise;
- }
- },
- deferred = {};
-
- // Keep pipe for back-compat
- promise.pipe = promise.then;
-
- // Add list-specific methods
- jQuery.each( tuples, function( i, tuple ) {
- var list = tuple[ 2 ],
- stateString = tuple[ 3 ];
-
- // promise[ done | fail | progress ] = list.add
- promise[ tuple[1] ] = list.add;
-
- // Handle state
- if ( stateString ) {
- list.add(function() {
- // state = [ resolved | rejected ]
- state = stateString;
-
- // [ reject_list | resolve_list ].disable; progress_list.lock
- }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
- }
-
- // deferred[ resolve | reject | notify ] = list.fire
- deferred[ tuple[0] ] = list.fire;
- deferred[ tuple[0] + "With" ] = list.fireWith;
- });
-
- // Make the deferred a promise
- promise.promise( deferred );
-
- // Call given func if any
- if ( func ) {
- func.call( deferred, deferred );
- }
-
- // All done!
- return deferred;
- },
-
- // Deferred helper
- when: function( subordinate /* , ..., subordinateN */ ) {
- var i = 0,
- resolveValues = core_slice.call( arguments ),
- length = resolveValues.length,
-
- // the count of uncompleted subordinates
- remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
-
- // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
- deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
-
- // Update function for both resolve and progress values
- updateFunc = function( i, contexts, values ) {
- return function( value ) {
- contexts[ i ] = this;
- values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
- if( values === progressValues ) {
- deferred.notifyWith( contexts, values );
- } else if ( !( --remaining ) ) {
- deferred.resolveWith( contexts, values );
- }
- };
- },
-
- progressValues, progressContexts, resolveContexts;
-
- // add listeners to Deferred subordinates; treat others as resolved
- if ( length > 1 ) {
- progressValues = new Array( length );
- progressContexts = new Array( length );
- resolveContexts = new Array( length );
- for ( ; i < length; i++ ) {
- if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
- resolveValues[ i ].promise()
- .done( updateFunc( i, resolveContexts, resolveValues ) )
- .fail( deferred.reject )
- .progress( updateFunc( i, progressContexts, progressValues ) );
- } else {
- --remaining;
- }
- }
- }
-
- // if we're not waiting on anything, resolve the master
- if ( !remaining ) {
- deferred.resolveWith( resolveContexts, resolveValues );
- }
-
- return deferred.promise();
- }
-});
-jQuery.support = (function() {
-
- var support,
- all,
- a,
- select,
- opt,
- input,
- fragment,
- eventName,
- i,
- isSupported,
- clickFn,
- div = document.createElement("div");
-
- // Setup
- div.setAttribute( "className", "t" );
- div.innerHTML = "
a";
-
- // Support tests won't run in some limited or non-browser environments
- all = div.getElementsByTagName("*");
- a = div.getElementsByTagName("a")[ 0 ];
- if ( !all || !a || !all.length ) {
- return {};
- }
-
- // First batch of tests
- select = document.createElement("select");
- opt = select.appendChild( document.createElement("option") );
- input = div.getElementsByTagName("input")[ 0 ];
-
- a.style.cssText = "top:1px;float:left;opacity:.5";
- support = {
- // IE strips leading whitespace when .innerHTML is used
- leadingWhitespace: ( div.firstChild.nodeType === 3 ),
-
- // Make sure that tbody elements aren't automatically inserted
- // IE will insert them into empty tables
- tbody: !div.getElementsByTagName("tbody").length,
-
- // Make sure that link elements get serialized correctly by innerHTML
- // This requires a wrapper element in IE
- htmlSerialize: !!div.getElementsByTagName("link").length,
-
- // Get the style information from getAttribute
- // (IE uses .cssText instead)
- style: /top/.test( a.getAttribute("style") ),
-
- // Make sure that URLs aren't manipulated
- // (IE normalizes it by default)
- hrefNormalized: ( a.getAttribute("href") === "/a" ),
-
- // Make sure that element opacity exists
- // (IE uses filter instead)
- // Use a regex to work around a WebKit issue. See #5145
- opacity: /^0.5/.test( a.style.opacity ),
-
- // Verify style float existence
- // (IE uses styleFloat instead of cssFloat)
- cssFloat: !!a.style.cssFloat,
-
- // Make sure that if no value is specified for a checkbox
- // that it defaults to "on".
- // (WebKit defaults to "" instead)
- checkOn: ( input.value === "on" ),
-
- // Make sure that a selected-by-default option has a working selected property.
- // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
- optSelected: opt.selected,
-
- // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
- getSetAttribute: div.className !== "t",
-
- // Tests for enctype support on a form (#6743)
- enctype: !!document.createElement("form").enctype,
-
- // Makes sure cloning an html5 element does not cause problems
- // Where outerHTML is undefined, this still works
- html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>",
-
- // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
- boxModel: ( document.compatMode === "CSS1Compat" ),
-
- // Will be defined later
- submitBubbles: true,
- changeBubbles: true,
- focusinBubbles: false,
- deleteExpando: true,
- noCloneEvent: true,
- inlineBlockNeedsLayout: false,
- shrinkWrapBlocks: false,
- reliableMarginRight: true,
- boxSizingReliable: true,
- pixelPosition: false
- };
-
- // Make sure checked status is properly cloned
- input.checked = true;
- support.noCloneChecked = input.cloneNode( true ).checked;
-
- // Make sure that the options inside disabled selects aren't marked as disabled
- // (WebKit marks them as disabled)
- select.disabled = true;
- support.optDisabled = !opt.disabled;
-
- // Test to see if it's possible to delete an expando from an element
- // Fails in Internet Explorer
- try {
- delete div.test;
- } catch( e ) {
- support.deleteExpando = false;
- }
-
- if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
- div.attachEvent( "onclick", clickFn = function() {
- // Cloning a node shouldn't copy over any
- // bound event handlers (IE does this)
- support.noCloneEvent = false;
- });
- div.cloneNode( true ).fireEvent("onclick");
- div.detachEvent( "onclick", clickFn );
- }
-
- // Check if a radio maintains its value
- // after being appended to the DOM
- input = document.createElement("input");
- input.value = "t";
- input.setAttribute( "type", "radio" );
- support.radioValue = input.value === "t";
-
- input.setAttribute( "checked", "checked" );
-
- // #11217 - WebKit loses check when the name is after the checked attribute
- input.setAttribute( "name", "t" );
-
- div.appendChild( input );
- fragment = document.createDocumentFragment();
- fragment.appendChild( div.lastChild );
-
- // WebKit doesn't clone checked state correctly in fragments
- support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
- // Check if a disconnected checkbox will retain its checked
- // value of true after appended to the DOM (IE6/7)
- support.appendChecked = input.checked;
-
- fragment.removeChild( input );
- fragment.appendChild( div );
-
- // Technique from Juriy Zaytsev
- // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
- // We only care about the case where non-standard event systems
- // are used, namely in IE. Short-circuiting here helps us to
- // avoid an eval call (in setAttribute) which can cause CSP
- // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
- if ( div.attachEvent ) {
- for ( i in {
- submit: true,
- change: true,
- focusin: true
- }) {
- eventName = "on" + i;
- isSupported = ( eventName in div );
- if ( !isSupported ) {
- div.setAttribute( eventName, "return;" );
- isSupported = ( typeof div[ eventName ] === "function" );
- }
- support[ i + "Bubbles" ] = isSupported;
- }
- }
-
- // Run tests that need a body at doc ready
- jQuery(function() {
- var container, div, tds, marginDiv,
- divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
- body = document.getElementsByTagName("body")[0];
-
- if ( !body ) {
- // Return for frameset docs that don't have a body
- return;
- }
-
- container = document.createElement("div");
- container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
- body.insertBefore( container, body.firstChild );
-
- // Construct the test element
- div = document.createElement("div");
- container.appendChild( div );
-
- // Check if table cells still have offsetWidth/Height when they are set
- // to display:none and there are still other visible table cells in a
- // table row; if so, offsetWidth/Height are not reliable for use when
- // determining if an element has been hidden directly using
- // display:none (it is still safe to use offsets if a parent element is
- // hidden; don safety goggles and see bug #4512 for more information).
- // (only IE 8 fails this test)
- div.innerHTML = "
t
";
- tds = div.getElementsByTagName("td");
- tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
- isSupported = ( tds[ 0 ].offsetHeight === 0 );
-
- tds[ 0 ].style.display = "";
- tds[ 1 ].style.display = "none";
-
- // Check if empty table cells still have offsetWidth/Height
- // (IE <= 8 fail this test)
- support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
-
- // Check box-sizing and margin behavior
- div.innerHTML = "";
- div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
- support.boxSizing = ( div.offsetWidth === 4 );
- support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
-
- // NOTE: To any future maintainer, we've window.getComputedStyle
- // because jsdom on node.js will break without it.
- if ( window.getComputedStyle ) {
- support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
- support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
-
- // Check if div with explicit width and no margin-right incorrectly
- // gets computed margin-right based on width of container. For more
- // info see bug #3333
- // Fails in WebKit before Feb 2011 nightlies
- // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- marginDiv = document.createElement("div");
- marginDiv.style.cssText = div.style.cssText = divReset;
- marginDiv.style.marginRight = marginDiv.style.width = "0";
- div.style.width = "1px";
- div.appendChild( marginDiv );
- support.reliableMarginRight =
- !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
- }
-
- if ( typeof div.style.zoom !== "undefined" ) {
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- // (IE < 8 does this)
- div.innerHTML = "";
- div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
- support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
-
- // Check if elements with layout shrink-wrap their children
- // (IE 6 does this)
- div.style.display = "block";
- div.style.overflow = "visible";
- div.innerHTML = "";
- div.firstChild.style.width = "5px";
- support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
-
- container.style.zoom = 1;
- }
-
- // Null elements to avoid leaks in IE
- body.removeChild( container );
- container = div = tds = marginDiv = null;
- });
-
- // Null elements to avoid leaks in IE
- fragment.removeChild( div );
- all = a = select = opt = input = fragment = div = null;
-
- return support;
-})();
-var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
- rmultiDash = /([A-Z])/g;
-
-jQuery.extend({
- cache: {},
-
- deletedIds: [],
-
- // Remove at next major release (1.9/2.0)
- uuid: 0,
-
- // Unique for each copy of jQuery on the page
- // Non-digits removed to match rinlinejQuery
- expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
-
- // The following elements throw uncatchable exceptions if you
- // attempt to add expando properties to them.
- noData: {
- "embed": true,
- // Ban all objects except for Flash (which handle expandos)
- "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
- "applet": true
- },
-
- hasData: function( elem ) {
- elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
- return !!elem && !isEmptyDataObject( elem );
- },
-
- data: function( elem, name, data, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var thisCache, ret,
- internalKey = jQuery.expando,
- getByName = typeof name === "string",
-
- // We have to handle DOM nodes and JS objects differently because IE6-7
- // can't GC object references properly across the DOM-JS boundary
- isNode = elem.nodeType,
-
- // Only DOM nodes need the global jQuery cache; JS object data is
- // attached directly to the object so GC can occur automatically
- cache = isNode ? jQuery.cache : elem,
-
- // Only defining an ID for JS objects if its cache already exists allows
- // the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
-
- // Avoid doing any more work than we need to when trying to get data on an
- // object that has no data at all
- if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
- return;
- }
-
- if ( !id ) {
- // Only DOM nodes need a new unique ID for each element since their data
- // ends up in the global cache
- if ( isNode ) {
- elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;
- } else {
- id = internalKey;
- }
- }
-
- if ( !cache[ id ] ) {
- cache[ id ] = {};
-
- // Avoids exposing jQuery metadata on plain JS objects when the object
- // is serialized using JSON.stringify
- if ( !isNode ) {
- cache[ id ].toJSON = jQuery.noop;
- }
- }
-
- // An object can be passed to jQuery.data instead of a key/value pair; this gets
- // shallow copied over onto the existing cache
- if ( typeof name === "object" || typeof name === "function" ) {
- if ( pvt ) {
- cache[ id ] = jQuery.extend( cache[ id ], name );
- } else {
- cache[ id ].data = jQuery.extend( cache[ id ].data, name );
- }
- }
-
- thisCache = cache[ id ];
-
- // jQuery data() is stored in a separate object inside the object's internal data
- // cache in order to avoid key collisions between internal data and user-defined
- // data.
- if ( !pvt ) {
- if ( !thisCache.data ) {
- thisCache.data = {};
- }
-
- thisCache = thisCache.data;
- }
-
- if ( data !== undefined ) {
- thisCache[ jQuery.camelCase( name ) ] = data;
- }
-
- // Check for both converted-to-camel and non-converted data property names
- // If a data property was specified
- if ( getByName ) {
-
- // First Try to find as-is property data
- ret = thisCache[ name ];
-
- // Test for null|undefined property data
- if ( ret == null ) {
-
- // Try to find the camelCased property
- ret = thisCache[ jQuery.camelCase( name ) ];
- }
- } else {
- ret = thisCache;
- }
-
- return ret;
- },
-
- removeData: function( elem, name, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var thisCache, i, l,
-
- isNode = elem.nodeType,
-
- // See jQuery.data for more information
- cache = isNode ? jQuery.cache : elem,
- id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
-
- // If there is already no cache entry for this object, there is no
- // purpose in continuing
- if ( !cache[ id ] ) {
- return;
- }
-
- if ( name ) {
-
- thisCache = pvt ? cache[ id ] : cache[ id ].data;
-
- if ( thisCache ) {
-
- // Support array or space separated string names for data keys
- if ( !jQuery.isArray( name ) ) {
-
- // try the string as a key before any manipulation
- if ( name in thisCache ) {
- name = [ name ];
- } else {
-
- // split the camel cased version by spaces unless a key with the spaces exists
- name = jQuery.camelCase( name );
- if ( name in thisCache ) {
- name = [ name ];
- } else {
- name = name.split(" ");
- }
- }
- }
-
- for ( i = 0, l = name.length; i < l; i++ ) {
- delete thisCache[ name[i] ];
- }
-
- // If there is no data left in the cache, we want to continue
- // and let the cache object itself get destroyed
- if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
- return;
- }
- }
- }
-
- // See jQuery.data for more information
- if ( !pvt ) {
- delete cache[ id ].data;
-
- // Don't destroy the parent cache unless the internal data object
- // had been the only thing left in it
- if ( !isEmptyDataObject( cache[ id ] ) ) {
- return;
- }
- }
-
- // Destroy the cache
- if ( isNode ) {
- jQuery.cleanData( [ elem ], true );
-
- // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
- } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
- delete cache[ id ];
-
- // When all else fails, null
- } else {
- cache[ id ] = null;
- }
- },
-
- // For internal use only.
- _data: function( elem, name, data ) {
- return jQuery.data( elem, name, data, true );
- },
-
- // A method for determining if a DOM node can handle the data expando
- acceptData: function( elem ) {
- var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
-
- // nodes accept data unless otherwise specified; rejection can be conditional
- return !noData || noData !== true && elem.getAttribute("classid") === noData;
- }
-});
-
-jQuery.fn.extend({
- data: function( key, value ) {
- var parts, part, attr, name, l,
- elem = this[0],
- i = 0,
- data = null;
-
- // Gets all values
- if ( key === undefined ) {
- if ( this.length ) {
- data = jQuery.data( elem );
-
- if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
- attr = elem.attributes;
- for ( l = attr.length; i < l; i++ ) {
- name = attr[i].name;
-
- if ( !name.indexOf( "data-" ) ) {
- name = jQuery.camelCase( name.substring(5) );
-
- dataAttr( elem, name, data[ name ] );
- }
- }
- jQuery._data( elem, "parsedAttrs", true );
- }
- }
-
- return data;
- }
-
- // Sets multiple values
- if ( typeof key === "object" ) {
- return this.each(function() {
- jQuery.data( this, key );
- });
- }
-
- parts = key.split( ".", 2 );
- parts[1] = parts[1] ? "." + parts[1] : "";
- part = parts[1] + "!";
-
- return jQuery.access( this, function( value ) {
-
- if ( value === undefined ) {
- data = this.triggerHandler( "getData" + part, [ parts[0] ] );
-
- // Try to fetch any internally stored data first
- if ( data === undefined && elem ) {
- data = jQuery.data( elem, key );
- data = dataAttr( elem, key, data );
- }
-
- return data === undefined && parts[1] ?
- this.data( parts[0] ) :
- data;
- }
-
- parts[1] = value;
- this.each(function() {
- var self = jQuery( this );
-
- self.triggerHandler( "setData" + part, parts );
- jQuery.data( this, key, value );
- self.triggerHandler( "changeData" + part, parts );
- });
- }, null, value, arguments.length > 1, null, false );
- },
-
- removeData: function( key ) {
- return this.each(function() {
- jQuery.removeData( this, key );
- });
- }
-});
-
-function dataAttr( elem, key, data ) {
- // If nothing was found internally, try to fetch any
- // data from the HTML5 data-* attribute
- if ( data === undefined && elem.nodeType === 1 ) {
-
- var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
-
- data = elem.getAttribute( name );
-
- if ( typeof data === "string" ) {
- try {
- data = data === "true" ? true :
- data === "false" ? false :
- data === "null" ? null :
- // Only convert to a number if it doesn't change the string
- +data + "" === data ? +data :
- rbrace.test( data ) ? jQuery.parseJSON( data ) :
- data;
- } catch( e ) {}
-
- // Make sure we set the data so it isn't changed later
- jQuery.data( elem, key, data );
-
- } else {
- data = undefined;
- }
- }
-
- return data;
-}
-
-// checks a cache object for emptiness
-function isEmptyDataObject( obj ) {
- var name;
- for ( name in obj ) {
-
- // if the public data object is empty, the private is still empty
- if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
- continue;
- }
- if ( name !== "toJSON" ) {
- return false;
- }
- }
-
- return true;
-}
-jQuery.extend({
- queue: function( elem, type, data ) {
- var queue;
-
- if ( elem ) {
- type = ( type || "fx" ) + "queue";
- queue = jQuery._data( elem, type );
-
- // Speed up dequeue by getting out quickly if this is just a lookup
- if ( data ) {
- if ( !queue || jQuery.isArray(data) ) {
- queue = jQuery._data( elem, type, jQuery.makeArray(data) );
- } else {
- queue.push( data );
- }
- }
- return queue || [];
- }
- },
-
- dequeue: function( elem, type ) {
- type = type || "fx";
-
- var queue = jQuery.queue( elem, type ),
- startLength = queue.length,
- fn = queue.shift(),
- hooks = jQuery._queueHooks( elem, type ),
- next = function() {
- jQuery.dequeue( elem, type );
- };
-
- // If the fx queue is dequeued, always remove the progress sentinel
- if ( fn === "inprogress" ) {
- fn = queue.shift();
- startLength--;
- }
-
- if ( fn ) {
-
- // Add a progress sentinel to prevent the fx queue from being
- // automatically dequeued
- if ( type === "fx" ) {
- queue.unshift( "inprogress" );
- }
-
- // clear up the last queue stop function
- delete hooks.stop;
- fn.call( elem, next, hooks );
- }
-
- if ( !startLength && hooks ) {
- hooks.empty.fire();
- }
- },
-
- // not intended for public consumption - generates a queueHooks object, or returns the current one
- _queueHooks: function( elem, type ) {
- var key = type + "queueHooks";
- return jQuery._data( elem, key ) || jQuery._data( elem, key, {
- empty: jQuery.Callbacks("once memory").add(function() {
- jQuery.removeData( elem, type + "queue", true );
- jQuery.removeData( elem, key, true );
- })
- });
- }
-});
-
-jQuery.fn.extend({
- queue: function( type, data ) {
- var setter = 2;
-
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- setter--;
- }
-
- if ( arguments.length < setter ) {
- return jQuery.queue( this[0], type );
- }
-
- return data === undefined ?
- this :
- this.each(function() {
- var queue = jQuery.queue( this, type, data );
-
- // ensure a hooks for this queue
- jQuery._queueHooks( this, type );
-
- if ( type === "fx" && queue[0] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- });
- },
- dequeue: function( type ) {
- return this.each(function() {
- jQuery.dequeue( this, type );
- });
- },
- // Based off of the plugin by Clint Helfers, with permission.
- // http://blindsignals.com/index.php/2009/07/jquery-delay/
- delay: function( time, type ) {
- time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
- type = type || "fx";
-
- return this.queue( type, function( next, hooks ) {
- var timeout = setTimeout( next, time );
- hooks.stop = function() {
- clearTimeout( timeout );
- };
- });
- },
- clearQueue: function( type ) {
- return this.queue( type || "fx", [] );
- },
- // Get a promise resolved when queues of a certain type
- // are emptied (fx is the type by default)
- promise: function( type, obj ) {
- var tmp,
- count = 1,
- defer = jQuery.Deferred(),
- elements = this,
- i = this.length,
- resolve = function() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- };
-
- if ( typeof type !== "string" ) {
- obj = type;
- type = undefined;
- }
- type = type || "fx";
-
- while( i-- ) {
- tmp = jQuery._data( elements[ i ], type + "queueHooks" );
- if ( tmp && tmp.empty ) {
- count++;
- tmp.empty.add( resolve );
- }
- }
- resolve();
- return defer.promise( obj );
- }
-});
-var nodeHook, boolHook, fixSpecified,
- rclass = /[\t\r\n]/g,
- rreturn = /\r/g,
- rtype = /^(?:button|input)$/i,
- rfocusable = /^(?:button|input|object|select|textarea)$/i,
- rclickable = /^a(?:rea|)$/i,
- rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
- getSetAttribute = jQuery.support.getSetAttribute;
-
-jQuery.fn.extend({
- attr: function( name, value ) {
- return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
- },
-
- removeAttr: function( name ) {
- return this.each(function() {
- jQuery.removeAttr( this, name );
- });
- },
-
- prop: function( name, value ) {
- return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
- },
-
- removeProp: function( name ) {
- name = jQuery.propFix[ name ] || name;
- return this.each(function() {
- // try/catch handles cases where IE balks (such as removing a property on window)
- try {
- this[ name ] = undefined;
- delete this[ name ];
- } catch( e ) {}
- });
- },
-
- addClass: function( value ) {
- var classNames, i, l, elem,
- setClass, c, cl;
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).addClass( value.call(this, j, this.className) );
- });
- }
-
- if ( value && typeof value === "string" ) {
- classNames = value.split( core_rspace );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- elem = this[ i ];
-
- if ( elem.nodeType === 1 ) {
- if ( !elem.className && classNames.length === 1 ) {
- elem.className = value;
-
- } else {
- setClass = " " + elem.className + " ";
-
- for ( c = 0, cl = classNames.length; c < cl; c++ ) {
- if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) {
- setClass += classNames[ c ] + " ";
- }
- }
- elem.className = jQuery.trim( setClass );
- }
- }
- }
- }
-
- return this;
- },
-
- removeClass: function( value ) {
- var removes, className, elem, c, cl, i, l;
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( j ) {
- jQuery( this ).removeClass( value.call(this, j, this.className) );
- });
- }
- if ( (value && typeof value === "string") || value === undefined ) {
- removes = ( value || "" ).split( core_rspace );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- elem = this[ i ];
- if ( elem.nodeType === 1 && elem.className ) {
-
- className = (" " + elem.className + " ").replace( rclass, " " );
-
- // loop over each item in the removal list
- for ( c = 0, cl = removes.length; c < cl; c++ ) {
- // Remove until there is nothing to remove,
- while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) {
- className = className.replace( " " + removes[ c ] + " " , " " );
- }
- }
- elem.className = value ? jQuery.trim( className ) : "";
- }
- }
- }
-
- return this;
- },
-
- toggleClass: function( value, stateVal ) {
- var type = typeof value,
- isBool = typeof stateVal === "boolean";
-
- if ( jQuery.isFunction( value ) ) {
- return this.each(function( i ) {
- jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
- });
- }
-
- return this.each(function() {
- if ( type === "string" ) {
- // toggle individual class names
- var className,
- i = 0,
- self = jQuery( this ),
- state = stateVal,
- classNames = value.split( core_rspace );
-
- while ( (className = classNames[ i++ ]) ) {
- // check each className given, space separated list
- state = isBool ? state : !self.hasClass( className );
- self[ state ? "addClass" : "removeClass" ]( className );
- }
-
- } else if ( type === "undefined" || type === "boolean" ) {
- if ( this.className ) {
- // store className if set
- jQuery._data( this, "__className__", this.className );
- }
-
- // toggle whole className
- this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
- }
- });
- },
-
- hasClass: function( selector ) {
- var className = " " + selector + " ",
- i = 0,
- l = this.length;
- for ( ; i < l; i++ ) {
- if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
- return true;
- }
- }
-
- return false;
- },
-
- val: function( value ) {
- var hooks, ret, isFunction,
- elem = this[0];
-
- if ( !arguments.length ) {
- if ( elem ) {
- hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
-
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
- return ret;
- }
-
- ret = elem.value;
-
- return typeof ret === "string" ?
- // handle most common string cases
- ret.replace(rreturn, "") :
- // handle cases where value is null/undef or number
- ret == null ? "" : ret;
- }
-
- return;
- }
-
- isFunction = jQuery.isFunction( value );
-
- return this.each(function( i ) {
- var val,
- self = jQuery(this);
-
- if ( this.nodeType !== 1 ) {
- return;
- }
-
- if ( isFunction ) {
- val = value.call( this, i, self.val() );
- } else {
- val = value;
- }
-
- // Treat null/undefined as ""; convert numbers to string
- if ( val == null ) {
- val = "";
- } else if ( typeof val === "number" ) {
- val += "";
- } else if ( jQuery.isArray( val ) ) {
- val = jQuery.map(val, function ( value ) {
- return value == null ? "" : value + "";
- });
- }
-
- hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
-
- // If set returns undefined, fall back to normal setting
- if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
- this.value = val;
- }
- });
- }
-});
-
-jQuery.extend({
- valHooks: {
- option: {
- get: function( elem ) {
- // attributes.value is undefined in Blackberry 4.7 but
- // uses .value. See #6932
- var val = elem.attributes.value;
- return !val || val.specified ? elem.value : elem.text;
- }
- },
- select: {
- get: function( elem ) {
- var value, option,
- options = elem.options,
- index = elem.selectedIndex,
- one = elem.type === "select-one" || index < 0,
- values = one ? null : [],
- max = one ? index + 1 : options.length,
- i = index < 0 ?
- max :
- one ? index : 0;
-
- // Loop through all the selected options
- for ( ; i < max; i++ ) {
- option = options[ i ];
-
- // oldIE doesn't update selected after form reset (#2551)
- if ( ( option.selected || i === index ) &&
- // Don't return options that are disabled or in a disabled optgroup
- ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
- ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
-
- // Get the specific value for the option
- value = jQuery( option ).val();
-
- // We don't need an array for one selects
- if ( one ) {
- return value;
- }
-
- // Multi-Selects return an array
- values.push( value );
- }
- }
-
- return values;
- },
-
- set: function( elem, value ) {
- var values = jQuery.makeArray( value );
-
- jQuery(elem).find("option").each(function() {
- this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
- });
-
- if ( !values.length ) {
- elem.selectedIndex = -1;
- }
- return values;
- }
- }
- },
-
- // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
- attrFn: {},
-
- attr: function( elem, name, value, pass ) {
- var ret, hooks, notxml,
- nType = elem.nodeType;
-
- // don't get/set attributes on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return;
- }
-
- if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
- return jQuery( elem )[ name ]( value );
- }
-
- // Fallback to prop when attributes are not supported
- if ( typeof elem.getAttribute === "undefined" ) {
- return jQuery.prop( elem, name, value );
- }
-
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- // All attributes are lowercase
- // Grab necessary hook if one is defined
- if ( notxml ) {
- name = name.toLowerCase();
- hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
- }
-
- if ( value !== undefined ) {
-
- if ( value === null ) {
- jQuery.removeAttr( elem, name );
- return;
-
- } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- elem.setAttribute( name, value + "" );
- return value;
- }
-
- } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
-
- } else {
-
- ret = elem.getAttribute( name );
-
- // Non-existent attributes return null, we normalize to undefined
- return ret === null ?
- undefined :
- ret;
- }
- },
-
- removeAttr: function( elem, value ) {
- var propName, attrNames, name, isBool,
- i = 0;
-
- if ( value && elem.nodeType === 1 ) {
-
- attrNames = value.split( core_rspace );
-
- for ( ; i < attrNames.length; i++ ) {
- name = attrNames[ i ];
-
- if ( name ) {
- propName = jQuery.propFix[ name ] || name;
- isBool = rboolean.test( name );
-
- // See #9699 for explanation of this approach (setting first, then removal)
- // Do not do this for boolean attributes (see #10870)
- if ( !isBool ) {
- jQuery.attr( elem, name, "" );
- }
- elem.removeAttribute( getSetAttribute ? name : propName );
-
- // Set corresponding property to false for boolean attributes
- if ( isBool && propName in elem ) {
- elem[ propName ] = false;
- }
- }
- }
- }
- },
-
- attrHooks: {
- type: {
- set: function( elem, value ) {
- // We can't allow the type property to be changed (since it causes problems in IE)
- if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
- jQuery.error( "type property can't be changed" );
- } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
- // Setting the type on a radio button after the value resets the value in IE6-9
- // Reset value to it's default in case type is set after value
- // This is for element creation
- var val = elem.value;
- elem.setAttribute( "type", value );
- if ( val ) {
- elem.value = val;
- }
- return value;
- }
- }
- },
- // Use the value property for back compat
- // Use the nodeHook for button elements in IE6/7 (#1954)
- value: {
- get: function( elem, name ) {
- if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
- return nodeHook.get( elem, name );
- }
- return name in elem ?
- elem.value :
- null;
- },
- set: function( elem, value, name ) {
- if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
- return nodeHook.set( elem, value, name );
- }
- // Does not return so that setAttribute is also used
- elem.value = value;
- }
- }
- },
-
- propFix: {
- tabindex: "tabIndex",
- readonly: "readOnly",
- "for": "htmlFor",
- "class": "className",
- maxlength: "maxLength",
- cellspacing: "cellSpacing",
- cellpadding: "cellPadding",
- rowspan: "rowSpan",
- colspan: "colSpan",
- usemap: "useMap",
- frameborder: "frameBorder",
- contenteditable: "contentEditable"
- },
-
- prop: function( elem, name, value ) {
- var ret, hooks, notxml,
- nType = elem.nodeType;
-
- // don't get/set properties on text, comment and attribute nodes
- if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
- return;
- }
-
- notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
-
- if ( notxml ) {
- // Fix name and attach hooks
- name = jQuery.propFix[ name ] || name;
- hooks = jQuery.propHooks[ name ];
- }
-
- if ( value !== undefined ) {
- if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
- return ret;
-
- } else {
- return ( elem[ name ] = value );
- }
-
- } else {
- if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
- return ret;
-
- } else {
- return elem[ name ];
- }
- }
- },
-
- propHooks: {
- tabIndex: {
- get: function( elem ) {
- // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
- // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
- var attributeNode = elem.getAttributeNode("tabindex");
-
- return attributeNode && attributeNode.specified ?
- parseInt( attributeNode.value, 10 ) :
- rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
- 0 :
- undefined;
- }
- }
- }
-});
-
-// Hook for boolean attributes
-boolHook = {
- get: function( elem, name ) {
- // Align boolean attributes with corresponding properties
- // Fall back to attribute presence where some booleans are not supported
- var attrNode,
- property = jQuery.prop( elem, name );
- return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
- name.toLowerCase() :
- undefined;
- },
- set: function( elem, value, name ) {
- var propName;
- if ( value === false ) {
- // Remove boolean attributes when set to false
- jQuery.removeAttr( elem, name );
- } else {
- // value is true since we know at this point it's type boolean and not false
- // Set boolean attributes to the same name and set the DOM property
- propName = jQuery.propFix[ name ] || name;
- if ( propName in elem ) {
- // Only set the IDL specifically if it already exists on the element
- elem[ propName ] = true;
- }
-
- elem.setAttribute( name, name.toLowerCase() );
- }
- return name;
- }
-};
-
-// IE6/7 do not support getting/setting some attributes with get/setAttribute
-if ( !getSetAttribute ) {
-
- fixSpecified = {
- name: true,
- id: true,
- coords: true
- };
-
- // Use this for any attribute in IE6/7
- // This fixes almost every IE6/7 issue
- nodeHook = jQuery.valHooks.button = {
- get: function( elem, name ) {
- var ret;
- ret = elem.getAttributeNode( name );
- return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
- ret.value :
- undefined;
- },
- set: function( elem, value, name ) {
- // Set the existing or create a new attribute node
- var ret = elem.getAttributeNode( name );
- if ( !ret ) {
- ret = document.createAttribute( name );
- elem.setAttributeNode( ret );
- }
- return ( ret.value = value + "" );
- }
- };
-
- // Set width and height to auto instead of 0 on empty string( Bug #8150 )
- // This is for removals
- jQuery.each([ "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
- set: function( elem, value ) {
- if ( value === "" ) {
- elem.setAttribute( name, "auto" );
- return value;
- }
- }
- });
- });
-
- // Set contenteditable to false on removals(#10429)
- // Setting to empty string throws an error as an invalid value
- jQuery.attrHooks.contenteditable = {
- get: nodeHook.get,
- set: function( elem, value, name ) {
- if ( value === "" ) {
- value = "false";
- }
- nodeHook.set( elem, value, name );
- }
- };
-}
-
-
-// Some attributes require a special call on IE
-if ( !jQuery.support.hrefNormalized ) {
- jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
- jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
- get: function( elem ) {
- var ret = elem.getAttribute( name, 2 );
- return ret === null ? undefined : ret;
- }
- });
- });
-}
-
-if ( !jQuery.support.style ) {
- jQuery.attrHooks.style = {
- get: function( elem ) {
- // Return undefined in the case of empty string
- // Normalize to lowercase since IE uppercases css property names
- return elem.style.cssText.toLowerCase() || undefined;
- },
- set: function( elem, value ) {
- return ( elem.style.cssText = value + "" );
- }
- };
-}
-
-// Safari mis-reports the default selected property of an option
-// Accessing the parent's selectedIndex property fixes it
-if ( !jQuery.support.optSelected ) {
- jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
- get: function( elem ) {
- var parent = elem.parentNode;
-
- if ( parent ) {
- parent.selectedIndex;
-
- // Make sure that it also works with optgroups, see #5701
- if ( parent.parentNode ) {
- parent.parentNode.selectedIndex;
- }
- }
- return null;
- }
- });
-}
-
-// IE6/7 call enctype encoding
-if ( !jQuery.support.enctype ) {
- jQuery.propFix.enctype = "encoding";
-}
-
-// Radios and checkboxes getter/setter
-if ( !jQuery.support.checkOn ) {
- jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = {
- get: function( elem ) {
- // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
- return elem.getAttribute("value") === null ? "on" : elem.value;
- }
- };
- });
-}
-jQuery.each([ "radio", "checkbox" ], function() {
- jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
- set: function( elem, value ) {
- if ( jQuery.isArray( value ) ) {
- return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
- }
- }
- });
-});
-var rformElems = /^(?:textarea|input|select)$/i,
- rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
- rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
- rkeyEvent = /^key/,
- rmouseEvent = /^(?:mouse|contextmenu)|click/,
- rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
- hoverHack = function( events ) {
- return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
- };
-
-/*
- * Helper functions for managing events -- not part of the public interface.
- * Props to Dean Edwards' addEvent library for many of the ideas.
- */
-jQuery.event = {
-
- add: function( elem, types, handler, data, selector ) {
-
- var elemData, eventHandle, events,
- t, tns, type, namespaces, handleObj,
- handleObjIn, handlers, special;
-
- // Don't attach events to noData or text/comment nodes (allow plain objects tho)
- if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
- return;
- }
-
- // Caller can pass in an object of custom data in lieu of the handler
- if ( handler.handler ) {
- handleObjIn = handler;
- handler = handleObjIn.handler;
- selector = handleObjIn.selector;
- }
-
- // Make sure that the handler has a unique ID, used to find/remove it later
- if ( !handler.guid ) {
- handler.guid = jQuery.guid++;
- }
-
- // Init the element's event structure and main handler, if this is the first
- events = elemData.events;
- if ( !events ) {
- elemData.events = events = {};
- }
- eventHandle = elemData.handle;
- if ( !eventHandle ) {
- elemData.handle = eventHandle = function( e ) {
- // Discard the second event of a jQuery.event.trigger() and
- // when an event is called after a page has unloaded
- return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
- undefined;
- };
- // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
- eventHandle.elem = elem;
- }
-
- // Handle multiple events separated by a space
- // jQuery(...).bind("mouseover mouseout", fn);
- types = jQuery.trim( hoverHack(types) ).split( " " );
- for ( t = 0; t < types.length; t++ ) {
-
- tns = rtypenamespace.exec( types[t] ) || [];
- type = tns[1];
- namespaces = ( tns[2] || "" ).split( "." ).sort();
-
- // If event changes its type, use the special event handlers for the changed type
- special = jQuery.event.special[ type ] || {};
-
- // If selector defined, determine special event api type, otherwise given type
- type = ( selector ? special.delegateType : special.bindType ) || type;
-
- // Update special based on newly reset type
- special = jQuery.event.special[ type ] || {};
-
- // handleObj is passed to all event handlers
- handleObj = jQuery.extend({
- type: type,
- origType: tns[1],
- data: data,
- handler: handler,
- guid: handler.guid,
- selector: selector,
- needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
- namespace: namespaces.join(".")
- }, handleObjIn );
-
- // Init the event handler queue if we're the first
- handlers = events[ type ];
- if ( !handlers ) {
- handlers = events[ type ] = [];
- handlers.delegateCount = 0;
-
- // Only use addEventListener/attachEvent if the special events handler returns false
- if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
- // Bind the global event handler to the element
- if ( elem.addEventListener ) {
- elem.addEventListener( type, eventHandle, false );
-
- } else if ( elem.attachEvent ) {
- elem.attachEvent( "on" + type, eventHandle );
- }
- }
- }
-
- if ( special.add ) {
- special.add.call( elem, handleObj );
-
- if ( !handleObj.handler.guid ) {
- handleObj.handler.guid = handler.guid;
- }
- }
-
- // Add to the element's handler list, delegates in front
- if ( selector ) {
- handlers.splice( handlers.delegateCount++, 0, handleObj );
- } else {
- handlers.push( handleObj );
- }
-
- // Keep track of which events have ever been used, for event optimization
- jQuery.event.global[ type ] = true;
- }
-
- // Nullify elem to prevent memory leaks in IE
- elem = null;
- },
-
- global: {},
-
- // Detach an event or set of events from an element
- remove: function( elem, types, handler, selector, mappedTypes ) {
-
- var t, tns, type, origType, namespaces, origCount,
- j, events, special, eventType, handleObj,
- elemData = jQuery.hasData( elem ) && jQuery._data( elem );
-
- if ( !elemData || !(events = elemData.events) ) {
- return;
- }
-
- // Once for each type.namespace in types; type may be omitted
- types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
- for ( t = 0; t < types.length; t++ ) {
- tns = rtypenamespace.exec( types[t] ) || [];
- type = origType = tns[1];
- namespaces = tns[2];
-
- // Unbind all events (on this namespace, if provided) for the element
- if ( !type ) {
- for ( type in events ) {
- jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
- }
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
- type = ( selector? special.delegateType : special.bindType ) || type;
- eventType = events[ type ] || [];
- origCount = eventType.length;
- namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
-
- // Remove matching events
- for ( j = 0; j < eventType.length; j++ ) {
- handleObj = eventType[ j ];
-
- if ( ( mappedTypes || origType === handleObj.origType ) &&
- ( !handler || handler.guid === handleObj.guid ) &&
- ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
- ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
- eventType.splice( j--, 1 );
-
- if ( handleObj.selector ) {
- eventType.delegateCount--;
- }
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
- }
- }
-
- // Remove generic event handler if we removed something and no more handlers exist
- // (avoids potential for endless recursion during removal of special event handlers)
- if ( eventType.length === 0 && origCount !== eventType.length ) {
- if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
- jQuery.removeEvent( elem, type, elemData.handle );
- }
-
- delete events[ type ];
- }
- }
-
- // Remove the expando if it's no longer used
- if ( jQuery.isEmptyObject( events ) ) {
- delete elemData.handle;
-
- // removeData also checks for emptiness and clears the expando if empty
- // so use it instead of delete
- jQuery.removeData( elem, "events", true );
- }
- },
-
- // Events that are safe to short-circuit if no handlers are attached.
- // Native DOM events should not be added, they may have inline handlers.
- customEvent: {
- "getData": true,
- "setData": true,
- "changeData": true
- },
-
- trigger: function( event, data, elem, onlyHandlers ) {
- // Don't do events on text and comment nodes
- if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
- return;
- }
-
- // Event object or event type
- var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
- type = event.type || event,
- namespaces = [];
-
- // focus/blur morphs to focusin/out; ensure we're not firing them right now
- if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
- return;
- }
-
- if ( type.indexOf( "!" ) >= 0 ) {
- // Exclusive events trigger only for the exact event (no namespaces)
- type = type.slice(0, -1);
- exclusive = true;
- }
-
- if ( type.indexOf( "." ) >= 0 ) {
- // Namespaced trigger; create a regexp to match event type in handle()
- namespaces = type.split(".");
- type = namespaces.shift();
- namespaces.sort();
- }
-
- if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
- // No jQuery handlers for this event type, and it can't have inline handlers
- return;
- }
-
- // Caller can pass in an Event, Object, or just an event type string
- event = typeof event === "object" ?
- // jQuery.Event object
- event[ jQuery.expando ] ? event :
- // Object literal
- new jQuery.Event( type, event ) :
- // Just the event type (string)
- new jQuery.Event( type );
-
- event.type = type;
- event.isTrigger = true;
- event.exclusive = exclusive;
- event.namespace = namespaces.join( "." );
- event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
- ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
-
- // Handle a global trigger
- if ( !elem ) {
-
- // TODO: Stop taunting the data cache; remove global events and always attach to document
- cache = jQuery.cache;
- for ( i in cache ) {
- if ( cache[ i ].events && cache[ i ].events[ type ] ) {
- jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
- }
- }
- return;
- }
-
- // Clean up the event in case it is being reused
- event.result = undefined;
- if ( !event.target ) {
- event.target = elem;
- }
-
- // Clone any incoming data and prepend the event, creating the handler arg list
- data = data != null ? jQuery.makeArray( data ) : [];
- data.unshift( event );
-
- // Allow special events to draw outside the lines
- special = jQuery.event.special[ type ] || {};
- if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
- return;
- }
-
- // Determine event propagation path in advance, per W3C events spec (#9951)
- // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
- eventPath = [[ elem, special.bindType || type ]];
- if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
-
- bubbleType = special.delegateType || type;
- cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
- for ( old = elem; cur; cur = cur.parentNode ) {
- eventPath.push([ cur, bubbleType ]);
- old = cur;
- }
-
- // Only add window if we got to document (e.g., not plain obj or detached DOM)
- if ( old === (elem.ownerDocument || document) ) {
- eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
- }
- }
-
- // Fire handlers on the event path
- for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
-
- cur = eventPath[i][0];
- event.type = eventPath[i][1];
-
- handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
- if ( handle ) {
- handle.apply( cur, data );
- }
- // Note that this is a bare JS function and not a jQuery handler
- handle = ontype && cur[ ontype ];
- if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
- event.preventDefault();
- }
- }
- event.type = type;
-
- // If nobody prevented the default action, do it now
- if ( !onlyHandlers && !event.isDefaultPrevented() ) {
-
- if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
- !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
-
- // Call a native DOM method on the target with the same name name as the event.
- // Can't use an .isFunction() check here because IE6/7 fails that test.
- // Don't do default actions on window, that's where global variables be (#6170)
- // IE<9 dies on focus/blur to hidden element (#1486)
- if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
-
- // Don't re-trigger an onFOO event when we call its FOO() method
- old = elem[ ontype ];
-
- if ( old ) {
- elem[ ontype ] = null;
- }
-
- // Prevent re-triggering of the same event, since we already bubbled it above
- jQuery.event.triggered = type;
- elem[ type ]();
- jQuery.event.triggered = undefined;
-
- if ( old ) {
- elem[ ontype ] = old;
- }
- }
- }
- }
-
- return event.result;
- },
-
- dispatch: function( event ) {
-
- // Make a writable jQuery.Event from the native event object
- event = jQuery.event.fix( event || window.event );
-
- var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,
- handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
- delegateCount = handlers.delegateCount,
- args = core_slice.call( arguments ),
- run_all = !event.exclusive && !event.namespace,
- special = jQuery.event.special[ event.type ] || {},
- handlerQueue = [];
-
- // Use the fix-ed jQuery.Event rather than the (read-only) native event
- args[0] = event;
- event.delegateTarget = this;
-
- // Call the preDispatch hook for the mapped type, and let it bail if desired
- if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
- return;
- }
-
- // Determine handlers that should run if there are delegated events
- // Avoid non-left-click bubbling in Firefox (#3861)
- if ( delegateCount && !(event.button && event.type === "click") ) {
-
- for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
-
- // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
- if ( cur.disabled !== true || event.type !== "click" ) {
- selMatch = {};
- matches = [];
- for ( i = 0; i < delegateCount; i++ ) {
- handleObj = handlers[ i ];
- sel = handleObj.selector;
-
- if ( selMatch[ sel ] === undefined ) {
- selMatch[ sel ] = handleObj.needsContext ?
- jQuery( sel, this ).index( cur ) >= 0 :
- jQuery.find( sel, this, null, [ cur ] ).length;
- }
- if ( selMatch[ sel ] ) {
- matches.push( handleObj );
- }
- }
- if ( matches.length ) {
- handlerQueue.push({ elem: cur, matches: matches });
- }
- }
- }
- }
-
- // Add the remaining (directly-bound) handlers
- if ( handlers.length > delegateCount ) {
- handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
- }
-
- // Run delegates first; they may want to stop propagation beneath us
- for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
- matched = handlerQueue[ i ];
- event.currentTarget = matched.elem;
-
- for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
- handleObj = matched.matches[ j ];
-
- // Triggered event must either 1) be non-exclusive and have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
- if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
-
- event.data = handleObj.data;
- event.handleObj = handleObj;
-
- ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
- .apply( matched.elem, args );
-
- if ( ret !== undefined ) {
- event.result = ret;
- if ( ret === false ) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
- }
-
- // Call the postDispatch hook for the mapped type
- if ( special.postDispatch ) {
- special.postDispatch.call( this, event );
- }
-
- return event.result;
- },
-
- // Includes some event props shared by KeyEvent and MouseEvent
- // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
- props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
-
- fixHooks: {},
-
- keyHooks: {
- props: "char charCode key keyCode".split(" "),
- filter: function( event, original ) {
-
- // Add which for key events
- if ( event.which == null ) {
- event.which = original.charCode != null ? original.charCode : original.keyCode;
- }
-
- return event;
- }
- },
-
- mouseHooks: {
- props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
- filter: function( event, original ) {
- var eventDoc, doc, body,
- button = original.button,
- fromElement = original.fromElement;
-
- // Calculate pageX/Y if missing and clientX/Y available
- if ( event.pageX == null && original.clientX != null ) {
- eventDoc = event.target.ownerDocument || document;
- doc = eventDoc.documentElement;
- body = eventDoc.body;
-
- event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
- event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
- }
-
- // Add relatedTarget, if necessary
- if ( !event.relatedTarget && fromElement ) {
- event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
- }
-
- // Add which for click: 1 === left; 2 === middle; 3 === right
- // Note: button is not normalized, so don't use it
- if ( !event.which && button !== undefined ) {
- event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
- }
-
- return event;
- }
- },
-
- fix: function( event ) {
- if ( event[ jQuery.expando ] ) {
- return event;
- }
-
- // Create a writable copy of the event object and normalize some properties
- var i, prop,
- originalEvent = event,
- fixHook = jQuery.event.fixHooks[ event.type ] || {},
- copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
-
- event = jQuery.Event( originalEvent );
-
- for ( i = copy.length; i; ) {
- prop = copy[ --i ];
- event[ prop ] = originalEvent[ prop ];
- }
-
- // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
- if ( !event.target ) {
- event.target = originalEvent.srcElement || document;
- }
-
- // Target should not be a text node (#504, Safari)
- if ( event.target.nodeType === 3 ) {
- event.target = event.target.parentNode;
- }
-
- // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
- event.metaKey = !!event.metaKey;
-
- return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
- },
-
- special: {
- load: {
- // Prevent triggered image.load events from bubbling to window.load
- noBubble: true
- },
-
- focus: {
- delegateType: "focusin"
- },
- blur: {
- delegateType: "focusout"
- },
-
- beforeunload: {
- setup: function( data, namespaces, eventHandle ) {
- // We only want to do this special case on windows
- if ( jQuery.isWindow( this ) ) {
- this.onbeforeunload = eventHandle;
- }
- },
-
- teardown: function( namespaces, eventHandle ) {
- if ( this.onbeforeunload === eventHandle ) {
- this.onbeforeunload = null;
- }
- }
- }
- },
-
- simulate: function( type, elem, event, bubble ) {
- // Piggyback on a donor event to simulate a different one.
- // Fake originalEvent to avoid donor's stopPropagation, but if the
- // simulated event prevents default then we do the same on the donor.
- var e = jQuery.extend(
- new jQuery.Event(),
- event,
- { type: type,
- isSimulated: true,
- originalEvent: {}
- }
- );
- if ( bubble ) {
- jQuery.event.trigger( e, null, elem );
- } else {
- jQuery.event.dispatch.call( elem, e );
- }
- if ( e.isDefaultPrevented() ) {
- event.preventDefault();
- }
- }
-};
-
-// Some plugins are using, but it's undocumented/deprecated and will be removed.
-// The 1.7 special event interface should provide all the hooks needed now.
-jQuery.event.handle = jQuery.event.dispatch;
-
-jQuery.removeEvent = document.removeEventListener ?
- function( elem, type, handle ) {
- if ( elem.removeEventListener ) {
- elem.removeEventListener( type, handle, false );
- }
- } :
- function( elem, type, handle ) {
- var name = "on" + type;
-
- if ( elem.detachEvent ) {
-
- // #8545, #7054, preventing memory leaks for custom events in IE6-8
- // detachEvent needed property on element, by name of that event, to properly expose it to GC
- if ( typeof elem[ name ] === "undefined" ) {
- elem[ name ] = null;
- }
-
- elem.detachEvent( name, handle );
- }
- };
-
-jQuery.Event = function( src, props ) {
- // Allow instantiation without the 'new' keyword
- if ( !(this instanceof jQuery.Event) ) {
- return new jQuery.Event( src, props );
- }
-
- // Event object
- if ( src && src.type ) {
- this.originalEvent = src;
- this.type = src.type;
-
- // Events bubbling up the document may have been marked as prevented
- // by a handler lower down the tree; reflect the correct value.
- this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
- src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
-
- // Event type
- } else {
- this.type = src;
- }
-
- // Put explicitly provided properties onto the event object
- if ( props ) {
- jQuery.extend( this, props );
- }
-
- // Create a timestamp if incoming event doesn't have one
- this.timeStamp = src && src.timeStamp || jQuery.now();
-
- // Mark it as fixed
- this[ jQuery.expando ] = true;
-};
-
-function returnFalse() {
- return false;
-}
-function returnTrue() {
- return true;
-}
-
-// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
-// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
-jQuery.Event.prototype = {
- preventDefault: function() {
- this.isDefaultPrevented = returnTrue;
-
- var e = this.originalEvent;
- if ( !e ) {
- return;
- }
-
- // if preventDefault exists run it on the original event
- if ( e.preventDefault ) {
- e.preventDefault();
-
- // otherwise set the returnValue property of the original event to false (IE)
- } else {
- e.returnValue = false;
- }
- },
- stopPropagation: function() {
- this.isPropagationStopped = returnTrue;
-
- var e = this.originalEvent;
- if ( !e ) {
- return;
- }
- // if stopPropagation exists run it on the original event
- if ( e.stopPropagation ) {
- e.stopPropagation();
- }
- // otherwise set the cancelBubble property of the original event to true (IE)
- e.cancelBubble = true;
- },
- stopImmediatePropagation: function() {
- this.isImmediatePropagationStopped = returnTrue;
- this.stopPropagation();
- },
- isDefaultPrevented: returnFalse,
- isPropagationStopped: returnFalse,
- isImmediatePropagationStopped: returnFalse
-};
-
-// Create mouseenter/leave events using mouseover/out and event-time checks
-jQuery.each({
- mouseenter: "mouseover",
- mouseleave: "mouseout"
-}, function( orig, fix ) {
- jQuery.event.special[ orig ] = {
- delegateType: fix,
- bindType: fix,
-
- handle: function( event ) {
- var ret,
- target = this,
- related = event.relatedTarget,
- handleObj = event.handleObj,
- selector = handleObj.selector;
-
- // For mousenter/leave call the handler if related is outside the target.
- // NB: No relatedTarget if the mouse left/entered the browser window
- if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
- event.type = handleObj.origType;
- ret = handleObj.handler.apply( this, arguments );
- event.type = fix;
- }
- return ret;
- }
- };
-});
-
-// IE submit delegation
-if ( !jQuery.support.submitBubbles ) {
-
- jQuery.event.special.submit = {
- setup: function() {
- // Only need this for delegated form submit events
- if ( jQuery.nodeName( this, "form" ) ) {
- return false;
- }
-
- // Lazy-add a submit handler when a descendant form may potentially be submitted
- jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
- // Node name check avoids a VML-related crash in IE (#9807)
- var elem = e.target,
- form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
- if ( form && !jQuery._data( form, "_submit_attached" ) ) {
- jQuery.event.add( form, "submit._submit", function( event ) {
- event._submit_bubble = true;
- });
- jQuery._data( form, "_submit_attached", true );
- }
- });
- // return undefined since we don't need an event listener
- },
-
- postDispatch: function( event ) {
- // If form was submitted by the user, bubble the event up the tree
- if ( event._submit_bubble ) {
- delete event._submit_bubble;
- if ( this.parentNode && !event.isTrigger ) {
- jQuery.event.simulate( "submit", this.parentNode, event, true );
- }
- }
- },
-
- teardown: function() {
- // Only need this for delegated form submit events
- if ( jQuery.nodeName( this, "form" ) ) {
- return false;
- }
-
- // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
- jQuery.event.remove( this, "._submit" );
- }
- };
-}
-
-// IE change delegation and checkbox/radio fix
-if ( !jQuery.support.changeBubbles ) {
-
- jQuery.event.special.change = {
-
- setup: function() {
-
- if ( rformElems.test( this.nodeName ) ) {
- // IE doesn't fire change on a check/radio until blur; trigger it on click
- // after a propertychange. Eat the blur-change in special.change.handle.
- // This still fires onchange a second time for check/radio after blur.
- if ( this.type === "checkbox" || this.type === "radio" ) {
- jQuery.event.add( this, "propertychange._change", function( event ) {
- if ( event.originalEvent.propertyName === "checked" ) {
- this._just_changed = true;
- }
- });
- jQuery.event.add( this, "click._change", function( event ) {
- if ( this._just_changed && !event.isTrigger ) {
- this._just_changed = false;
- }
- // Allow triggered, simulated change events (#11500)
- jQuery.event.simulate( "change", this, event, true );
- });
- }
- return false;
- }
- // Delegated event; lazy-add a change handler on descendant inputs
- jQuery.event.add( this, "beforeactivate._change", function( e ) {
- var elem = e.target;
-
- if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
- jQuery.event.add( elem, "change._change", function( event ) {
- if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
- jQuery.event.simulate( "change", this.parentNode, event, true );
- }
- });
- jQuery._data( elem, "_change_attached", true );
- }
- });
- },
-
- handle: function( event ) {
- var elem = event.target;
-
- // Swallow native change events from checkbox/radio, we already triggered them above
- if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
- return event.handleObj.handler.apply( this, arguments );
- }
- },
-
- teardown: function() {
- jQuery.event.remove( this, "._change" );
-
- return !rformElems.test( this.nodeName );
- }
- };
-}
-
-// Create "bubbling" focus and blur events
-if ( !jQuery.support.focusinBubbles ) {
- jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
-
- // Attach a single capturing handler while someone wants focusin/focusout
- var attaches = 0,
- handler = function( event ) {
- jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
- };
-
- jQuery.event.special[ fix ] = {
- setup: function() {
- if ( attaches++ === 0 ) {
- document.addEventListener( orig, handler, true );
- }
- },
- teardown: function() {
- if ( --attaches === 0 ) {
- document.removeEventListener( orig, handler, true );
- }
- }
- };
- });
-}
-
-jQuery.fn.extend({
-
- on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
- var origFn, type;
-
- // Types can be a map of types/handlers
- if ( typeof types === "object" ) {
- // ( types-Object, selector, data )
- if ( typeof selector !== "string" ) { // && selector != null
- // ( types-Object, data )
- data = data || selector;
- selector = undefined;
- }
- for ( type in types ) {
- this.on( type, selector, data, types[ type ], one );
- }
- return this;
- }
-
- if ( data == null && fn == null ) {
- // ( types, fn )
- fn = selector;
- data = selector = undefined;
- } else if ( fn == null ) {
- if ( typeof selector === "string" ) {
- // ( types, selector, fn )
- fn = data;
- data = undefined;
- } else {
- // ( types, data, fn )
- fn = data;
- data = selector;
- selector = undefined;
- }
- }
- if ( fn === false ) {
- fn = returnFalse;
- } else if ( !fn ) {
- return this;
- }
-
- if ( one === 1 ) {
- origFn = fn;
- fn = function( event ) {
- // Can use an empty set, since event contains the info
- jQuery().off( event );
- return origFn.apply( this, arguments );
- };
- // Use same guid so caller can remove using origFn
- fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
- }
- return this.each( function() {
- jQuery.event.add( this, types, fn, data, selector );
- });
- },
- one: function( types, selector, data, fn ) {
- return this.on( types, selector, data, fn, 1 );
- },
- off: function( types, selector, fn ) {
- var handleObj, type;
- if ( types && types.preventDefault && types.handleObj ) {
- // ( event ) dispatched jQuery.Event
- handleObj = types.handleObj;
- jQuery( types.delegateTarget ).off(
- handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
- handleObj.selector,
- handleObj.handler
- );
- return this;
- }
- if ( typeof types === "object" ) {
- // ( types-object [, selector] )
- for ( type in types ) {
- this.off( type, selector, types[ type ] );
- }
- return this;
- }
- if ( selector === false || typeof selector === "function" ) {
- // ( types [, fn] )
- fn = selector;
- selector = undefined;
- }
- if ( fn === false ) {
- fn = returnFalse;
- }
- return this.each(function() {
- jQuery.event.remove( this, types, fn, selector );
- });
- },
-
- bind: function( types, data, fn ) {
- return this.on( types, null, data, fn );
- },
- unbind: function( types, fn ) {
- return this.off( types, null, fn );
- },
-
- live: function( types, data, fn ) {
- jQuery( this.context ).on( types, this.selector, data, fn );
- return this;
- },
- die: function( types, fn ) {
- jQuery( this.context ).off( types, this.selector || "**", fn );
- return this;
- },
-
- delegate: function( selector, types, data, fn ) {
- return this.on( types, selector, data, fn );
- },
- undelegate: function( selector, types, fn ) {
- // ( namespace ) or ( selector, types [, fn] )
- return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
- },
-
- trigger: function( type, data ) {
- return this.each(function() {
- jQuery.event.trigger( type, data, this );
- });
- },
- triggerHandler: function( type, data ) {
- if ( this[0] ) {
- return jQuery.event.trigger( type, data, this[0], true );
- }
- },
-
- toggle: function( fn ) {
- // Save reference to arguments for access in closure
- var args = arguments,
- guid = fn.guid || jQuery.guid++,
- i = 0,
- toggler = function( event ) {
- // Figure out which function to execute
- var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
- jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
-
- // Make sure that clicks stop
- event.preventDefault();
-
- // and execute the function
- return args[ lastToggle ].apply( this, arguments ) || false;
- };
-
- // link all the functions, so any of them can unbind this click handler
- toggler.guid = guid;
- while ( i < args.length ) {
- args[ i++ ].guid = guid;
- }
-
- return this.click( toggler );
- },
-
- hover: function( fnOver, fnOut ) {
- return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
- }
-});
-
-jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
- "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
- "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
-
- // Handle event binding
- jQuery.fn[ name ] = function( data, fn ) {
- if ( fn == null ) {
- fn = data;
- data = null;
- }
-
- return arguments.length > 0 ?
- this.on( name, null, data, fn ) :
- this.trigger( name );
- };
-
- if ( rkeyEvent.test( name ) ) {
- jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
- }
-
- if ( rmouseEvent.test( name ) ) {
- jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
- }
-});
-/*!
- * Sizzle CSS Selector Engine
- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license
- * http://sizzlejs.com/
- */
-(function( window, undefined ) {
-
-var cachedruns,
- assertGetIdNotName,
- Expr,
- getText,
- isXML,
- contains,
- compile,
- sortOrder,
- hasDuplicate,
- outermostContext,
-
- baseHasDuplicate = true,
- strundefined = "undefined",
-
- expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
-
- Token = String,
- document = window.document,
- docElem = document.documentElement,
- dirruns = 0,
- done = 0,
- pop = [].pop,
- push = [].push,
- slice = [].slice,
- // Use a stripped-down indexOf if a native one is unavailable
- indexOf = [].indexOf || function( elem ) {
- var i = 0,
- len = this.length;
- for ( ; i < len; i++ ) {
- if ( this[i] === elem ) {
- return i;
- }
- }
- return -1;
- },
-
- // Augment a function for special use by Sizzle
- markFunction = function( fn, value ) {
- fn[ expando ] = value == null || value;
- return fn;
- },
-
- createCache = function() {
- var cache = {},
- keys = [];
-
- return markFunction(function( key, value ) {
- // Only keep the most recent entries
- if ( keys.push( key ) > Expr.cacheLength ) {
- delete cache[ keys.shift() ];
- }
-
- // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
- return (cache[ key + " " ] = value);
- }, cache );
- },
-
- classCache = createCache(),
- tokenCache = createCache(),
- compilerCache = createCache(),
-
- // Regex
-
- // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
- whitespace = "[\\x20\\t\\r\\n\\f]",
- // http://www.w3.org/TR/css3-syntax/#characters
- characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
-
- // Loosely modeled on CSS identifier characters
- // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
- // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
- identifier = characterEncoding.replace( "w", "w#" ),
-
- // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
- operators = "([*^$|!~]?=)",
- attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
- "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
-
- // Prefer arguments not in parens/brackets,
- // then attribute selectors and non-pseudos (denoted by :),
- // then anything else
- // These preferences are here to reduce the number of selectors
- // needing tokenize in the PSEUDO preFilter
- pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
-
- // For matchExpr.POS and matchExpr.needsContext
- pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
- "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",
-
- // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
- rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
-
- rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
- rpseudo = new RegExp( pseudos ),
-
- // Easily-parseable/retrievable ID or TAG or CLASS selectors
- rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
-
- rnot = /^:not/,
- rsibling = /[\x20\t\r\n\f]*[+~]/,
- rendsWithNot = /:not\($/,
-
- rheader = /h\d/i,
- rinputs = /input|select|textarea|button/i,
-
- rbackslash = /\\(?!\\)/g,
-
- matchExpr = {
- "ID": new RegExp( "^#(" + characterEncoding + ")" ),
- "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
- "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
- "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
- "ATTR": new RegExp( "^" + attributes ),
- "PSEUDO": new RegExp( "^" + pseudos ),
- "POS": new RegExp( pos, "i" ),
- "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
- "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
- "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
- // For use in libraries implementing .is()
- "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
- },
-
- // Support
-
- // Used for testing something on an element
- assert = function( fn ) {
- var div = document.createElement("div");
-
- try {
- return fn( div );
- } catch (e) {
- return false;
- } finally {
- // release memory in IE
- div = null;
- }
- },
-
- // Check if getElementsByTagName("*") returns only elements
- assertTagNameNoComments = assert(function( div ) {
- div.appendChild( document.createComment("") );
- return !div.getElementsByTagName("*").length;
- }),
-
- // Check if getAttribute returns normalized href attributes
- assertHrefNotNormalized = assert(function( div ) {
- div.innerHTML = "";
- return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
- div.firstChild.getAttribute("href") === "#";
- }),
-
- // Check if attributes should be retrieved by attribute nodes
- assertAttributes = assert(function( div ) {
- div.innerHTML = "";
- var type = typeof div.lastChild.getAttribute("multiple");
- // IE8 returns a string for some attributes even when not present
- return type !== "boolean" && type !== "string";
- }),
-
- // Check if getElementsByClassName can be trusted
- assertUsableClassName = assert(function( div ) {
- // Opera can't find a second classname (in 9.6)
- div.innerHTML = "";
- if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
- return false;
- }
-
- // Safari 3.2 caches class attributes and doesn't catch changes
- div.lastChild.className = "e";
- return div.getElementsByClassName("e").length === 2;
- }),
-
- // Check if getElementById returns elements by name
- // Check if getElementsByName privileges form controls or returns elements by ID
- assertUsableName = assert(function( div ) {
- // Inject content
- div.id = expando + 0;
- div.innerHTML = "";
- docElem.insertBefore( div, docElem.firstChild );
-
- // Test
- var pass = document.getElementsByName &&
- // buggy browsers will return fewer than the correct 2
- document.getElementsByName( expando ).length === 2 +
- // buggy browsers will return more than the correct 0
- document.getElementsByName( expando + 0 ).length;
- assertGetIdNotName = !document.getElementById( expando );
-
- // Cleanup
- docElem.removeChild( div );
-
- return pass;
- });
-
-// If slice is not available, provide a backup
-try {
- slice.call( docElem.childNodes, 0 )[0].nodeType;
-} catch ( e ) {
- slice = function( i ) {
- var elem,
- results = [];
- for ( ; (elem = this[i]); i++ ) {
- results.push( elem );
- }
- return results;
- };
-}
-
-function Sizzle( selector, context, results, seed ) {
- results = results || [];
- context = context || document;
- var match, elem, xml, m,
- nodeType = context.nodeType;
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- if ( nodeType !== 1 && nodeType !== 9 ) {
- return [];
- }
-
- xml = isXML( context );
-
- if ( !xml && !seed ) {
- if ( (match = rquickExpr.exec( selector )) ) {
- // Speed-up: Sizzle("#ID")
- if ( (m = match[1]) ) {
- if ( nodeType === 9 ) {
- elem = context.getElementById( m );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE, Opera, and Webkit return items
- // by name instead of ID
- if ( elem.id === m ) {
- results.push( elem );
- return results;
- }
- } else {
- return results;
- }
- } else {
- // Context is not a document
- if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
- contains( context, elem ) && elem.id === m ) {
- results.push( elem );
- return results;
- }
- }
-
- // Speed-up: Sizzle("TAG")
- } else if ( match[2] ) {
- push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
- return results;
-
- // Speed-up: Sizzle(".CLASS")
- } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
- push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
- return results;
- }
- }
- }
-
- // All others
- return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
-}
-
-Sizzle.matches = function( expr, elements ) {
- return Sizzle( expr, null, null, elements );
-};
-
-Sizzle.matchesSelector = function( elem, expr ) {
- return Sizzle( expr, null, null, [ elem ] ).length > 0;
-};
-
-// Returns a function to use in pseudos for input types
-function createInputPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === type;
- };
-}
-
-// Returns a function to use in pseudos for buttons
-function createButtonPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && elem.type === type;
- };
-}
-
-// Returns a function to use in pseudos for positionals
-function createPositionalPseudo( fn ) {
- return markFunction(function( argument ) {
- argument = +argument;
- return markFunction(function( seed, matches ) {
- var j,
- matchIndexes = fn( [], seed.length, argument ),
- i = matchIndexes.length;
-
- // Match elements found at the specified indexes
- while ( i-- ) {
- if ( seed[ (j = matchIndexes[i]) ] ) {
- seed[j] = !(matches[j] = seed[j]);
- }
- }
- });
- });
-}
-
-/**
- * Utility function for retrieving the text value of an array of DOM nodes
- * @param {Array|Element} elem
- */
-getText = Sizzle.getText = function( elem ) {
- var node,
- ret = "",
- i = 0,
- nodeType = elem.nodeType;
-
- if ( nodeType ) {
- if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
- // Use textContent for elements
- // innerText usage removed for consistency of new lines (see #11153)
- if ( typeof elem.textContent === "string" ) {
- return elem.textContent;
- } else {
- // Traverse its children
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- ret += getText( elem );
- }
- }
- } else if ( nodeType === 3 || nodeType === 4 ) {
- return elem.nodeValue;
- }
- // Do not include comment or processing instruction nodes
- } else {
-
- // If no nodeType, this is expected to be an array
- for ( ; (node = elem[i]); i++ ) {
- // Do not traverse comment nodes
- ret += getText( node );
- }
- }
- return ret;
-};
-
-isXML = Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = elem && (elem.ownerDocument || elem).documentElement;
- return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-// Element contains another
-contains = Sizzle.contains = docElem.contains ?
- function( a, b ) {
- var adown = a.nodeType === 9 ? a.documentElement : a,
- bup = b && b.parentNode;
- return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
- } :
- docElem.compareDocumentPosition ?
- function( a, b ) {
- return b && !!( a.compareDocumentPosition( b ) & 16 );
- } :
- function( a, b ) {
- while ( (b = b.parentNode) ) {
- if ( b === a ) {
- return true;
- }
- }
- return false;
- };
-
-Sizzle.attr = function( elem, name ) {
- var val,
- xml = isXML( elem );
-
- if ( !xml ) {
- name = name.toLowerCase();
- }
- if ( (val = Expr.attrHandle[ name ]) ) {
- return val( elem );
- }
- if ( xml || assertAttributes ) {
- return elem.getAttribute( name );
- }
- val = elem.getAttributeNode( name );
- return val ?
- typeof elem[ name ] === "boolean" ?
- elem[ name ] ? name : null :
- val.specified ? val.value : null :
- null;
-};
-
-Expr = Sizzle.selectors = {
-
- // Can be adjusted by the user
- cacheLength: 50,
-
- createPseudo: markFunction,
-
- match: matchExpr,
-
- // IE6/7 return a modified href
- attrHandle: assertHrefNotNormalized ?
- {} :
- {
- "href": function( elem ) {
- return elem.getAttribute( "href", 2 );
- },
- "type": function( elem ) {
- return elem.getAttribute("type");
- }
- },
-
- find: {
- "ID": assertGetIdNotName ?
- function( id, context, xml ) {
- if ( typeof context.getElementById !== strundefined && !xml ) {
- var m = context.getElementById( id );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [m] : [];
- }
- } :
- function( id, context, xml ) {
- if ( typeof context.getElementById !== strundefined && !xml ) {
- var m = context.getElementById( id );
-
- return m ?
- m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
- [m] :
- undefined :
- [];
- }
- },
-
- "TAG": assertTagNameNoComments ?
- function( tag, context ) {
- if ( typeof context.getElementsByTagName !== strundefined ) {
- return context.getElementsByTagName( tag );
- }
- } :
- function( tag, context ) {
- var results = context.getElementsByTagName( tag );
-
- // Filter out possible comments
- if ( tag === "*" ) {
- var elem,
- tmp = [],
- i = 0;
-
- for ( ; (elem = results[i]); i++ ) {
- if ( elem.nodeType === 1 ) {
- tmp.push( elem );
- }
- }
-
- return tmp;
- }
- return results;
- },
-
- "NAME": assertUsableName && function( tag, context ) {
- if ( typeof context.getElementsByName !== strundefined ) {
- return context.getElementsByName( name );
- }
- },
-
- "CLASS": assertUsableClassName && function( className, context, xml ) {
- if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
- return context.getElementsByClassName( className );
- }
- }
- },
-
- relative: {
- ">": { dir: "parentNode", first: true },
- " ": { dir: "parentNode" },
- "+": { dir: "previousSibling", first: true },
- "~": { dir: "previousSibling" }
- },
-
- preFilter: {
- "ATTR": function( match ) {
- match[1] = match[1].replace( rbackslash, "" );
-
- // Move the given value to match[3] whether quoted or unquoted
- match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
-
- if ( match[2] === "~=" ) {
- match[3] = " " + match[3] + " ";
- }
-
- return match.slice( 0, 4 );
- },
-
- "CHILD": function( match ) {
- /* matches from matchExpr["CHILD"]
- 1 type (only|nth|...)
- 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
- 3 xn-component of xn+y argument ([+-]?\d*n|)
- 4 sign of xn-component
- 5 x of xn-component
- 6 sign of y-component
- 7 y of y-component
- */
- match[1] = match[1].toLowerCase();
-
- if ( match[1] === "nth" ) {
- // nth-child requires argument
- if ( !match[2] ) {
- Sizzle.error( match[0] );
- }
-
- // numeric x and y parameters for Expr.filter.CHILD
- // remember that false/true cast respectively to 0/1
- match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
- match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
-
- // other types prohibit arguments
- } else if ( match[2] ) {
- Sizzle.error( match[0] );
- }
-
- return match;
- },
-
- "PSEUDO": function( match ) {
- var unquoted, excess;
- if ( matchExpr["CHILD"].test( match[0] ) ) {
- return null;
- }
-
- if ( match[3] ) {
- match[2] = match[3];
- } else if ( (unquoted = match[4]) ) {
- // Only check arguments that contain a pseudo
- if ( rpseudo.test(unquoted) &&
- // Get excess from tokenize (recursively)
- (excess = tokenize( unquoted, true )) &&
- // advance to the next closing parenthesis
- (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
-
- // excess is a negative index
- unquoted = unquoted.slice( 0, excess );
- match[0] = match[0].slice( 0, excess );
- }
- match[2] = unquoted;
- }
-
- // Return only captures needed by the pseudo filter method (type and argument)
- return match.slice( 0, 3 );
- }
- },
-
- filter: {
- "ID": assertGetIdNotName ?
- function( id ) {
- id = id.replace( rbackslash, "" );
- return function( elem ) {
- return elem.getAttribute("id") === id;
- };
- } :
- function( id ) {
- id = id.replace( rbackslash, "" );
- return function( elem ) {
- var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
- return node && node.value === id;
- };
- },
-
- "TAG": function( nodeName ) {
- if ( nodeName === "*" ) {
- return function() { return true; };
- }
- nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
-
- return function( elem ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
- };
- },
-
- "CLASS": function( className ) {
- var pattern = classCache[ expando ][ className + " " ];
-
- return pattern ||
- (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
- classCache( className, function( elem ) {
- return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
- });
- },
-
- "ATTR": function( name, operator, check ) {
- return function( elem, context ) {
- var result = Sizzle.attr( elem, name );
-
- if ( result == null ) {
- return operator === "!=";
- }
- if ( !operator ) {
- return true;
- }
-
- result += "";
-
- return operator === "=" ? result === check :
- operator === "!=" ? result !== check :
- operator === "^=" ? check && result.indexOf( check ) === 0 :
- operator === "*=" ? check && result.indexOf( check ) > -1 :
- operator === "$=" ? check && result.substr( result.length - check.length ) === check :
- operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
- operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
- false;
- };
- },
-
- "CHILD": function( type, argument, first, last ) {
-
- if ( type === "nth" ) {
- return function( elem ) {
- var node, diff,
- parent = elem.parentNode;
-
- if ( first === 1 && last === 0 ) {
- return true;
- }
-
- if ( parent ) {
- diff = 0;
- for ( node = parent.firstChild; node; node = node.nextSibling ) {
- if ( node.nodeType === 1 ) {
- diff++;
- if ( elem === node ) {
- break;
- }
- }
- }
- }
-
- // Incorporate the offset (or cast to NaN), then check against cycle size
- diff -= last;
- return diff === first || ( diff % first === 0 && diff / first >= 0 );
- };
- }
-
- return function( elem ) {
- var node = elem;
-
- switch ( type ) {
- case "only":
- case "first":
- while ( (node = node.previousSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- if ( type === "first" ) {
- return true;
- }
-
- node = elem;
-
- /* falls through */
- case "last":
- while ( (node = node.nextSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
- }
- }
-
- return true;
- }
- };
- },
-
- "PSEUDO": function( pseudo, argument ) {
- // pseudo-class names are case-insensitive
- // http://www.w3.org/TR/selectors/#pseudo-classes
- // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
- // Remember that setFilters inherits from pseudos
- var args,
- fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
- Sizzle.error( "unsupported pseudo: " + pseudo );
-
- // The user may use createPseudo to indicate that
- // arguments are needed to create the filter function
- // just as Sizzle does
- if ( fn[ expando ] ) {
- return fn( argument );
- }
-
- // But maintain support for old signatures
- if ( fn.length > 1 ) {
- args = [ pseudo, pseudo, "", argument ];
- return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
- markFunction(function( seed, matches ) {
- var idx,
- matched = fn( seed, argument ),
- i = matched.length;
- while ( i-- ) {
- idx = indexOf.call( seed, matched[i] );
- seed[ idx ] = !( matches[ idx ] = matched[i] );
- }
- }) :
- function( elem ) {
- return fn( elem, 0, args );
- };
- }
-
- return fn;
- }
- },
-
- pseudos: {
- "not": markFunction(function( selector ) {
- // Trim the selector passed to compile
- // to avoid treating leading and trailing
- // spaces as combinators
- var input = [],
- results = [],
- matcher = compile( selector.replace( rtrim, "$1" ) );
-
- return matcher[ expando ] ?
- markFunction(function( seed, matches, context, xml ) {
- var elem,
- unmatched = matcher( seed, null, xml, [] ),
- i = seed.length;
-
- // Match elements unmatched by `matcher`
- while ( i-- ) {
- if ( (elem = unmatched[i]) ) {
- seed[i] = !(matches[i] = elem);
- }
- }
- }) :
- function( elem, context, xml ) {
- input[0] = elem;
- matcher( input, null, xml, results );
- return !results.pop();
- };
- }),
-
- "has": markFunction(function( selector ) {
- return function( elem ) {
- return Sizzle( selector, elem ).length > 0;
- };
- }),
-
- "contains": markFunction(function( text ) {
- return function( elem ) {
- return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
- };
- }),
-
- "enabled": function( elem ) {
- return elem.disabled === false;
- },
-
- "disabled": function( elem ) {
- return elem.disabled === true;
- },
-
- "checked": function( elem ) {
- // In CSS3, :checked should return both checked and selected elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- var nodeName = elem.nodeName.toLowerCase();
- return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
- },
-
- "selected": function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- "parent": function( elem ) {
- return !Expr.pseudos["empty"]( elem );
- },
-
- "empty": function( elem ) {
- // http://www.w3.org/TR/selectors/#empty-pseudo
- // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
- // not comment, processing instructions, or others
- // Thanks to Diego Perini for the nodeName shortcut
- // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
- var nodeType;
- elem = elem.firstChild;
- while ( elem ) {
- if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
- return false;
- }
- elem = elem.nextSibling;
- }
- return true;
- },
-
- "header": function( elem ) {
- return rheader.test( elem.nodeName );
- },
-
- "text": function( elem ) {
- var type, attr;
- // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
- // use getAttribute instead to test this case
- return elem.nodeName.toLowerCase() === "input" &&
- (type = elem.type) === "text" &&
- ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
- },
-
- // Input types
- "radio": createInputPseudo("radio"),
- "checkbox": createInputPseudo("checkbox"),
- "file": createInputPseudo("file"),
- "password": createInputPseudo("password"),
- "image": createInputPseudo("image"),
-
- "submit": createButtonPseudo("submit"),
- "reset": createButtonPseudo("reset"),
-
- "button": function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === "button" || name === "button";
- },
-
- "input": function( elem ) {
- return rinputs.test( elem.nodeName );
- },
-
- "focus": function( elem ) {
- var doc = elem.ownerDocument;
- return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
- },
-
- "active": function( elem ) {
- return elem === elem.ownerDocument.activeElement;
- },
-
- // Positional types
- "first": createPositionalPseudo(function() {
- return [ 0 ];
- }),
-
- "last": createPositionalPseudo(function( matchIndexes, length ) {
- return [ length - 1 ];
- }),
-
- "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
- return [ argument < 0 ? argument + length : argument ];
- }),
-
- "even": createPositionalPseudo(function( matchIndexes, length ) {
- for ( var i = 0; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "odd": createPositionalPseudo(function( matchIndexes, length ) {
- for ( var i = 1; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- })
- }
-};
-
-function siblingCheck( a, b, ret ) {
- if ( a === b ) {
- return ret;
- }
-
- var cur = a.nextSibling;
-
- while ( cur ) {
- if ( cur === b ) {
- return -1;
- }
-
- cur = cur.nextSibling;
- }
-
- return 1;
-}
-
-sortOrder = docElem.compareDocumentPosition ?
- function( a, b ) {
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
- a.compareDocumentPosition :
- a.compareDocumentPosition(b) & 4
- ) ? -1 : 1;
- } :
- function( a, b ) {
- // The nodes are identical, we can exit early
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
-
- // Fallback to using sourceIndex (in IE) if it's available on both nodes
- } else if ( a.sourceIndex && b.sourceIndex ) {
- return a.sourceIndex - b.sourceIndex;
- }
-
- var al, bl,
- ap = [],
- bp = [],
- aup = a.parentNode,
- bup = b.parentNode,
- cur = aup;
-
- // If the nodes are siblings (or identical) we can do a quick check
- if ( aup === bup ) {
- return siblingCheck( a, b );
-
- // If no parents were found then the nodes are disconnected
- } else if ( !aup ) {
- return -1;
-
- } else if ( !bup ) {
- return 1;
- }
-
- // Otherwise they're somewhere else in the tree so we need
- // to build up a full list of the parentNodes for comparison
- while ( cur ) {
- ap.unshift( cur );
- cur = cur.parentNode;
- }
-
- cur = bup;
-
- while ( cur ) {
- bp.unshift( cur );
- cur = cur.parentNode;
- }
-
- al = ap.length;
- bl = bp.length;
-
- // Start walking down the tree looking for a discrepancy
- for ( var i = 0; i < al && i < bl; i++ ) {
- if ( ap[i] !== bp[i] ) {
- return siblingCheck( ap[i], bp[i] );
- }
- }
-
- // We ended someplace up the tree so do a sibling check
- return i === al ?
- siblingCheck( a, bp[i], -1 ) :
- siblingCheck( ap[i], b, 1 );
- };
-
-// Always assume the presence of duplicates if sort doesn't
-// pass them to our comparison function (as in Google Chrome).
-[0, 0].sort( sortOrder );
-baseHasDuplicate = !hasDuplicate;
-
-// Document sorting and removing duplicates
-Sizzle.uniqueSort = function( results ) {
- var elem,
- duplicates = [],
- i = 1,
- j = 0;
-
- hasDuplicate = baseHasDuplicate;
- results.sort( sortOrder );
-
- if ( hasDuplicate ) {
- for ( ; (elem = results[i]); i++ ) {
- if ( elem === results[ i - 1 ] ) {
- j = duplicates.push( i );
- }
- }
- while ( j-- ) {
- results.splice( duplicates[ j ], 1 );
- }
- }
-
- return results;
-};
-
-Sizzle.error = function( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-function tokenize( selector, parseOnly ) {
- var matched, match, tokens, type,
- soFar, groups, preFilters,
- cached = tokenCache[ expando ][ selector + " " ];
-
- if ( cached ) {
- return parseOnly ? 0 : cached.slice( 0 );
- }
-
- soFar = selector;
- groups = [];
- preFilters = Expr.preFilter;
-
- while ( soFar ) {
-
- // Comma and first run
- if ( !matched || (match = rcomma.exec( soFar )) ) {
- if ( match ) {
- // Don't consume trailing commas as valid
- soFar = soFar.slice( match[0].length ) || soFar;
- }
- groups.push( tokens = [] );
- }
-
- matched = false;
-
- // Combinators
- if ( (match = rcombinators.exec( soFar )) ) {
- tokens.push( matched = new Token( match.shift() ) );
- soFar = soFar.slice( matched.length );
-
- // Cast descendant combinators to space
- matched.type = match[0].replace( rtrim, " " );
- }
-
- // Filters
- for ( type in Expr.filter ) {
- if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
- (match = preFilters[ type ]( match ))) ) {
-
- tokens.push( matched = new Token( match.shift() ) );
- soFar = soFar.slice( matched.length );
- matched.type = type;
- matched.matches = match;
- }
- }
-
- if ( !matched ) {
- break;
- }
- }
-
- // Return the length of the invalid excess
- // if we're just parsing
- // Otherwise, throw an error or return tokens
- return parseOnly ?
- soFar.length :
- soFar ?
- Sizzle.error( selector ) :
- // Cache the tokens
- tokenCache( selector, groups ).slice( 0 );
-}
-
-function addCombinator( matcher, combinator, base ) {
- var dir = combinator.dir,
- checkNonElements = base && combinator.dir === "parentNode",
- doneName = done++;
-
- return combinator.first ?
- // Check against closest ancestor/preceding element
- function( elem, context, xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( checkNonElements || elem.nodeType === 1 ) {
- return matcher( elem, context, xml );
- }
- }
- } :
-
- // Check against all ancestor/preceding elements
- function( elem, context, xml ) {
- // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
- if ( !xml ) {
- var cache,
- dirkey = dirruns + " " + doneName + " ",
- cachedkey = dirkey + cachedruns;
- while ( (elem = elem[ dir ]) ) {
- if ( checkNonElements || elem.nodeType === 1 ) {
- if ( (cache = elem[ expando ]) === cachedkey ) {
- return elem.sizset;
- } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
- if ( elem.sizset ) {
- return elem;
- }
- } else {
- elem[ expando ] = cachedkey;
- if ( matcher( elem, context, xml ) ) {
- elem.sizset = true;
- return elem;
- }
- elem.sizset = false;
- }
- }
- }
- } else {
- while ( (elem = elem[ dir ]) ) {
- if ( checkNonElements || elem.nodeType === 1 ) {
- if ( matcher( elem, context, xml ) ) {
- return elem;
- }
- }
- }
- }
- };
-}
-
-function elementMatcher( matchers ) {
- return matchers.length > 1 ?
- function( elem, context, xml ) {
- var i = matchers.length;
- while ( i-- ) {
- if ( !matchers[i]( elem, context, xml ) ) {
- return false;
- }
- }
- return true;
- } :
- matchers[0];
-}
-
-function condense( unmatched, map, filter, context, xml ) {
- var elem,
- newUnmatched = [],
- i = 0,
- len = unmatched.length,
- mapped = map != null;
-
- for ( ; i < len; i++ ) {
- if ( (elem = unmatched[i]) ) {
- if ( !filter || filter( elem, context, xml ) ) {
- newUnmatched.push( elem );
- if ( mapped ) {
- map.push( i );
- }
- }
- }
- }
-
- return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
- if ( postFilter && !postFilter[ expando ] ) {
- postFilter = setMatcher( postFilter );
- }
- if ( postFinder && !postFinder[ expando ] ) {
- postFinder = setMatcher( postFinder, postSelector );
- }
- return markFunction(function( seed, results, context, xml ) {
- var temp, i, elem,
- preMap = [],
- postMap = [],
- preexisting = results.length,
-
- // Get initial elements from seed or context
- elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
-
- // Prefilter to get matcher input, preserving a map for seed-results synchronization
- matcherIn = preFilter && ( seed || !selector ) ?
- condense( elems, preMap, preFilter, context, xml ) :
- elems,
-
- matcherOut = matcher ?
- // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
- postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
- // ...intermediate processing is necessary
- [] :
-
- // ...otherwise use results directly
- results :
- matcherIn;
-
- // Find primary matches
- if ( matcher ) {
- matcher( matcherIn, matcherOut, context, xml );
- }
-
- // Apply postFilter
- if ( postFilter ) {
- temp = condense( matcherOut, postMap );
- postFilter( temp, [], context, xml );
-
- // Un-match failing elements by moving them back to matcherIn
- i = temp.length;
- while ( i-- ) {
- if ( (elem = temp[i]) ) {
- matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
- }
- }
- }
-
- if ( seed ) {
- if ( postFinder || preFilter ) {
- if ( postFinder ) {
- // Get the final matcherOut by condensing this intermediate into postFinder contexts
- temp = [];
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) ) {
- // Restore matcherIn since elem is not yet a final match
- temp.push( (matcherIn[i] = elem) );
- }
- }
- postFinder( null, (matcherOut = []), temp, xml );
- }
-
- // Move matched elements from seed to results to keep them synchronized
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) &&
- (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
-
- seed[temp] = !(results[temp] = elem);
- }
- }
- }
-
- // Add elements to results, through postFinder if defined
- } else {
- matcherOut = condense(
- matcherOut === results ?
- matcherOut.splice( preexisting, matcherOut.length ) :
- matcherOut
- );
- if ( postFinder ) {
- postFinder( null, results, matcherOut, xml );
- } else {
- push.apply( results, matcherOut );
- }
- }
- });
-}
-
-function matcherFromTokens( tokens ) {
- var checkContext, matcher, j,
- len = tokens.length,
- leadingRelative = Expr.relative[ tokens[0].type ],
- implicitRelative = leadingRelative || Expr.relative[" "],
- i = leadingRelative ? 1 : 0,
-
- // The foundational matcher ensures that elements are reachable from top-level context(s)
- matchContext = addCombinator( function( elem ) {
- return elem === checkContext;
- }, implicitRelative, true ),
- matchAnyContext = addCombinator( function( elem ) {
- return indexOf.call( checkContext, elem ) > -1;
- }, implicitRelative, true ),
- matchers = [ function( elem, context, xml ) {
- return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
- (checkContext = context).nodeType ?
- matchContext( elem, context, xml ) :
- matchAnyContext( elem, context, xml ) );
- } ];
-
- for ( ; i < len; i++ ) {
- if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
- matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
- } else {
- matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
-
- // Return special upon seeing a positional matcher
- if ( matcher[ expando ] ) {
- // Find the next relative operator (if any) for proper handling
- j = ++i;
- for ( ; j < len; j++ ) {
- if ( Expr.relative[ tokens[j].type ] ) {
- break;
- }
- }
- return setMatcher(
- i > 1 && elementMatcher( matchers ),
- i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
- matcher,
- i < j && matcherFromTokens( tokens.slice( i, j ) ),
- j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
- j < len && tokens.join("")
- );
- }
- matchers.push( matcher );
- }
- }
-
- return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
- var bySet = setMatchers.length > 0,
- byElement = elementMatchers.length > 0,
- superMatcher = function( seed, context, xml, results, expandContext ) {
- var elem, j, matcher,
- setMatched = [],
- matchedCount = 0,
- i = "0",
- unmatched = seed && [],
- outermost = expandContext != null,
- contextBackup = outermostContext,
- // We must always have either seed elements or context
- elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
- // Nested matchers should use non-integer dirruns
- dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);
-
- if ( outermost ) {
- outermostContext = context !== document && context;
- cachedruns = superMatcher.el;
- }
-
- // Add elements passing elementMatchers directly to results
- for ( ; (elem = elems[i]) != null; i++ ) {
- if ( byElement && elem ) {
- for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
- if ( matcher( elem, context, xml ) ) {
- results.push( elem );
- break;
- }
- }
- if ( outermost ) {
- dirruns = dirrunsUnique;
- cachedruns = ++superMatcher.el;
- }
- }
-
- // Track unmatched elements for set filters
- if ( bySet ) {
- // They will have gone through all possible matchers
- if ( (elem = !matcher && elem) ) {
- matchedCount--;
- }
-
- // Lengthen the array for every element, matched or not
- if ( seed ) {
- unmatched.push( elem );
- }
- }
- }
-
- // Apply set filters to unmatched elements
- matchedCount += i;
- if ( bySet && i !== matchedCount ) {
- for ( j = 0; (matcher = setMatchers[j]); j++ ) {
- matcher( unmatched, setMatched, context, xml );
- }
-
- if ( seed ) {
- // Reintegrate element matches to eliminate the need for sorting
- if ( matchedCount > 0 ) {
- while ( i-- ) {
- if ( !(unmatched[i] || setMatched[i]) ) {
- setMatched[i] = pop.call( results );
- }
- }
- }
-
- // Discard index placeholder values to get only actual matches
- setMatched = condense( setMatched );
- }
-
- // Add matches to results
- push.apply( results, setMatched );
-
- // Seedless set matches succeeding multiple successful matchers stipulate sorting
- if ( outermost && !seed && setMatched.length > 0 &&
- ( matchedCount + setMatchers.length ) > 1 ) {
-
- Sizzle.uniqueSort( results );
- }
- }
-
- // Override manipulation of globals by nested matchers
- if ( outermost ) {
- dirruns = dirrunsUnique;
- outermostContext = contextBackup;
- }
-
- return unmatched;
- };
-
- superMatcher.el = 0;
- return bySet ?
- markFunction( superMatcher ) :
- superMatcher;
-}
-
-compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
- var i,
- setMatchers = [],
- elementMatchers = [],
- cached = compilerCache[ expando ][ selector + " " ];
-
- if ( !cached ) {
- // Generate a function of recursive functions that can be used to check each element
- if ( !group ) {
- group = tokenize( selector );
- }
- i = group.length;
- while ( i-- ) {
- cached = matcherFromTokens( group[i] );
- if ( cached[ expando ] ) {
- setMatchers.push( cached );
- } else {
- elementMatchers.push( cached );
- }
- }
-
- // Cache the compiled function
- cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
- }
- return cached;
-};
-
-function multipleContexts( selector, contexts, results ) {
- var i = 0,
- len = contexts.length;
- for ( ; i < len; i++ ) {
- Sizzle( selector, contexts[i], results );
- }
- return results;
-}
-
-function select( selector, context, results, seed, xml ) {
- var i, tokens, token, type, find,
- match = tokenize( selector ),
- j = match.length;
-
- if ( !seed ) {
- // Try to minimize operations if there is only one group
- if ( match.length === 1 ) {
-
- // Take a shortcut and set the context if the root selector is an ID
- tokens = match[0] = match[0].slice( 0 );
- if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
- context.nodeType === 9 && !xml &&
- Expr.relative[ tokens[1].type ] ) {
-
- context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
- if ( !context ) {
- return results;
- }
-
- selector = selector.slice( tokens.shift().length );
- }
-
- // Fetch a seed set for right-to-left matching
- for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
- token = tokens[i];
-
- // Abort if we hit a combinator
- if ( Expr.relative[ (type = token.type) ] ) {
- break;
- }
- if ( (find = Expr.find[ type ]) ) {
- // Search, expanding context for leading sibling combinators
- if ( (seed = find(
- token.matches[0].replace( rbackslash, "" ),
- rsibling.test( tokens[0].type ) && context.parentNode || context,
- xml
- )) ) {
-
- // If seed is empty or no tokens remain, we can return early
- tokens.splice( i, 1 );
- selector = seed.length && tokens.join("");
- if ( !selector ) {
- push.apply( results, slice.call( seed, 0 ) );
- return results;
- }
-
- break;
- }
- }
- }
- }
- }
-
- // Compile and execute a filtering function
- // Provide `match` to avoid retokenization if we modified the selector above
- compile( selector, match )(
- seed,
- context,
- xml,
- results,
- rsibling.test( selector )
- );
- return results;
-}
-
-if ( document.querySelectorAll ) {
- (function() {
- var disconnectedMatch,
- oldSelect = select,
- rescape = /'|\\/g,
- rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
-
- // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
- // A support test would require too much code (would include document ready)
- rbuggyQSA = [ ":focus" ],
-
- // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
- // A support test would require too much code (would include document ready)
- // just skip matchesSelector for :active
- rbuggyMatches = [ ":active" ],
- matches = docElem.matchesSelector ||
- docElem.mozMatchesSelector ||
- docElem.webkitMatchesSelector ||
- docElem.oMatchesSelector ||
- docElem.msMatchesSelector;
-
- // Build QSA regex
- // Regex strategy adopted from Diego Perini
- assert(function( div ) {
- // Select is set to empty string on purpose
- // This is to test IE's treatment of not explictly
- // setting a boolean content attribute,
- // since its presence should be enough
- // http://bugs.jquery.com/ticket/12359
- div.innerHTML = "";
-
- // IE8 - Some boolean attributes are not treated correctly
- if ( !div.querySelectorAll("[selected]").length ) {
- rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
- }
-
- // Webkit/Opera - :checked should return selected option elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- // IE8 throws error here (do not put tests after this one)
- if ( !div.querySelectorAll(":checked").length ) {
- rbuggyQSA.push(":checked");
- }
- });
-
- assert(function( div ) {
-
- // Opera 10-12/IE9 - ^= $= *= and empty values
- // Should not select anything
- div.innerHTML = "";
- if ( div.querySelectorAll("[test^='']").length ) {
- rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
- }
-
- // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
- // IE8 throws error here (do not put tests after this one)
- div.innerHTML = "";
- if ( !div.querySelectorAll(":enabled").length ) {
- rbuggyQSA.push(":enabled", ":disabled");
- }
- });
-
- // rbuggyQSA always contains :focus, so no need for a length check
- rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );
-
- select = function( selector, context, results, seed, xml ) {
- // Only use querySelectorAll when not filtering,
- // when this is not xml,
- // and when no QSA bugs apply
- if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
- var groups, i,
- old = true,
- nid = expando,
- newContext = context,
- newSelector = context.nodeType === 9 && selector;
-
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- groups = tokenize( selector );
-
- if ( (old = context.getAttribute("id")) ) {
- nid = old.replace( rescape, "\\$&" );
- } else {
- context.setAttribute( "id", nid );
- }
- nid = "[id='" + nid + "'] ";
-
- i = groups.length;
- while ( i-- ) {
- groups[i] = nid + groups[i].join("");
- }
- newContext = rsibling.test( selector ) && context.parentNode || context;
- newSelector = groups.join(",");
- }
-
- if ( newSelector ) {
- try {
- push.apply( results, slice.call( newContext.querySelectorAll(
- newSelector
- ), 0 ) );
- return results;
- } catch(qsaError) {
- } finally {
- if ( !old ) {
- context.removeAttribute("id");
- }
- }
- }
- }
-
- return oldSelect( selector, context, results, seed, xml );
- };
-
- if ( matches ) {
- assert(function( div ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9)
- disconnectedMatch = matches.call( div, "div" );
-
- // This should fail with an exception
- // Gecko does not error, returns false instead
- try {
- matches.call( div, "[test!='']:sizzle" );
- rbuggyMatches.push( "!=", pseudos );
- } catch ( e ) {}
- });
-
- // rbuggyMatches always contains :active and :focus, so no need for a length check
- rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
-
- Sizzle.matchesSelector = function( elem, expr ) {
- // Make sure that attribute selectors are quoted
- expr = expr.replace( rattributeQuotes, "='$1']" );
-
- // rbuggyMatches always contains :active, so no need for an existence check
- if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
- try {
- var ret = matches.call( elem, expr );
-
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9
- elem.document && elem.document.nodeType !== 11 ) {
- return ret;
- }
- } catch(e) {}
- }
-
- return Sizzle( expr, null, null, [ elem ] ).length > 0;
- };
- }
- })();
-}
-
-// Deprecated
-Expr.pseudos["nth"] = Expr.pseudos["eq"];
-
-// Back-compat
-function setFilters() {}
-Expr.filters = setFilters.prototype = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-// Override sizzle attribute retrieval
-Sizzle.attr = jQuery.attr;
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.pseudos;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-})( window );
-var runtil = /Until$/,
- rparentsprev = /^(?:parents|prev(?:Until|All))/,
- isSimple = /^.[^:#\[\.,]*$/,
- rneedsContext = jQuery.expr.match.needsContext,
- // methods guaranteed to produce a unique set when starting from a unique set
- guaranteedUnique = {
- children: true,
- contents: true,
- next: true,
- prev: true
- };
-
-jQuery.fn.extend({
- find: function( selector ) {
- var i, l, length, n, r, ret,
- self = this;
-
- if ( typeof selector !== "string" ) {
- return jQuery( selector ).filter(function() {
- for ( i = 0, l = self.length; i < l; i++ ) {
- if ( jQuery.contains( self[ i ], this ) ) {
- return true;
- }
- }
- });
- }
-
- ret = this.pushStack( "", "find", selector );
-
- for ( i = 0, l = this.length; i < l; i++ ) {
- length = ret.length;
- jQuery.find( selector, this[i], ret );
-
- if ( i > 0 ) {
- // Make sure that the results are unique
- for ( n = length; n < ret.length; n++ ) {
- for ( r = 0; r < length; r++ ) {
- if ( ret[r] === ret[n] ) {
- ret.splice(n--, 1);
- break;
- }
- }
- }
- }
- }
-
- return ret;
- },
-
- has: function( target ) {
- var i,
- targets = jQuery( target, this ),
- len = targets.length;
-
- return this.filter(function() {
- for ( i = 0; i < len; i++ ) {
- if ( jQuery.contains( this, targets[i] ) ) {
- return true;
- }
- }
- });
- },
-
- not: function( selector ) {
- return this.pushStack( winnow(this, selector, false), "not", selector);
- },
-
- filter: function( selector ) {
- return this.pushStack( winnow(this, selector, true), "filter", selector );
- },
-
- is: function( selector ) {
- return !!selector && (
- typeof selector === "string" ?
- // If this is a positional/relative selector, check membership in the returned set
- // so $("p:first").is("p:last") won't return true for a doc with two "p".
- rneedsContext.test( selector ) ?
- jQuery( selector, this.context ).index( this[0] ) >= 0 :
- jQuery.filter( selector, this ).length > 0 :
- this.filter( selector ).length > 0 );
- },
-
- closest: function( selectors, context ) {
- var cur,
- i = 0,
- l = this.length,
- ret = [],
- pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
- jQuery( selectors, context || this.context ) :
- 0;
-
- for ( ; i < l; i++ ) {
- cur = this[i];
-
- while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
- if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- ret.push( cur );
- break;
- }
- cur = cur.parentNode;
- }
- }
-
- ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
-
- return this.pushStack( ret, "closest", selectors );
- },
-
- // Determine the position of an element within
- // the matched set of elements
- index: function( elem ) {
-
- // No argument, return index in parent
- if ( !elem ) {
- return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
- }
-
- // index in selector
- if ( typeof elem === "string" ) {
- return jQuery.inArray( this[0], jQuery( elem ) );
- }
-
- // Locate the position of the desired element
- return jQuery.inArray(
- // If it receives a jQuery object, the first element is used
- elem.jquery ? elem[0] : elem, this );
- },
-
- add: function( selector, context ) {
- var set = typeof selector === "string" ?
- jQuery( selector, context ) :
- jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
- all = jQuery.merge( this.get(), set );
-
- return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
- all :
- jQuery.unique( all ) );
- },
-
- addBack: function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter(selector)
- );
- }
-});
-
-jQuery.fn.andSelf = jQuery.fn.addBack;
-
-// A painfully simple check to see if an element is disconnected
-// from a document (should be improved, where feasible).
-function isDisconnected( node ) {
- return !node || !node.parentNode || node.parentNode.nodeType === 11;
-}
-
-function sibling( cur, dir ) {
- do {
- cur = cur[ dir ];
- } while ( cur && cur.nodeType !== 1 );
-
- return cur;
-}
-
-jQuery.each({
- parent: function( elem ) {
- var parent = elem.parentNode;
- return parent && parent.nodeType !== 11 ? parent : null;
- },
- parents: function( elem ) {
- return jQuery.dir( elem, "parentNode" );
- },
- parentsUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "parentNode", until );
- },
- next: function( elem ) {
- return sibling( elem, "nextSibling" );
- },
- prev: function( elem ) {
- return sibling( elem, "previousSibling" );
- },
- nextAll: function( elem ) {
- return jQuery.dir( elem, "nextSibling" );
- },
- prevAll: function( elem ) {
- return jQuery.dir( elem, "previousSibling" );
- },
- nextUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "nextSibling", until );
- },
- prevUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "previousSibling", until );
- },
- siblings: function( elem ) {
- return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
- },
- children: function( elem ) {
- return jQuery.sibling( elem.firstChild );
- },
- contents: function( elem ) {
- return jQuery.nodeName( elem, "iframe" ) ?
- elem.contentDocument || elem.contentWindow.document :
- jQuery.merge( [], elem.childNodes );
- }
-}, function( name, fn ) {
- jQuery.fn[ name ] = function( until, selector ) {
- var ret = jQuery.map( this, fn, until );
-
- if ( !runtil.test( name ) ) {
- selector = until;
- }
-
- if ( selector && typeof selector === "string" ) {
- ret = jQuery.filter( selector, ret );
- }
-
- ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
-
- if ( this.length > 1 && rparentsprev.test( name ) ) {
- ret = ret.reverse();
- }
-
- return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
- };
-});
-
-jQuery.extend({
- filter: function( expr, elems, not ) {
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
-
- return elems.length === 1 ?
- jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
- jQuery.find.matches(expr, elems);
- },
-
- dir: function( elem, dir, until ) {
- var matched = [],
- cur = elem[ dir ];
-
- while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
- if ( cur.nodeType === 1 ) {
- matched.push( cur );
- }
- cur = cur[dir];
- }
- return matched;
- },
-
- sibling: function( n, elem ) {
- var r = [];
-
- for ( ; n; n = n.nextSibling ) {
- if ( n.nodeType === 1 && n !== elem ) {
- r.push( n );
- }
- }
-
- return r;
- }
-});
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, keep ) {
-
- // Can't pass null or undefined to indexOf in Firefox 4
- // Set to 0 to skip string check
- qualifier = qualifier || 0;
-
- if ( jQuery.isFunction( qualifier ) ) {
- return jQuery.grep(elements, function( elem, i ) {
- var retVal = !!qualifier.call( elem, i, elem );
- return retVal === keep;
- });
-
- } else if ( qualifier.nodeType ) {
- return jQuery.grep(elements, function( elem, i ) {
- return ( elem === qualifier ) === keep;
- });
-
- } else if ( typeof qualifier === "string" ) {
- var filtered = jQuery.grep(elements, function( elem ) {
- return elem.nodeType === 1;
- });
-
- if ( isSimple.test( qualifier ) ) {
- return jQuery.filter(qualifier, filtered, !keep);
- } else {
- qualifier = jQuery.filter( qualifier, filtered );
- }
- }
-
- return jQuery.grep(elements, function( elem, i ) {
- return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
- });
-}
-function createSafeFragment( document ) {
- var list = nodeNames.split( "|" ),
- safeFrag = document.createDocumentFragment();
-
- if ( safeFrag.createElement ) {
- while ( list.length ) {
- safeFrag.createElement(
- list.pop()
- );
- }
- }
- return safeFrag;
-}
-
-var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
- "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
- rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
- rleadingWhitespace = /^\s+/,
- rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
- rtagName = /<([\w:]+)/,
- rtbody = /]", "i"),
- rcheckableType = /^(?:checkbox|radio)$/,
- // checked="checked" or checked
- rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
- rscriptType = /\/(java|ecma)script/i,
- rcleanScript = /^\s*\s*$/g,
- wrapMap = {
- option: [ 1, "" ],
- legend: [ 1, "" ],
- thead: [ 1, "
", "
" ],
- tr: [ 2, "
", "
" ],
- td: [ 3, "
", "
" ],
- col: [ 2, "
", "
" ],
- area: [ 1, "" ],
- _default: [ 0, "", "" ]
- },
- safeFragment = createSafeFragment( document ),
- fragmentDiv = safeFragment.appendChild( document.createElement("div") );
-
-wrapMap.optgroup = wrapMap.option;
-wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
-wrapMap.th = wrapMap.td;
-
-// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
-// unless wrapped in a div with non-breaking characters in front of it.
-if ( !jQuery.support.htmlSerialize ) {
- wrapMap._default = [ 1, "X
", "
" ];
-}
-
-jQuery.fn.extend({
- text: function( value ) {
- return jQuery.access( this, function( value ) {
- return value === undefined ?
- jQuery.text( this ) :
- this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
- }, null, value, arguments.length );
- },
-
- wrapAll: function( html ) {
- if ( jQuery.isFunction( html ) ) {
- return this.each(function(i) {
- jQuery(this).wrapAll( html.call(this, i) );
- });
- }
-
- if ( this[0] ) {
- // The elements to wrap the target around
- var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
-
- if ( this[0].parentNode ) {
- wrap.insertBefore( this[0] );
- }
-
- wrap.map(function() {
- var elem = this;
-
- while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
- elem = elem.firstChild;
- }
-
- return elem;
- }).append( this );
- }
-
- return this;
- },
-
- wrapInner: function( html ) {
- if ( jQuery.isFunction( html ) ) {
- return this.each(function(i) {
- jQuery(this).wrapInner( html.call(this, i) );
- });
- }
-
- return this.each(function() {
- var self = jQuery( this ),
- contents = self.contents();
-
- if ( contents.length ) {
- contents.wrapAll( html );
-
- } else {
- self.append( html );
- }
- });
- },
-
- wrap: function( html ) {
- var isFunction = jQuery.isFunction( html );
-
- return this.each(function(i) {
- jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
- });
- },
-
- unwrap: function() {
- return this.parent().each(function() {
- if ( !jQuery.nodeName( this, "body" ) ) {
- jQuery( this ).replaceWith( this.childNodes );
- }
- }).end();
- },
-
- append: function() {
- return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 || this.nodeType === 11 ) {
- this.appendChild( elem );
- }
- });
- },
-
- prepend: function() {
- return this.domManip(arguments, true, function( elem ) {
- if ( this.nodeType === 1 || this.nodeType === 11 ) {
- this.insertBefore( elem, this.firstChild );
- }
- });
- },
-
- before: function() {
- if ( !isDisconnected( this[0] ) ) {
- return this.domManip(arguments, false, function( elem ) {
- this.parentNode.insertBefore( elem, this );
- });
- }
-
- if ( arguments.length ) {
- var set = jQuery.clean( arguments );
- return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
- }
- },
-
- after: function() {
- if ( !isDisconnected( this[0] ) ) {
- return this.domManip(arguments, false, function( elem ) {
- this.parentNode.insertBefore( elem, this.nextSibling );
- });
- }
-
- if ( arguments.length ) {
- var set = jQuery.clean( arguments );
- return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
- }
- },
-
- // keepData is for internal use only--do not document
- remove: function( selector, keepData ) {
- var elem,
- i = 0;
-
- for ( ; (elem = this[i]) != null; i++ ) {
- if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
- if ( !keepData && elem.nodeType === 1 ) {
- jQuery.cleanData( elem.getElementsByTagName("*") );
- jQuery.cleanData( [ elem ] );
- }
-
- if ( elem.parentNode ) {
- elem.parentNode.removeChild( elem );
- }
- }
- }
-
- return this;
- },
-
- empty: function() {
- var elem,
- i = 0;
-
- for ( ; (elem = this[i]) != null; i++ ) {
- // Remove element nodes and prevent memory leaks
- if ( elem.nodeType === 1 ) {
- jQuery.cleanData( elem.getElementsByTagName("*") );
- }
-
- // Remove any remaining nodes
- while ( elem.firstChild ) {
- elem.removeChild( elem.firstChild );
- }
- }
-
- return this;
- },
-
- clone: function( dataAndEvents, deepDataAndEvents ) {
- dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
- deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
-
- return this.map( function () {
- return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
- });
- },
-
- html: function( value ) {
- return jQuery.access( this, function( value ) {
- var elem = this[0] || {},
- i = 0,
- l = this.length;
-
- if ( value === undefined ) {
- return elem.nodeType === 1 ?
- elem.innerHTML.replace( rinlinejQuery, "" ) :
- undefined;
- }
-
- // See if we can take a shortcut and just use innerHTML
- if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
- ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
- ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
- !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
-
- value = value.replace( rxhtmlTag, "<$1>$2>" );
-
- try {
- for (; i < l; i++ ) {
- // Remove element nodes and prevent memory leaks
- elem = this[i] || {};
- if ( elem.nodeType === 1 ) {
- jQuery.cleanData( elem.getElementsByTagName( "*" ) );
- elem.innerHTML = value;
- }
- }
-
- elem = 0;
-
- // If using innerHTML throws an exception, use the fallback method
- } catch(e) {}
- }
-
- if ( elem ) {
- this.empty().append( value );
- }
- }, null, value, arguments.length );
- },
-
- replaceWith: function( value ) {
- if ( !isDisconnected( this[0] ) ) {
- // Make sure that the elements are removed from the DOM before they are inserted
- // this can help fix replacing a parent with child elements
- if ( jQuery.isFunction( value ) ) {
- return this.each(function(i) {
- var self = jQuery(this), old = self.html();
- self.replaceWith( value.call( this, i, old ) );
- });
- }
-
- if ( typeof value !== "string" ) {
- value = jQuery( value ).detach();
- }
-
- return this.each(function() {
- var next = this.nextSibling,
- parent = this.parentNode;
-
- jQuery( this ).remove();
-
- if ( next ) {
- jQuery(next).before( value );
- } else {
- jQuery(parent).append( value );
- }
- });
- }
-
- return this.length ?
- this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
- this;
- },
-
- detach: function( selector ) {
- return this.remove( selector, true );
- },
-
- domManip: function( args, table, callback ) {
-
- // Flatten any nested arrays
- args = [].concat.apply( [], args );
-
- var results, first, fragment, iNoClone,
- i = 0,
- value = args[0],
- scripts = [],
- l = this.length;
-
- // We can't cloneNode fragments that contain checked, in WebKit
- if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
- return this.each(function() {
- jQuery(this).domManip( args, table, callback );
- });
- }
-
- if ( jQuery.isFunction(value) ) {
- return this.each(function(i) {
- var self = jQuery(this);
- args[0] = value.call( this, i, table ? self.html() : undefined );
- self.domManip( args, table, callback );
- });
- }
-
- if ( this[0] ) {
- results = jQuery.buildFragment( args, this, scripts );
- fragment = results.fragment;
- first = fragment.firstChild;
-
- if ( fragment.childNodes.length === 1 ) {
- fragment = first;
- }
-
- if ( first ) {
- table = table && jQuery.nodeName( first, "tr" );
-
- // Use the original fragment for the last item instead of the first because it can end up
- // being emptied incorrectly in certain situations (#8070).
- // Fragments from the fragment cache must always be cloned and never used in place.
- for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
- callback.call(
- table && jQuery.nodeName( this[i], "table" ) ?
- findOrAppend( this[i], "tbody" ) :
- this[i],
- i === iNoClone ?
- fragment :
- jQuery.clone( fragment, true, true )
- );
- }
- }
-
- // Fix #11809: Avoid leaking memory
- fragment = first = null;
-
- if ( scripts.length ) {
- jQuery.each( scripts, function( i, elem ) {
- if ( elem.src ) {
- if ( jQuery.ajax ) {
- jQuery.ajax({
- url: elem.src,
- type: "GET",
- dataType: "script",
- async: false,
- global: false,
- "throws": true
- });
- } else {
- jQuery.error("no ajax");
- }
- } else {
- jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
- }
-
- if ( elem.parentNode ) {
- elem.parentNode.removeChild( elem );
- }
- });
- }
- }
-
- return this;
- }
-});
-
-function findOrAppend( elem, tag ) {
- return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
-}
-
-function cloneCopyEvent( src, dest ) {
-
- if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
- return;
- }
-
- var type, i, l,
- oldData = jQuery._data( src ),
- curData = jQuery._data( dest, oldData ),
- events = oldData.events;
-
- if ( events ) {
- delete curData.handle;
- curData.events = {};
-
- for ( type in events ) {
- for ( i = 0, l = events[ type ].length; i < l; i++ ) {
- jQuery.event.add( dest, type, events[ type ][ i ] );
- }
- }
- }
-
- // make the cloned public data object a copy from the original
- if ( curData.data ) {
- curData.data = jQuery.extend( {}, curData.data );
- }
-}
-
-function cloneFixAttributes( src, dest ) {
- var nodeName;
-
- // We do not need to do anything for non-Elements
- if ( dest.nodeType !== 1 ) {
- return;
- }
-
- // clearAttributes removes the attributes, which we don't want,
- // but also removes the attachEvent events, which we *do* want
- if ( dest.clearAttributes ) {
- dest.clearAttributes();
- }
-
- // mergeAttributes, in contrast, only merges back on the
- // original attributes, not the events
- if ( dest.mergeAttributes ) {
- dest.mergeAttributes( src );
- }
-
- nodeName = dest.nodeName.toLowerCase();
-
- if ( nodeName === "object" ) {
- // IE6-10 improperly clones children of object elements using classid.
- // IE10 throws NoModificationAllowedError if parent is null, #12132.
- if ( dest.parentNode ) {
- dest.outerHTML = src.outerHTML;
- }
-
- // This path appears unavoidable for IE9. When cloning an object
- // element in IE9, the outerHTML strategy above is not sufficient.
- // If the src has innerHTML and the destination does not,
- // copy the src.innerHTML into the dest.innerHTML. #10324
- if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
- dest.innerHTML = src.innerHTML;
- }
-
- } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
- // IE6-8 fails to persist the checked state of a cloned checkbox
- // or radio button. Worse, IE6-7 fail to give the cloned element
- // a checked appearance if the defaultChecked value isn't also set
-
- dest.defaultChecked = dest.checked = src.checked;
-
- // IE6-7 get confused and end up setting the value of a cloned
- // checkbox/radio button to an empty string instead of "on"
- if ( dest.value !== src.value ) {
- dest.value = src.value;
- }
-
- // IE6-8 fails to return the selected option to the default selected
- // state when cloning options
- } else if ( nodeName === "option" ) {
- dest.selected = src.defaultSelected;
-
- // IE6-8 fails to set the defaultValue to the correct value when
- // cloning other types of input fields
- } else if ( nodeName === "input" || nodeName === "textarea" ) {
- dest.defaultValue = src.defaultValue;
-
- // IE blanks contents when cloning scripts
- } else if ( nodeName === "script" && dest.text !== src.text ) {
- dest.text = src.text;
- }
-
- // Event data gets referenced instead of copied if the expando
- // gets copied too
- dest.removeAttribute( jQuery.expando );
-}
-
-jQuery.buildFragment = function( args, context, scripts ) {
- var fragment, cacheable, cachehit,
- first = args[ 0 ];
-
- // Set context from what may come in as undefined or a jQuery collection or a node
- // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 &
- // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception
- context = context || document;
- context = !context.nodeType && context[0] || context;
- context = context.ownerDocument || context;
-
- // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
- // Cloning options loses the selected state, so don't cache them
- // IE 6 doesn't like it when you put