Mercurial > cpdt > repo
comparison src/Match.v @ 135:091583baf345
Through completer'
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Sun, 26 Oct 2008 14:11:41 -0400 |
parents | f9d8f33c9b46 |
children | d6b1e9de7fc1 |
comparison
equal
deleted
inserted
replaced
134:f9d8f33c9b46 | 135:091583baf345 |
---|---|
182 -> f x = f (f (f y)). | 182 -> f x = f (f (f y)). |
183 intros; autorewrite with my_db in *; assumption. | 183 intros; autorewrite with my_db in *; assumption. |
184 Qed. | 184 Qed. |
185 | 185 |
186 End autorewrite. | 186 End autorewrite. |
187 | |
188 | |
189 (** * Ltac Programming Basics *) | |
190 | |
191 (** We have already seen many examples of Ltac programs. In the rest of this chapter, we attempt to give a more principled introduction to the important features and design patterns. | |
192 | |
193 One common use for [match] tactics is identification of subjects for case analysis, as we see in this tactic definition. *) | |
194 | |
195 Ltac find_if := | |
196 match goal with | |
197 | [ |- if ?X then _ else _ ] => destruct X | |
198 end. | |
199 | |
200 (** The tactic checks if the conclusion is an [if], [destruct]ing the test expression if so. Certain classes of theorem are trivial to prove automatically with such a tactic. *) | |
201 | |
202 Theorem hmm : forall (a b c : bool), | |
203 if a | |
204 then if b | |
205 then True | |
206 else True | |
207 else if c | |
208 then True | |
209 else True. | |
210 intros; repeat find_if; constructor. | |
211 Qed. | |
212 | |
213 (** The [repeat] that we use here is called a %\textit{%#<i>#tactical#</i>#%}%, or tactic combinator. The behavior of [repeat t] is to loop through running [t], running [t] on all generated subgoals, running [t] on %\textit{%#<i>#their#</i>#%}% generated subgoals, and so on. When [t] fails at any point in this search tree, that particular subgoal is left to be handled by later tactics. Thus, it is important never to use [repeat] with a tactic that always succeeds. | |
214 | |
215 Another very useful Ltac building block is %\textit{%#<i>#context patterns#</i>#%}%. *) | |
216 | |
217 Ltac find_if_inside := | |
218 match goal with | |
219 | [ |- context[if ?X then _ else _] ] => destruct X | |
220 end. | |
221 | |
222 (** The behavior of this tactic is to find any subterm of the conclusion that is an [if] and then [destruct] the test expression. This version subsumes [find_if]. *) | |
223 | |
224 Theorem hmm' : forall (a b c : bool), | |
225 if a | |
226 then if b | |
227 then True | |
228 else True | |
229 else if c | |
230 then True | |
231 else True. | |
232 intros; repeat find_if_inside; constructor. | |
233 Qed. | |
234 | |
235 (** We can also use [find_if_inside] to prove goals that [find_if] does not simplify sufficiently. *) | |
236 | |
237 Theorem duh2 : forall (a b : bool), | |
238 (if a then 42 else 42) = (if b then 42 else 42). | |
239 intros; repeat find_if_inside; reflexivity. | |
240 Qed. | |
241 | |
242 (** Many decision procedures can be coded in Ltac via "[repeat match] loops." For instance, we can implement a subset of the functionality of [tauto]. *) | |
243 | |
244 Ltac my_tauto := | |
245 repeat match goal with | |
246 | [ H : ?P |- ?P ] => exact H | |
247 | |
248 | [ |- True ] => constructor | |
249 | [ |- _ /\ _ ] => constructor | |
250 | [ |- _ -> _ ] => intro | |
251 | |
252 | [ H : False |- _ ] => destruct H | |
253 | [ H : _ /\ _ |- _ ] => destruct H | |
254 | [ H : _ \/ _ |- _ ] => destruct H | |
255 | |
256 | [ H1 : ?P -> ?Q, H2 : ?P |- _ ] => | |
257 let H := fresh "H" in | |
258 generalize (H1 H2); clear H1; intro H | |
259 end. | |
260 | |
261 (** Since [match] patterns can share unification variables between hypothesis and conclusion patterns, it is easy to figure out when the conclusion matches a hypothesis. The [exact] tactic solves a goal completely when given a proof term of the proper type. | |
262 | |
263 It is also trivial to implement the "introduction rules" for a few of the connectives. Implementing elimination rules is only a little more work, since we must bind a name for a hypothesis to [destruct]. | |
264 | |
265 The last rule implements modus ponens. The most interesting part is the use of the Ltac-level [let] with a [fresh] expression. [fresh] takes in a name base and returns a fresh hypothesis variable based on that name. We use the new name variable [H] as the name we assign to the result of modus ponens. The use of [generalize] changes our conclusion to be an implication from [Q]. We clear the original hypothesis and move [Q] into the context with name [H]. *) | |
266 | |
267 Section propositional. | |
268 Variables P Q R : Prop. | |
269 | |
270 Theorem and_comm : (P \/ Q \/ False) /\ (P -> Q) -> True /\ Q. | |
271 my_tauto. | |
272 Qed. | |
273 End propositional. | |
274 | |
275 (** It was relatively easy to implement modus ponens, because we do not lose information by clearing every implication that we use. If we want to implement a similarly-complete procedure for quantifier instantiation, we need a way to ensure that a particular proposition is not already included among our hypotheses. To do that effectively, we first need to learn a bit more about the semantics of [match]. | |
276 | |
277 It is tempting to assume that [match] works like it does in ML. In fact, there are a few critical differences in its behavior. One is that we may include arbitrary expressions in patterns, instead of being restricted to variables and constructors. Another is that the same variable may appear multiple times, inducing an implicit equality constraint. | |
278 | |
279 There is a related pair of two other differences that are much more important than the others. [match] has a %\textit{%#<i>#backtracking semantics for failure#</i>#%}%. In ML, pattern matching works by finding the first pattern to match and then executing its body. If the body raises an exception, then the overall match raises the same exception. In Coq, failures in case bodies instead trigger continued search through the list of cases. | |
280 | |
281 For instance, this (unnecessarily verbose) proof script works: *) | |
282 | |
283 Theorem m1 : True. | |
284 match goal with | |
285 | [ |- _ ] => intro | |
286 | [ |- True ] => constructor | |
287 end. | |
288 Qed. | |
289 | |
290 (** The first case matches trivially, but its body tactic fails, since the conclusion does not begin with a quantifier or implication. In a similar ML match, that would mean that the whole pattern-match fails. In Coq, we backtrack and try the next pattern, which also matches. Its body tactic succeeds, so the overall tactic succeeds as well. | |
291 | |
292 The example shows how failure can move to a different pattern within a [match]. Failure can also trigger an attempt to find %\textit{%#<i>#a different way of matching a single pattern#</i>#%}%. Consider another example: *) | |
293 | |
294 Theorem m2 : forall P Q R : Prop, P -> Q -> R -> Q. | |
295 intros; match goal with | |
296 | [ H : _ |- _ ] => pose H | |
297 end. | |
298 (** [[ | |
299 | |
300 r := H1 : R | |
301 ============================ | |
302 Q | |
303 ]] | |
304 | |
305 By applying [pose], a convenient debugging tool for "leaking information out of [match]es," we see that this [match] first tries binding [H] to [H1], which cannot be used to prove [Q]. Nonetheless, the following variation on the tactic succeeds at proving the goal: *) | |
306 | |
307 match goal with | |
308 | [ H : _ |- _ ] => exact H | |
309 end. | |
310 Qed. | |
311 | |
312 (** The tactic first unifies [H] with [H1], as before, but [exact H] fails in that case, so the tactic engine searches for more possible values of [H]. Eventually, it arrives at the correct value, so that [exact H] and the overall tactic succeed. *) | |
313 | |
314 (** Now we are equipped to implement a tactic for checking that a proposition is not among our hypotheses: *) | |
315 | |
316 Ltac notHyp P := | |
317 match goal with | |
318 | [ _ : P |- _ ] => fail 1 | |
319 | _ => | |
320 match P with | |
321 | ?P1 /\ ?P2 => first [ notHyp P1 | notHyp P2 | fail 2 ] | |
322 | _ => idtac | |
323 end | |
324 end. | |
325 | |
326 (** We use the equality checking that is built into pattern-matching to see if there is a hypothesis that matches the proposition exactly. If so, we use the [fail] tactic. Without arguments, [fail] signals normal tactic failure, as you might expect. When [fail] is passed an argument [n], [n] is used to count outwards through the enclosing cases of backtracking search. In this case, [fail 1] says "fail not just in this pattern-matching branch, but for the whole [match]." The second case will never be tried when the [fail 1] is reached. | |
327 | |
328 This second case, used when [P] matches no hypothesis, checks if [P] is a conjunction. Other simplifications may have split conjunctions into their component formulas, so we need to check that at least one of those components is also not represented. To achieve this, we apply the [first] tactical, which takes a list of tactics and continues down the list until one of them does not fail. The [fail 2] at the end says to [fail] both the [first] and the [match] wrapped around it. | |
329 | |
330 The body of the [?P1 /\ ?P2] case guarantees that, if it is reached, we either succeed completely or fail completely. Thus, if we reach the wildcard case, [P] is not a conjunction. We use [idtac], a tactic that would be silly to apply on its own, since its effect is to succeed at doing nothing. Nonetheless, [idtac] is a useful placeholder for cases like what we see here. | |
331 | |
332 With the non-presence check implemented, it is easy to build a tactic that takes as input a proof term and adds its conclusion as a new hypothesis, only if that conclusion is not already present, failing otherwise. *) | |
333 | |
334 Ltac extend pf := | |
335 let t := type of pf in | |
336 notHyp t; generalize pf; intro. | |
337 | |
338 (** We see the useful [type of] operator of Ltac. This operator could not be implemented in Gallina, but it is easy to support in Ltac. We end up with [t] bound to the type of [pf]. We check that [t] is not already present. If so, we use a [generalize]/[intro] combo to add a new hypothesis proved by [pf]. | |
339 | |
340 With these tactics defined, we can write a tactic [completer] for adding to the context all consequences of a set of simple first-order formulas. *) | |
341 | |
342 Ltac completer := | |
343 repeat match goal with | |
344 | [ |- _ /\ _ ] => constructor | |
345 | [ H : _ /\ _ |- _ ] => destruct H | |
346 | [ H : ?P -> ?Q, H' : ?P |- _ ] => | |
347 generalize (H H'); clear H; intro H | |
348 | [ |- forall x, _ ] => intro | |
349 | |
350 | [ H : forall x, ?P x -> _, H' : ?P ?X |- _ ] => | |
351 extend (H X H') | |
352 end. | |
353 | |
354 (** We use the same kind of conjunction and implication handling as previously. Note that, since [->] is the special non-dependent case of [forall], the fourth rule handles [intro] for implications, too. | |
355 | |
356 In the fifth rule, when we find a [forall] fact [H] with a premise matching one of our hypotheses, we add the appropriate instantiation of [H]'s conclusion, if we have not already added it. | |
357 | |
358 We can check that [completer] is working properly: *) | |
359 | |
360 Section firstorder. | |
361 Variable A : Set. | |
362 Variables P Q R S : A -> Prop. | |
363 | |
364 Hypothesis H1 : forall x, P x -> Q x /\ R x. | |
365 Hypothesis H2 : forall x, R x -> S x. | |
366 | |
367 Theorem fo : forall x, P x -> S x. | |
368 completer. | |
369 (** [[ | |
370 | |
371 x : A | |
372 H : P x | |
373 H0 : Q x | |
374 H3 : R x | |
375 H4 : S x | |
376 ============================ | |
377 S x | |
378 ]] *) | |
379 | |
380 assumption. | |
381 Qed. | |
382 End firstorder. | |
383 | |
384 (** We narrowly avoided a subtle pitfall in our definition of [completer]. Let us try another definition that even seems preferable to the original, to the untrained eye. *) | |
385 | |
386 Ltac completer' := | |
387 repeat match goal with | |
388 | [ |- _ /\ _ ] => constructor | |
389 | [ H : _ /\ _ |- _ ] => destruct H | |
390 | [ H : ?P -> _, H' : ?P |- _ ] => | |
391 generalize (H H'); clear H; intro H | |
392 | [ |- forall x, _ ] => intro | |
393 | |
394 | [ H : forall x, ?P x -> _, H' : ?P ?X |- _ ] => | |
395 extend (H X H') | |
396 end. | |
397 | |
398 (** The only difference is in the modus ponens rule, where we have replaced an unused unification variable [?Q] with a wildcard. Let us try our example again with this version: *) | |
399 | |
400 Section firstorder'. | |
401 Variable A : Set. | |
402 Variables P Q R S : A -> Prop. | |
403 | |
404 Hypothesis H1 : forall x, P x -> Q x /\ R x. | |
405 Hypothesis H2 : forall x, R x -> S x. | |
406 | |
407 Theorem fo' : forall x, P x -> S x. | |
408 (** [[ | |
409 | |
410 completer'. | |
411 | |
412 Coq loops forever at this point. What went wrong? *) | |
413 Abort. | |
414 End firstorder'. |