Mercurial > cpdt > repo
comparison src/Reflection.v @ 398:05efde66559d
Get it working in Coq 8.4beta1; use nice coqdoc notation for italics
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Wed, 06 Jun 2012 11:25:13 -0400 |
parents | e23d41ae63be |
children | d32de711635c |
comparison
equal
deleted
inserted
replaced
397:d62ed7381a0b | 398:05efde66559d |
---|---|
16 (* end hide *) | 16 (* end hide *) |
17 | 17 |
18 | 18 |
19 (** %\chapter{Proof by Reflection}% *) | 19 (** %\chapter{Proof by Reflection}% *) |
20 | 20 |
21 (** The last chapter highlighted a very heuristic approach to proving. In this chapter, we will study an alternative technique, %\index{proof by reflection}\textit{%#<i>#proof by reflection#</i>#%}~\cite{reflection}%. We will write, in Gallina, decision procedures with proofs of correctness, and we will appeal to these procedures in writing very short proofs. Such a proof is checked by running the decision procedure. The term %\textit{%#<i>#reflection#</i>#%}% applies because we will need to translate Gallina propositions into values of inductive types representing syntax, so that Gallina programs may analyze them. *) | 21 (** The last chapter highlighted a very heuristic approach to proving. In this chapter, we will study an alternative technique, %\index{proof by reflection}%_proof by reflection_ %\cite{reflection}%. We will write, in Gallina, decision procedures with proofs of correctness, and we will appeal to these procedures in writing very short proofs. Such a proof is checked by running the decision procedure. The term _reflection_ applies because we will need to translate Gallina propositions into values of inductive types representing syntax, so that Gallina programs may analyze them. *) |
22 | 22 |
23 | 23 |
24 (** * Proving Evenness *) | 24 (** * Proving Evenness *) |
25 | 25 |
26 (** Proving that particular natural number constants are even is certainly something we would rather have happen automatically. The Ltac-programming techniques that we learned in the last chapter make it easy to implement such a procedure. *) | 26 (** Proving that particular natural number constants are even is certainly something we would rather have happen automatically. The Ltac-programming techniques that we learned in the last chapter make it easy to implement such a procedure. *) |
79 | 1 => No | 79 | 1 => No |
80 | S (S n') => Reduce (F n') | 80 | S (S n') => Reduce (F n') |
81 end); auto. | 81 end); auto. |
82 Defined. | 82 Defined. |
83 | 83 |
84 (** The function [check_even] may be viewed as a %\emph{%#<i>#verified decision procedure#</i>#%}%, because its type guarantees that it never returns [Yes] for inputs that are not even. | 84 (** The function [check_even] may be viewed as a _verified decision procedure_, because its type guarantees that it never returns [Yes] for inputs that are not even. |
85 | 85 |
86 Now we can use dependent pattern-matching to write a function that performs a surprising feat. When given a [partial P], this function [partialOut] returns a proof of [P] if the [partial] value contains a proof, and it returns a (useless) proof of [True] otherwise. From the standpoint of ML and Haskell programming, it seems impossible to write such a type, but it is trivial with a [return] annotation. *) | 86 Now we can use dependent pattern-matching to write a function that performs a surprising feat. When given a [partial P], this function [partialOut] returns a proof of [P] if the [partial] value contains a proof, and it returns a (useless) proof of [True] otherwise. From the standpoint of ML and Haskell programming, it seems impossible to write such a type, but it is trivial with a [return] annotation. *) |
87 | 87 |
88 Definition partialOut (P : Prop) (x : [P]) := | 88 Definition partialOut (P : Prop) (x : [P]) := |
89 match x return (match x with | 89 match x return (match x with |
165 | 165 |
166 ]] | 166 ]] |
167 | 167 |
168 As we might expect, the proof that [tauto] builds contains explicit applications of natural deduction rules. For large formulas, this can add a linear amount of proof size overhead, beyond the size of the input. | 168 As we might expect, the proof that [tauto] builds contains explicit applications of natural deduction rules. For large formulas, this can add a linear amount of proof size overhead, beyond the size of the input. |
169 | 169 |
170 To write a reflective procedure for this class of goals, we will need to get into the actual %``%#"#reflection#"#%''% part of %``%#"#proof by reflection.#"#%''% It is impossible to case-analyze a [Prop] in any way in Gallina. We must %\index{reification}\textit{%#<i>#reify#</i>#%}% [Prop] into some type that we %\textit{%#<i>#can#</i>#%}% analyze. This inductive type is a good candidate: *) | 170 To write a reflective procedure for this class of goals, we will need to get into the actual %``%#"#reflection#"#%''% part of %``%#"#proof by reflection.#"#%''% It is impossible to case-analyze a [Prop] in any way in Gallina. We must %\index{reification}%_reify_ [Prop] into some type that we _can_ analyze. This inductive type is a good candidate: *) |
171 | 171 |
172 (* begin thide *) | 172 (* begin thide *) |
173 Inductive taut : Set := | 173 Inductive taut : Set := |
174 | TautTrue : taut | 174 | TautTrue : taut |
175 | TautAnd : taut -> taut -> taut | 175 | TautAnd : taut -> taut -> taut |
176 | TautOr : taut -> taut -> taut | 176 | TautOr : taut -> taut -> taut |
177 | TautImp : taut -> taut -> taut. | 177 | TautImp : taut -> taut -> taut. |
178 | 178 |
179 (** We write a recursive function to %\emph{%#<i>#reflect#</i>#%}% this syntax back to [Prop]. Such functions are also called %\index{interpretation function}\emph{%#<i>#interpretation functions#</i>#%}%, and have used them in previous examples to give semantics to small programming languages. *) | 179 (** We write a recursive function to _reflect_ this syntax back to [Prop]. Such functions are also called %\index{interpretation function}%_interpretation functions_, and have used them in previous examples to give semantics to small programming languages. *) |
180 | 180 |
181 Fixpoint tautDenote (t : taut) : Prop := | 181 Fixpoint tautDenote (t : taut) : Prop := |
182 match t with | 182 match t with |
183 | TautTrue => True | 183 | TautTrue => True |
184 | TautAnd t1 t2 => tautDenote t1 /\ tautDenote t2 | 184 | TautAnd t1 t2 => tautDenote t1 /\ tautDenote t2 |
235 (TautImp (TautAnd TautTrue TautTrue) | 235 (TautImp (TautAnd TautTrue TautTrue) |
236 (TautOr TautTrue (TautAnd TautTrue (TautImp TautTrue TautTrue)))) | 236 (TautOr TautTrue (TautAnd TautTrue (TautImp TautTrue TautTrue)))) |
237 : True /\ True -> True \/ True /\ (True -> True) | 237 : True /\ True -> True \/ True /\ (True -> True) |
238 ]] | 238 ]] |
239 | 239 |
240 It is worth considering how the reflective tactic improves on a pure-Ltac implementation. The formula reification process is just as ad-hoc as before, so we gain little there. In general, proofs will be more complicated than formula translation, and the %``%#"#generic proof rule#"#%''% that we apply here %\textit{%#<i>#is#</i>#%}% on much better formal footing than a recursive Ltac function. The dependent type of the proof guarantees that it %``%#"#works#"#%''% on any input formula. This is all in addition to the proof-size improvement that we have already seen. | 240 It is worth considering how the reflective tactic improves on a pure-Ltac implementation. The formula reification process is just as ad-hoc as before, so we gain little there. In general, proofs will be more complicated than formula translation, and the %``%#"#generic proof rule#"#%''% that we apply here _is_ on much better formal footing than a recursive Ltac function. The dependent type of the proof guarantees that it %``%#"#works#"#%''% on any input formula. This is all in addition to the proof-size improvement that we have already seen. |
241 | 241 |
242 It may also be worth pointing out that our previous example of evenness testing used a function [partialOut] for sound handling of input goals that the verified decision procedure fails to prove. Here, we prove that our procedure [tautTrue] (recall that an inductive proof may be viewed as a recursive procedure) is able to prove any goal representable in [taut], so no extra step is necessary. *) | 242 It may also be worth pointing out that our previous example of evenness testing used a function [partialOut] for sound handling of input goals that the verified decision procedure fails to prove. Here, we prove that our procedure [tautTrue] (recall that an inductive proof may be viewed as a recursive procedure) is able to prove any goal representable in [taut], so no extra step is necessary. *) |
243 | 243 |
244 | 244 |
245 (** * A Monoid Expression Simplifier *) | 245 (** * A Monoid Expression Simplifier *) |
783 constr:(Abs (fun x => r1 x)) | 783 constr:(Abs (fun x => r1 x)) |
784 | 784 |
785 | _ => constr:(Const e) | 785 | _ => constr:(Const e) |
786 end. | 786 end. |
787 | 787 |
788 (** Recall that a regular Ltac pattern variable [?X] only matches terms that %\emph{%#<i>#do not mention new variables introduced within the pattern#</i>#%}%. In our naive implementation, the case for matching function abstractions matches the function body in a way that prevents it from mentioning the function argument! Our code above plays fast and loose with the function body in a way that leads to independent problems, but we could change the code so that it indeed handles function abstractions that ignore their arguments. | 788 (** Recall that a regular Ltac pattern variable [?X] only matches terms that _do not mention new variables introduced within the pattern_. In our naive implementation, the case for matching function abstractions matches the function body in a way that prevents it from mentioning the function argument! Our code above plays fast and loose with the function body in a way that leads to independent problems, but we could change the code so that it indeed handles function abstractions that ignore their arguments. |
789 | 789 |
790 To handle functions in general, we will use the pattern variable form [@?X], which allows [X] to mention newly introduced variables that are declared explicitly. For instance: *) | 790 To handle functions in general, we will use the pattern variable form [@?X], which allows [X] to mention newly introduced variables that are declared explicitly. For instance: *) |
791 | 791 |
792 Reset refl'. | 792 Reset refl'. |
793 Ltac refl' e := | 793 Ltac refl' e := |
802 constr:(Abs r1) | 802 constr:(Abs r1) |
803 | 803 |
804 | _ => constr:(Const e) | 804 | _ => constr:(Const e) |
805 end. | 805 end. |
806 | 806 |
807 (** Now, in the abstraction case, we bind [E1] as a function from an [x] value to the value of the abstraction body. Unfortunately, our recursive call there is not destined for success. It will match the same abstraction pattern and trigger another recursive call, and so on through infinite recursion. One last refactoring yields a working procedure. The key idea is to consider every input to [refl'] as %\emph{%#<i>#a function over the values of variables introduced during recursion#</i>#%}%. *) | 807 (** Now, in the abstraction case, we bind [E1] as a function from an [x] value to the value of the abstraction body. Unfortunately, our recursive call there is not destined for success. It will match the same abstraction pattern and trigger another recursive call, and so on through infinite recursion. One last refactoring yields a working procedure. The key idea is to consider every input to [refl'] as _a function over the values of variables introduced during recursion_. *) |
808 | 808 |
809 Reset refl'. | 809 Reset refl'. |
810 Ltac refl' e := | 810 Ltac refl' e := |
811 match eval simpl in e with | 811 match eval simpl in e with |
812 | fun x : ?T => @?E1 x + @?E2 x => | 812 | fun x : ?T => @?E1 x + @?E2 x => |