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 =>