Mercurial > cpdt > repo
comparison src/Match.v @ 431:85e743564b22
Pass through Match, to incorporate new coqdoc features
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Thu, 26 Jul 2012 14:35:34 -0400 |
parents | 078edca127cf |
children | 8077352044b2 |
comparison
equal
deleted
inserted
replaced
430:610568369aee | 431:85e743564b22 |
---|---|
24 | 24 |
25 (** A number of tactics are called repeatedly by [crush]. The %\index{tactics!intuition}%[intuition] tactic simplifies propositional structure of goals. The %\index{tactics!congruence}%[congruence] tactic applies the rules of equality and congruence closure, plus properties of constructors of inductive types. The %\index{tactics!omega}%[omega] tactic provides a complete decision procedure for a theory that is called %\index{linear arithmetic}%quantifier-free linear arithmetic or %\index{Presburger arithmetic}%Presburger arithmetic, depending on whom you ask. That is, [omega] proves any goal that follows from looking only at parts of that goal that can be interpreted as propositional formulas whose atomic formulas are basic comparison operations on natural numbers or integers, with operands built from constants, variables, addition, and subtraction (with multiplication by a constant available as a shorthand for addition or subtraction). | 25 (** A number of tactics are called repeatedly by [crush]. The %\index{tactics!intuition}%[intuition] tactic simplifies propositional structure of goals. The %\index{tactics!congruence}%[congruence] tactic applies the rules of equality and congruence closure, plus properties of constructors of inductive types. The %\index{tactics!omega}%[omega] tactic provides a complete decision procedure for a theory that is called %\index{linear arithmetic}%quantifier-free linear arithmetic or %\index{Presburger arithmetic}%Presburger arithmetic, depending on whom you ask. That is, [omega] proves any goal that follows from looking only at parts of that goal that can be interpreted as propositional formulas whose atomic formulas are basic comparison operations on natural numbers or integers, with operands built from constants, variables, addition, and subtraction (with multiplication by a constant available as a shorthand for addition or subtraction). |
26 | 26 |
27 The %\index{tactics!ring}%[ring] tactic solves goals by appealing to the axioms of rings or semi-rings (as in algebra), depending on the type involved. Coq developments may declare new types to be parts of rings and semi-rings by proving the associated axioms. There is a similar tactic [field] for simplifying values in fields by conversion to fractions over rings. Both [ring] and [field] can only solve goals that are equalities. The %\index{tactics!fourier}%[fourier] tactic uses Fourier's method to prove inequalities over real numbers, which are axiomatized in the Coq standard library. | 27 The %\index{tactics!ring}%[ring] tactic solves goals by appealing to the axioms of rings or semi-rings (as in algebra), depending on the type involved. Coq developments may declare new types to be parts of rings and semi-rings by proving the associated axioms. There is a similar tactic [field] for simplifying values in fields by conversion to fractions over rings. Both [ring] and [field] can only solve goals that are equalities. The %\index{tactics!fourier}%[fourier] tactic uses Fourier's method to prove inequalities over real numbers, which are axiomatized in the Coq standard library. |
28 | 28 |
29 The%\index{setoids}% _setoid_ facility makes it possible to register new equivalence relations to be understood by tactics like [rewrite]. For instance, [Prop] is registered as a setoid with the equivalence relation %``%#"#if and only if.#"#%''% The ability to register new setoids can be very useful in proofs of a kind common in math, where all reasoning is done after %``%#"#modding out by a relation.#"#%''% | 29 The%\index{setoids}% _setoid_ facility makes it possible to register new equivalence relations to be understood by tactics like [rewrite]. For instance, [Prop] is registered as a setoid with the equivalence relation "if and only if." The ability to register new setoids can be very useful in proofs of a kind common in math, where all reasoning is done after "modding out by a relation." |
30 | 30 |
31 There are several other built-in %``%#"#black box#"#%''% automation tactics, which one can learn about by perusing the Coq manual. The real promise of Coq, though, is in the coding of problem-specific tactics with Ltac. *) | 31 There are several other built-in "black box" automation tactics, which one can learn about by perusing the Coq manual. The real promise of Coq, though, is in the coding of problem-specific tactics with Ltac. *) |
32 | 32 |
33 | 33 |
34 (** * Ltac Programming Basics *) | 34 (** * Ltac Programming Basics *) |
35 | 35 |
36 (** We have already seen many examples of Ltac programs. In the rest of this chapter, we attempt to give a thorough introduction to the important features and design patterns. | 36 (** We have already seen many examples of Ltac programs. In the rest of this chapter, we attempt to give a thorough introduction to the important features and design patterns. |
92 (* begin thide *) | 92 (* begin thide *) |
93 intros; repeat find_if_inside; reflexivity. | 93 intros; repeat find_if_inside; reflexivity. |
94 Qed. | 94 Qed. |
95 (* end thide *) | 95 (* end thide *) |
96 | 96 |
97 (** Many decision procedures can be coded in Ltac via %``%#"#[repeat match] loops.#"#%''% For instance, we can implement a subset of the functionality of [tauto]. *) | 97 (** Many decision procedures can be coded in Ltac via "[repeat match] loops." For instance, we can implement a subset of the functionality of [tauto]. *) |
98 | 98 |
99 (* begin thide *) | 99 (* begin thide *) |
100 Ltac my_tauto := | 100 Ltac my_tauto := |
101 repeat match goal with | 101 repeat match goal with |
102 | [ H : ?P |- ?P ] => exact H | 102 | [ H : ?P |- ?P ] => exact H |
153 Theorem m2 : forall P Q R : Prop, P -> Q -> R -> Q. | 153 Theorem m2 : forall P Q R : Prop, P -> Q -> R -> Q. |
154 intros; match goal with | 154 intros; match goal with |
155 | [ H : _ |- _ ] => idtac H | 155 | [ H : _ |- _ ] => idtac H |
156 end. | 156 end. |
157 | 157 |
158 (** Coq prints %``%#"#[H1]#"#%''%. By applying %\index{tactics!idtac}%[idtac] with an argument, 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: *) | 158 (** Coq prints "[H1]". By applying %\index{tactics!idtac}%[idtac] with an argument, 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: *) |
159 | 159 |
160 (* begin thide *) | 160 (* begin thide *) |
161 match goal with | 161 match goal with |
162 | [ H : _ |- _ ] => exact H | 162 | [ H : _ |- _ ] => exact H |
163 end. | 163 end. |
178 | _ => idtac | 178 | _ => idtac |
179 end | 179 end |
180 end. | 180 end. |
181 (* end thide *) | 181 (* end thide *) |
182 | 182 |
183 (** 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 %\index{tactics!fail}%[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. | 183 (** 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 %\index{tactics!fail}%[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. |
184 | 184 |
185 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 %\index{tactics!first}%[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. | 185 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 %\index{tactics!first}%[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. |
186 | 186 |
187 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 %\index{tactics!idtac}%[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. | 187 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 %\index{tactics!idtac}%[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. |
188 | 188 |
306 Abort. | 306 Abort. |
307 (* end thide *) | 307 (* end thide *) |
308 | 308 |
309 (** The problem is that unification variables may not contain locally bound variables. In this case, [?P] would need to be bound to [x = x], which contains the local quantified variable [x]. By using a wildcard in the earlier version, we avoided this restriction. To understand why this applies to the [completer] tactics, recall that, in Coq, implication is shorthand for degenerate universal quantification where the quantified variable is not used. Nonetheless, in an Ltac pattern, Coq is happy to match a wildcard implication against a universal quantification. | 309 (** The problem is that unification variables may not contain locally bound variables. In this case, [?P] would need to be bound to [x = x], which contains the local quantified variable [x]. By using a wildcard in the earlier version, we avoided this restriction. To understand why this applies to the [completer] tactics, recall that, in Coq, implication is shorthand for degenerate universal quantification where the quantified variable is not used. Nonetheless, in an Ltac pattern, Coq is happy to match a wildcard implication against a universal quantification. |
310 | 310 |
311 The Coq 8.2 release includes a special pattern form for a unification variable with an explicit set of free variables. That unification variable is then bound to a function from the free variables to the %``%#"#real#"#%''% value. In Coq 8.1 and earlier, there is no such workaround. We will see an example of this fancier binding form in the next chapter. | 311 The Coq 8.2 release includes a special pattern form for a unification variable with an explicit set of free variables. That unification variable is then bound to a function from the free variables to the "real" value. In Coq 8.1 and earlier, there is no such workaround. We will see an example of this fancier binding form in the next chapter. |
312 | 312 |
313 No matter which Coq version you use, it is important to be aware of this restriction. As we have alluded to, the restriction is the culprit behind the infinite-looping behavior of [completer']. We unintentionally match quantified facts with the modus ponens rule, circumventing the %``%#"#already present#"#%''% check and leading to different behavior, where the same fact may be added to the context repeatedly in an infinite loop. Our earlier [completer] tactic uses a modus ponens rule that matches the implication conclusion with a variable, which blocks matching against non-trivial universal quantifiers. *) | 313 No matter which Coq version you use, it is important to be aware of this restriction. As we have alluded to, the restriction is the culprit behind the infinite-looping behavior of [completer']. We unintentionally match quantified facts with the modus ponens rule, circumventing the "already present" check and leading to different behavior, where the same fact may be added to the context repeatedly in an infinite loop. Our earlier [completer] tactic uses a modus ponens rule that matches the implication conclusion with a variable, which blocks matching against non-trivial universal quantifiers. *) |
314 | 314 |
315 | 315 |
316 (** * Functional Programming in Ltac *) | 316 (** * Functional Programming in Ltac *) |
317 | 317 |
318 (* EX: Write a list length function in Ltac. *) | 318 (* EX: Write a list length function in Ltac. *) |
345 | 345 |
346 << | 346 << |
347 Error: The reference S was not found in the current environment | 347 Error: The reference S was not found in the current environment |
348 >> | 348 >> |
349 | 349 |
350 The problem is that Ltac treats the expression [S (length ls')] as an invocation of a tactic [S] with argument [length ls']. We need to use a special annotation to %``%#"#escape into#"#%''% the Gallina parsing nonterminal.%\index{tactics!constr}% *) | 350 The problem is that Ltac treats the expression [S (length ls')] as an invocation of a tactic [S] with argument [length ls']. We need to use a special annotation to "escape into" the Gallina parsing nonterminal.%\index{tactics!constr}% *) |
351 | 351 |
352 (* begin thide *) | 352 (* begin thide *) |
353 Ltac length ls := | 353 Ltac length ls := |
354 match ls with | 354 match ls with |
355 | nil => O | 355 | nil => O |
398 | 398 |
399 Abort. | 399 Abort. |
400 (* end thide *) | 400 (* end thide *) |
401 | 401 |
402 (* EX: Write a list map function in Ltac. *) | 402 (* EX: Write a list map function in Ltac. *) |
403 | |
404 (* begin hide *) | |
405 Definition mapp := (map, list). | |
406 (* end hide *) | |
403 | 407 |
404 (** We can also use anonymous function expressions and local function definitions in Ltac, as this example of a standard list [map] function shows. *) | 408 (** We can also use anonymous function expressions and local function definitions in Ltac, as this example of a standard list [map] function shows. *) |
405 | 409 |
406 (* begin thide *) | 410 (* begin thide *) |
407 Ltac map T f := | 411 Ltac map T f := |
415 end in | 419 end in |
416 map'. | 420 map'. |
417 | 421 |
418 (** Ltac functions can have no implicit arguments. It may seem surprising that we need to pass [T], the carried type of the output list, explicitly. We cannot just use [type of f], because [f] is an Ltac term, not a Gallina term, and Ltac programs are dynamically typed. The function [f] could use very syntactic methods to decide to return differently typed terms for different inputs. We also could not replace [constr:(@nil T)] with [constr:nil], because we have no strongly typed context to use to infer the parameter to [nil]. Luckily, we do have sufficient context within [constr:(x' :: ls'')]. | 422 (** Ltac functions can have no implicit arguments. It may seem surprising that we need to pass [T], the carried type of the output list, explicitly. We cannot just use [type of f], because [f] is an Ltac term, not a Gallina term, and Ltac programs are dynamically typed. The function [f] could use very syntactic methods to decide to return differently typed terms for different inputs. We also could not replace [constr:(@nil T)] with [constr:nil], because we have no strongly typed context to use to infer the parameter to [nil]. Luckily, we do have sufficient context within [constr:(x' :: ls'')]. |
419 | 423 |
420 Sometimes we need to employ the opposite direction of %``%#"#nonterminal escape,#"#%''% when we want to pass a complicated tactic expression as an argument to another tactic, as we might want to do in invoking [map]. *) | 424 Sometimes we need to employ the opposite direction of "nonterminal escape," when we want to pass a complicated tactic expression as an argument to another tactic, as we might want to do in invoking %\coqdocvar{%#<tt>#map#</tt>#%}%. *) |
421 | 425 |
422 Goal False. | 426 Goal False. |
423 let ls := map (nat * nat)%type ltac:(fun x => constr:(x, x)) (1 :: 2 :: 3 :: nil) in | 427 let ls := map (nat * nat)%type ltac:(fun x => constr:(x, x)) (1 :: 2 :: 3 :: nil) in |
424 pose ls. | 428 pose ls. |
425 (** [[ | 429 (** [[ |
430 *) | 434 *) |
431 | 435 |
432 Abort. | 436 Abort. |
433 (* end thide *) | 437 (* end thide *) |
434 | 438 |
435 (** Each position within an Ltac script has a default applicable non-terminal, where [constr] and [ltac] are the main options worth thinking about, standing respectively for terms of Gallina and Ltac. The explicit colon notation can always be used to override the default non-terminal choice, though code being parsed as Gallina can no longer use such overrides. Within the [ltac] non-terminal, top-level function applications are treated as applications in Ltac, not Gallina; but the _arguments_ to such functions are parsed with [constr] by default. This choice may seem strange, until we realize that we have been relying on it all along in all the proof scripts we write! For instance, the [apply] tactic is an Ltac function, and it is natural to interpret its argument as a term of Gallina, not Ltac. We use an [ltac] prefix to parse Ltac function arguments as Ltac terms themselves, as in the call to [map] above. For some simple cases, Ltac terms may be passed without an extra prefix. For instance, an identifier that has an Ltac meaning but no Gallina meaning will be interpreted in Ltac automatically. | 439 (** Each position within an Ltac script has a default applicable non-terminal, where [constr] and [ltac] are the main options worth thinking about, standing respectively for terms of Gallina and Ltac. The explicit colon notation can always be used to override the default non-terminal choice, though code being parsed as Gallina can no longer use such overrides. Within the [ltac] non-terminal, top-level function applications are treated as applications in Ltac, not Gallina; but the _arguments_ to such functions are parsed with [constr] by default. This choice may seem strange, until we realize that we have been relying on it all along in all the proof scripts we write! For instance, the [apply] tactic is an Ltac function, and it is natural to interpret its argument as a term of Gallina, not Ltac. We use an [ltac] prefix to parse Ltac function arguments as Ltac terms themselves, as in the call to %\coqdocvar{%#<tt>#map#</tt>#%}% above. For some simple cases, Ltac terms may be passed without an extra prefix. For instance, an identifier that has an Ltac meaning but no Gallina meaning will be interpreted in Ltac automatically. |
436 | 440 |
437 One other gotcha shows up when we want to debug our Ltac functional programs. We might expect the following code to work, to give us a version of [length] that prints a debug trace of the arguments it is called with. *) | 441 One other gotcha shows up when we want to debug our Ltac functional programs. We might expect the following code to work, to give us a version of %\coqdocvar{%#<tt>#length#</tt>#%}% that prints a debug trace of the arguments it is called with. *) |
438 | 442 |
439 (* begin thide *) | 443 (* begin thide *) |
440 Reset length. | 444 Reset length. |
441 | 445 |
442 Ltac length ls := | 446 Ltac length ls := |
459 << | 463 << |
460 Error: variable n should be bound to a term. | 464 Error: variable n should be bound to a term. |
461 >> *) | 465 >> *) |
462 Abort. | 466 Abort. |
463 | 467 |
464 (** What is going wrong here? The answer has to do with the dual status of Ltac as both a purely functional and an imperative programming language. The basic programming language is purely functional, but tactic scripts are one %``%#"#datatype#"#%''% that can be returned by such programs, and Coq will run such a script using an imperative semantics that mutates proof states. Readers familiar with %\index{monad}\index{Haskell}%monadic programming in Haskell%~\cite{Monads,IO}% may recognize a similarity. Side-effecting Haskell programs can be thought of as pure programs that return _the code of programs in an imperative language_, where some out-of-band mechanism takes responsibility for running these derived programs. In this way, Haskell remains pure, while supporting usual input-output side effects and more. Ltac uses the same basic mechanism, but in a dynamically typed setting. Here the embedded imperative language includes all the tactics we have been applying so far. | 468 (** What is going wrong here? The answer has to do with the dual status of Ltac as both a purely functional and an imperative programming language. The basic programming language is purely functional, but tactic scripts are one "datatype" that can be returned by such programs, and Coq will run such a script using an imperative semantics that mutates proof states. Readers familiar with %\index{monad}\index{Haskell}%monadic programming in Haskell%~\cite{Monads,IO}% may recognize a similarity. Side-effecting Haskell programs can be thought of as pure programs that return _the code of programs in an imperative language_, where some out-of-band mechanism takes responsibility for running these derived programs. In this way, Haskell remains pure, while supporting usual input-output side effects and more. Ltac uses the same basic mechanism, but in a dynamically typed setting. Here the embedded imperative language includes all the tactics we have been applying so far. |
465 | 469 |
466 Even basic [idtac] is an embedded imperative program, so we may not automatically mix it with purely functional code. In fact, a semicolon operator alone marks a span of Ltac code as an embedded tactic script. This makes some amount of sense, since pure functional languages have no need for sequencing: since they lack side effects, there is no reason to run an expression and then just throw away its value and move on to another expression. | 470 Even basic [idtac] is an embedded imperative program, so we may not automatically mix it with purely functional code. In fact, a semicolon operator alone marks a span of Ltac code as an embedded tactic script. This makes some amount of sense, since pure functional languages have no need for sequencing: since they lack side effects, there is no reason to run an expression and then just throw away its value and move on to another expression. |
467 | 471 |
468 The solution is like in Haskell: we must %``%#"#monadify#"#%''% our pure program to give it access to side effects. The trouble is that the embedded tactic language has no [return] construct. Proof scripts are about proving theorems, not calculating results. We can apply a somewhat awkward workaround that requires translating our program into%\index{continuation-passing style}% _continuation-passing style_ %\cite{continuations}%, a program structuring idea popular in functional programming. *) | 472 The solution is like in Haskell: we must "monadify" our pure program to give it access to side effects. The trouble is that the embedded tactic language has no [return] construct. Proof scripts are about proving theorems, not calculating results. We can apply a somewhat awkward workaround that requires translating our program into%\index{continuation-passing style}% _continuation-passing style_ %\cite{continuations}%, a program structuring idea popular in functional programming. *) |
469 | 473 |
470 Reset length. | 474 Reset length. |
471 | 475 |
472 Ltac length ls k := | 476 Ltac length ls k := |
473 idtac ls; | 477 idtac ls; |
475 | nil => k O | 479 | nil => k O |
476 | _ :: ?ls' => length ls' ltac:(fun n => k (S n)) | 480 | _ :: ?ls' => length ls' ltac:(fun n => k (S n)) |
477 end. | 481 end. |
478 (* end thide *) | 482 (* end thide *) |
479 | 483 |
480 (** The new [length] takes a new input: a _continuation_ [k], which is a function to be called to continue whatever proving process we were in the middle of when we called [length]. The argument passed to [k] may be thought of as the return value of [length]. *) | 484 (** The new [length] takes a new input: a _continuation_ [k], which is a function to be called to continue whatever proving process we were in the middle of when we called %\coqdocvar{%#<tt>#length#</tt>#%}%. The argument passed to [k] may be thought of as the return value of %\coqdocvar{%#<tt>#length#</tt>#%}%. *) |
481 | 485 |
482 (* begin thide *) | 486 (* begin thide *) |
483 Goal False. | 487 Goal False. |
484 length (1 :: 2 :: 3 :: nil) ltac:(fun n => pose n). | 488 length (1 :: 2 :: 3 :: nil) ltac:(fun n => pose n). |
485 (** [[ | 489 (** [[ |
492 Abort. | 496 Abort. |
493 (* end thide *) | 497 (* end thide *) |
494 | 498 |
495 (** We see exactly the trace of function arguments that we expected initially, and an examination of the proof state afterward would show that variable [n] has been added with value [3]. | 499 (** We see exactly the trace of function arguments that we expected initially, and an examination of the proof state afterward would show that variable [n] has been added with value [3]. |
496 | 500 |
497 Considering the comparison with Haskell's IO monad, there is an important subtlety that deserves to be mentioned. A Haskell IO computation represents (theoretically speaking, at least) a transformer from one state of the real world to another, plus a pure value to return. Some of the state can be very specific to the program, as in the case of heap-allocated mutable references, but some can be along the lines of the favorite example %``%#"#launch missile,#"#%''% where the program has a side effect on the real world that is not possible to undo. | 501 Considering the comparison with Haskell's IO monad, there is an important subtlety that deserves to be mentioned. A Haskell IO computation represents (theoretically speaking, at least) a transformer from one state of the real world to another, plus a pure value to return. Some of the state can be very specific to the program, as in the case of heap-allocated mutable references, but some can be along the lines of the favorite example "launch missile," where the program has a side effect on the real world that is not possible to undo. |
498 | 502 |
499 In contrast, Ltac scripts can be thought of as controlling just two simple kinds of mutable state. First, there is the current sequence of proof subgoals. Second, there is a partial assignment of discovered values to unification variables introduced by proof search (for instance, by [eauto], as we saw in the previous chapter). Crucially, _every mutation of this state can be undone_ during backtracking introduced by [match], [auto], and other built-in Ltac constructs. Ltac proof scripts have state, but it is purely local, and all changes to it are reversible, which is a very useful semantics for proof search. *) | 503 In contrast, Ltac scripts can be thought of as controlling just two simple kinds of mutable state. First, there is the current sequence of proof subgoals. Second, there is a partial assignment of discovered values to unification variables introduced by proof search (for instance, by [eauto], as we saw in the previous chapter). Crucially, _every mutation of this state can be undone_ during backtracking introduced by [match], [auto], and other built-in Ltac constructs. Ltac proof scripts have state, but it is purely local, and all changes to it are reversible, which is a very useful semantics for proof search. *) |
500 | 504 |
501 | 505 |
502 (** * Recursive Proof Search *) | 506 (** * Recursive Proof Search *) |
503 | 507 |
504 (** Deciding how to instantiate quantifiers is one of the hardest parts of automated first-order theorem proving. For a given problem, we can consider all possible bounded-length sequences of quantifier instantiations, applying only propositional reasoning at the end. This is probably a bad idea for almost all goals, but it makes for a nice example of recursive proof search procedures in Ltac. | 508 (** Deciding how to instantiate quantifiers is one of the hardest parts of automated first-order theorem proving. For a given problem, we can consider all possible bounded-length sequences of quantifier instantiations, applying only propositional reasoning at the end. This is probably a bad idea for almost all goals, but it makes for a nice example of recursive proof search procedures in Ltac. |
505 | 509 |
506 We can consider the maximum %``%#"#dependency chain#"#%''% length for a first-order proof. We define the chain length for a hypothesis to be 0, and the chain length for an instantiation of a quantified fact to be one greater than the length for that fact. The tactic [inster n] is meant to try all possible proofs with chain length at most [n]. *) | 510 We can consider the maximum "dependency chain" length for a first-order proof. We define the chain length for a hypothesis to be 0, and the chain length for an instantiation of a quantified fact to be one greater than the length for that fact. The tactic [inster n] is meant to try all possible proofs with chain length at most [n]. *) |
507 | 511 |
508 (* begin thide *) | 512 (* begin thide *) |
509 Ltac inster n := | 513 Ltac inster n := |
510 intuition; | 514 intuition; |
511 match n with | 515 match n with |
538 Theorem test_inster2 : forall x y, x <> y -> P x -> Q (f y) -> Q (f x). | 542 Theorem test_inster2 : forall x y, x <> y -> P x -> Q (f y) -> Q (f x). |
539 inster 3. | 543 inster 3. |
540 Qed. | 544 Qed. |
541 End test_inster. | 545 End test_inster. |
542 | 546 |
543 (** The style employed in the definition of [inster] can seem very counterintuitive to functional programmers. Usually, functional programs accumulate state changes in explicit arguments to recursive functions. In Ltac, the state of the current subgoal is always implicit. Nonetheless, recalling the discussion at the end of the last section, in contrast to general imperative programming, it is easy to undo any changes to this state, and indeed such %``%#"#undoing#"#%''% happens automatically at failures within [match]es. In this way, Ltac programming is similar to programming in Haskell with a stateful failure monad that supports a composition operator along the lines of the [first] tactical. | 547 (** The style employed in the definition of [inster] can seem very counterintuitive to functional programmers. Usually, functional programs accumulate state changes in explicit arguments to recursive functions. In Ltac, the state of the current subgoal is always implicit. Nonetheless, recalling the discussion at the end of the last section, in contrast to general imperative programming, it is easy to undo any changes to this state, and indeed such "undoing" happens automatically at failures within [match]es. In this way, Ltac programming is similar to programming in Haskell with a stateful failure monad that supports a composition operator along the lines of the [first] tactical. |
544 | 548 |
545 Functional programming purists may react indignantly to the suggestion of programming this way. Nonetheless, as with other kinds of %``%#"#monadic programming,#"#%''% many problems are much simpler to solve with Ltac than they would be with explicit, pure proof manipulation in ML or Haskell. To demonstrate, we will write a basic simplification procedure for logical implications. | 549 Functional programming purists may react indignantly to the suggestion of programming this way. Nonetheless, as with other kinds of "monadic programming," many problems are much simpler to solve with Ltac than they would be with explicit, pure proof manipulation in ML or Haskell. To demonstrate, we will write a basic simplification procedure for logical implications. |
546 | 550 |
547 This procedure is inspired by one for separation logic%~\cite{separation}%, where conjuncts in formulas are thought of as %``%#"#resources,#"#%''% such that we lose no completeness by %``%#"#crossing out#"#%''% equal conjuncts on the two sides of an implication. This process is complicated by the fact that, for reasons of modularity, our formulas can have arbitrary nested tree structure (branching at conjunctions) and may include existential quantifiers. It is helpful for the matching process to %``%#"#go under#"#%''% quantifiers and in fact decide how to instantiate existential quantifiers in the conclusion. | 551 This procedure is inspired by one for separation logic%~\cite{separation}%, where conjuncts in formulas are thought of as "resources," such that we lose no completeness by "crossing out" equal conjuncts on the two sides of an implication. This process is complicated by the fact that, for reasons of modularity, our formulas can have arbitrary nested tree structure (branching at conjunctions) and may include existential quantifiers. It is helpful for the matching process to "go under" quantifiers and in fact decide how to instantiate existential quantifiers in the conclusion. |
548 | 552 |
549 To distinguish the implications that our tactic handles from the implications that will show up as %``%#"#plumbing#"#%''% in various lemmas, we define a wrapper definition, a notation, and a tactic. *) | 553 To distinguish the implications that our tactic handles from the implications that will show up as "plumbing" in various lemmas, we define a wrapper definition, a notation, and a tactic. *) |
550 | 554 |
551 Definition imp (P1 P2 : Prop) := P1 -> P2. | 555 Definition imp (P1 P2 : Prop) := P1 -> P2. |
552 Infix "-->" := imp (no associativity, at level 95). | 556 Infix "-->" := imp (no associativity, at level 95). |
553 Ltac imp := unfold imp; firstorder. | 557 Ltac imp := unfold imp; firstorder. |
554 | 558 |
600 (R --> P /\ Q) | 604 (R --> P /\ Q) |
601 -> (R --> Q /\ P). | 605 -> (R --> Q /\ P). |
602 imp. | 606 imp. |
603 Qed. | 607 Qed. |
604 | 608 |
605 (** The first order of business in crafting our [matcher] tactic will be auxiliary support for searching through formula trees. The [search_prem] tactic implements running its tactic argument [tac] on every subformula of an [imp] premise. As it traverses a tree, [search_prem] applies some of the above lemmas to rewrite the goal to bring different subformulas to the head of the goal. That is, for every subformula [P] of the implication premise, we want [P] to %``%#"#have a turn,#"#%''% where the premise is rearranged into the form [P /\ Q] for some [Q]. The tactic [tac] should expect to see a goal in this form and focus its attention on the first conjunct of the premise. *) | 609 (** The first order of business in crafting our [matcher] tactic will be auxiliary support for searching through formula trees. The [search_prem] tactic implements running its tactic argument [tac] on every subformula of an [imp] premise. As it traverses a tree, [search_prem] applies some of the above lemmas to rewrite the goal to bring different subformulas to the head of the goal. That is, for every subformula [P] of the implication premise, we want [P] to "have a turn," where the premise is rearranged into the form [P /\ Q] for some [Q]. The tactic [tac] should expect to see a goal in this form and focus its attention on the first conjunct of the premise. *) |
606 | 610 |
607 Ltac search_prem tac := | 611 Ltac search_prem tac := |
608 let rec search P := | 612 let rec search P := |
609 tac | 613 tac |
610 || (apply and_True_prem; tac) | 614 || (apply and_True_prem; tac) |
670 (Q --> P x /\ R) | 674 (Q --> P x /\ R) |
671 -> (Q --> ex P /\ R). | 675 -> (Q --> ex P /\ R). |
672 imp. | 676 imp. |
673 Qed. | 677 Qed. |
674 | 678 |
675 (** We will also want a %``%#"#base case#"#%''% lemma for finishing proofs where cancelation has removed every constituent of the conclusion. *) | 679 (** We will also want a "base case" lemma for finishing proofs where cancelation has removed every constituent of the conclusion. *) |
676 | 680 |
677 Theorem imp_True : forall P, | 681 Theorem imp_True : forall P, |
678 P --> True. | 682 P --> True. |
679 imp. | 683 imp. |
680 Qed. | 684 Qed. |
899 H0 : Q v2 | 903 H0 : Q v2 |
900 H' : Q v2 -> exists u : B, P v2 u |- Q v2] | 904 H' : Q v2 -> exists u : B, P v2 u |- Q v2] |
901 | 905 |
902 ]] | 906 ]] |
903 | 907 |
904 There is another similar line about a different existential variable. Here, %``%#"#existential variable#"#%''% means what we have also called %``%#"#unification variable.#"#%''% In the course of the proof, some unification variable [?4384] was introduced but never unified. Unification variables are just a device to structure proof search; the language of Gallina proof terms does not include them. Thus, we cannot produce a proof term without instantiating the variable. | 908 There is another similar line about a different existential variable. Here, "existential variable" means what we have also called "unification variable." In the course of the proof, some unification variable [?4384] was introduced but never unified. Unification variables are just a device to structure proof search; the language of Gallina proof terms does not include them. Thus, we cannot produce a proof term without instantiating the variable. |
905 | 909 |
906 The error message shows that [?4384] is meant to be a proof of [Q v2] in a particular proof state, whose variables and hypotheses are displayed. It turns out that [?4384] was created by [insterU], as the value of a proof to pass to [H1]. Recall that, in Gallina, implication is just a degenerate case of [forall] quantification, so the [insterU] code to match against [forall] also matched the implication. Since any proof of [Q v2] is as good as any other in this context, there was never any opportunity to use unification to determine exactly which proof is appropriate. We expect similar problems with any implications in arguments to [insterU]. *) | 910 The error message shows that [?4384] is meant to be a proof of [Q v2] in a particular proof state, whose variables and hypotheses are displayed. It turns out that [?4384] was created by [insterU], as the value of a proof to pass to [H1]. Recall that, in Gallina, implication is just a degenerate case of [forall] quantification, so the [insterU] code to match against [forall] also matched the implication. Since any proof of [Q v2] is as good as any other in this context, there was never any opportunity to use unification to determine exactly which proof is appropriate. We expect similar problems with any implications in arguments to [insterU]. *) |
907 | 911 |
908 Abort. | 912 Abort. |
909 End t7. | 913 End t7. |