Require Le. Require Arith. (** * Short Introduction to Proof-Automation and Ltac *) (** ** Proofs are Programs *) (** The theorem [forall (P Q : Prop), (P -> Q) -> P -> Q /\ P] may be proven directly as a Gallina term: *) Definition l_proof : forall (P Q : Prop), (P -> Q) -> P -> Q /\ P := fun P Q H Hp => conj (H Hp) Hp. (** The more common way is to use tactics. The result is similar; tactics merely are an interactive way to write proof terms (aka programs) *) Lemma l_tactic : forall P Q : Prop, (P -> Q) -> P -> Q /\ P. Proof. (** construct a function with four arguments *) intros P Q H Hp. (** use [conj] *) split. (** first argument; [H] with argument [Hp] *) apply H. apply Hp. (** second argument *) apply Hp. Qed. (** The use of tactics in the above example correspond very closely to the structure the proof term. Tactics like [induction] and [inversion] do more work, also dependent on the current context; induction, for example, can automatically introduce variables. However, all tactics produce proof terms, that are then checked by Coq for consistency. Most basic tactics, however, need to be told on what objects in the current goal to operate. This can become cumbersome. It is also fragile, as a lot of objects in a proof are named automatically and it is practically infeasible to anticipate the naming in all cases. This is especially a problem when some definitions have to be changed, even if they do not really change the proof (e.g. reordering of arguments in constructors); if one relies on explicit naming in the tactics the old proofs will surely fail. *) (** ** The [auto] tactic *) (** One possibility to avoid cumbersome enumeration of every proof step, and the problem of explicit naming of tactic arguments are the automation tactics Coq provides. The most basic of these tactics is [auto]. It will try to prove any goal using backwards-reasoning: it examines the current goal and tries to find matching lemmas. Then it applies them in turn and repeats until the goal is solved. If it encounters a ``dead-end'' it backtracks to the last point where there are still choices left; in other words, it's a depth-first search. The lemmas that [auto] attempts to use are the hypothesis of the current goal and predefined sets of lemmas called hint databases. The exact procedure and more details can be found in the Coq manual: # http://coq.inria.fr/distrib/V8.4/refman/Reference-Manual011.html##toc64 # Let's see how [auto] works by some simple examples (taken from Software Foundations Chapter ``UseAuto'', which is available #here#): *) (** [auto] can do [reflexivity] *) Lemma solving_by_reflexivity : 2 + 3 = 5. Proof. auto. Qed. (** [auto] uses hypothesis *) Lemma solving_by_apply : forall (P Q : nat->Prop), (forall n, Q n -> P n) -> (forall n, Q n) -> P 2. Proof. auto. Qed. (** cf [[ intros P Q H H'. apply H. apply H'. ]] *) (** [auto] has limited search depth. The following fairly trivial fact is not solved by auto, because it by default only applies up to five lemmas subsequently, before doing backtracking *) Lemma search_depth_0 : True /\ True /\ True /\ True /\ True /\ True. Proof. auto. Admitted. (** Supplying a numeric argument to [auto] can increase the search depth limit: *) Lemma search_depth_0' : True /\ True /\ True /\ True /\ True /\ True. Proof. auto 10. Qed. (** ** Hints *) (** [auto] only uses the hypothesis and some standard lemmas; it can be extended though: *) (** Here the lemma [le_n_S] has to be applied by hand. *) Lemma simple_le : forall n m P, P -> (P -> n <= m) -> S n <= S m. auto. intros. apply Le.le_n_S. auto. Qed. (** We can tell [auto] to include it specifically: *) Lemma simple_le' : forall n m, n <= m -> S n <= S m. auto using Le.le_n_S. Qed. (** By the way, if one wants to see what proof [auto] comes up with, the following form can be used *) Lemma simple_le'' : forall n m, n <= m -> S n <= S m. info auto using Le.le_n_S. Qed. (** Another possibility is to use Hints. [Hint Resolve ] will make [auto] consider for all subsequent proofs. *) Hint Resolve Le.le_n_S. Lemma simple_le''' : forall n m, n <= m -> S n <= S m. auto. Qed. (** The same thing has to be done for constructors: *) Inductive someprop : nat -> Prop := | sp_5 : someprop 5 | sp_6 : someprop 6 | sp_sum : forall n, someprop n -> someprop ( S (S (S n))) . (** [auto] fails, as it does not know about [sp_5]. *) Example simple_someprop : someprop 5. auto. apply sp_5. Qed. (** This works: *) Hint Resolve sp_5. Example simple_someprop' : someprop 5. auto. Qed. (** .. but only for one constructor. *) Example simple_someprop2 : someprop 6. auto. apply sp_6. Qed. (** Here's a shortcut for adding all constructors of a type: *) Hint Constructors someprop. Example simple_someprop2' : someprop 6. Print Hint. auto. Qed. (** The Hints are that we added are all stored in a database called [core]. There are also other predefined databases that define lemmas for special purposes; a list of them can be found in the manual. There it is also explained how to create your own databases and add your own lemmas to them. The following is an example using the [arith] database, which includes a lot of lemmas for calculation; it can do similar things as [omega]. *) Example use_arith_db : forall n, n + 1 = 1 + n. auto. auto with arith. Qed. (** It is possible to inspect the (current) content of a hint database with the [Print Hint] command: *) Print HintDb arith. (** ** E-tactics *) (** The following simple fact is not solvable by auto. *) Lemma solving_by_eapply : forall (P Q : nat->Prop), (forall n m, Q m -> P n) -> Q 1 -> P 2. Proof. auto. Admitted. (** The reason is that it does not make any attempt to find a matching term for [m] (which should be [1]). Compared with the manual version of the proof [[ intros P Q H H'. apply H with (m:=1). auto. ]] one notes that [apply] only works by giving the value of [m] explicitly. There is an alternative to [apply] called [eapply] that postpones the need to instantiate [m]. [[ intros P Q H H'. eapply H . apply H'. ]] It works by introducing so called ``existential variables''. They act like wildcards, that have to be instantiated later in the proof. In the above case, it becomes clear that [m] needs to match with [1] in the next step. Existential variables are prefixed in the proof state with a question mark; try out the above example to see how it looks and works. The tactic [eauto] always uses [eapply] instead of [apply] and therefore is able to solve the simple fact from above: *) Lemma solving_by_eapply' : forall (P Q : nat->Prop), (forall n m, Q m -> P n) -> Q 1 -> P 2. Proof. eauto. Qed. (** [eauto] has also some drawbacks; it is slower than [auto] as it has more matching possibilities due to the existential variables... and it may still fail, when it makes wrong choices about instantiations. It is therefore preferable to use [eauto] only in situations where the possibilities for the existentials are rather constrained; e.g. in the example above, there was only one possibility. *) (** ** Ltac *) (** In addition to the tactics that Coq provide out of the box, it includes a quite powerful scripting language called Ltac, that allows to program custom tactics using existing ones as building blocks. You are encouraged to look at the Ltac manual at # http://coq.inria.fr/distrib/V8.4/refman/Reference-Manual012.html # for the exact syntax and semantics. This is particularly useful for automating proofs as [auto] and [eauto] have a significant shortcoming: they _only_ apply lemmas and never do rewriting or inversion. For example, most of the times when dealing with negation, inversion is key; like in the following example: *) (** [auto] cannot do inversions *) Lemma not_someprop: ~ (someprop 7). Proof. unfold not. intros. auto. (** [auto] can't do anything *) inversion H. subst. inversion H1. subst. inversion H2. Qed. (** so we do cumbersome and fragile inversions by hand. *) (** Examining the manual proof above, we can notice we inversion always on the freshest [someprop] result in the hypothesis. Let us define a tactic using Ltac that will do that for us: *) Ltac invert_someprop := match goal with | [H : someprop _ |- _ ] => inversion H; clear H; subst; simpl end. (** Here, we define a new tactic [invert_someprop]. The special match form [match goal with ...] checks if the current proof state matches the ``goal patterns'' in its body and then executes the corresponding tactics. Check the manual for the exact goal pattern syntax. In our case we match for a hypothesis that is a statement about [someprop] and bind it to the name [H] (this is the [H : someprop _] part). We also accept any goal (the [ |- _ ] part) and then use [inversion] on the bound name [H]. The following tactics clean up the proof state a bit. If you do not know them yet, you can read up on them in the Coq manual. # http://coq.inria.fr/distrib/V8.4/refman/Reference-Manual011.html # The semicolon between the tactics simply applies the right tactic to all the subgoals that the left tactic generated. Let's see how our custom tactic works: *) Lemma not_someprop': ~ (someprop 7). Proof. unfold not. intros. invert_someprop. invert_someprop. invert_someprop. Qed. (** We now always match the right thing to invert without having to state it's auto-generated name. However, we still have to manually apply the tactic three times. To fix this we will use a so called ``tactical''. A tactical can be viewed as a control structure of Ltac. One example is the semicolon from our definition of [invert_someprop]. Here we can use another one, [repeat], that will repeat a tactic until it either fails or solves the goal. *) Lemma not_someprop'': ~ (someprop 7). Proof. unfold not. intros. repeat invert_someprop. Qed. (* This makes our proof much shorter and more robust; it basically will work on all [someprop]s with a literal argument: *) Lemma not_someprop14: ~ (someprop 19). Proof. unfold not. intros. repeat invert_someprop. Qed. Lemma not_someprop101: ~ (someprop 106). Proof. unfold not. intros. repeat invert_someprop. Qed. (** If we want to use the invert_someprop tactic implicitly, we can add it as a special external hing: *) Hint Extern 4 => invert_someprop. (** Now [auto] will always also try to use the [invert_somprop] tactic: *) Lemma not_someprop''': ~ (someprop 7). Proof. auto. Qed. (** Here's another example where automatic inversion of impossible cases is useful *) Inductive non_empty {X : Type}: list X -> Prop := | ne_constr: forall (x:X) l, non_empty (x::l). Ltac invert_ne := match goal with | [ H: non_empty nil |- _ ] => solve [inversion H] end. (** [solve] is a tactical that just executes its argument but also fails, if the current goal was not solved *) Hint Extern 4 => invert_ne. Lemma llist: forall X (l: list X), non_empty l -> length l >= 1. Proof. intros X l. induction l; simpl; auto with core arith. Qed. (** Another special matching construct that is often useful is [context]. It may be used for matching hypothesis or goals and it matches if its argument (which has to be given in brackets) merely occurs in the goal, i.e. also subterms of the goals are matched In the following example, we want to eliminate occurrences of plus where the right hand side is a literal number (i.e. no variable). We do this by first ``shoving'' all successor constructors to the left using rewriting of [plus_n_Sm] and then by eliminating a leftover zero on the right hand side with [plus_n_O]: *) Ltac canonical_plus := match goal with | [ |- context [ _ + S _ ] ] => rewrite <- plus_n_Sm | [ |- context [_ + O] ] => rewrite <- plus_n_O end. Hint Extern 5 => repeat canonical_plus. Lemma someprop_or_not : forall n, someprop (S n) -> someprop (n + 7). Proof. intros; auto. Qed. (** Some final tips on getting started with automation: - start modest: try to automate little things at first; if you not know what you are doing you might get really unpredictable results - Don't spend to much time on automation if it does not work as expected; if you get stuck it's best to do the proof manually and then experiment to automate parts of it when you have time. - However, it is always beneficial to spend some extra keystrokes to use a simple goal match instead of writing the explicit auto-generated names in your tactics. That way you also will get good practice how matching really works (we just covered part of it here). - Always keep the Coq manual at hand and read the (unfortunately very concise) descriptions of the automation features that you use, when you encounter problems. *)