Library LibTactics
This file contains a set of tactics that extends the set of builtin
tactics provided with the standard distribution of Coq. It intends
to overcome a number of limitations of the standard set of tactics,
and thereby to help user to write shorter and more robust scripts.
Hopefully, Coq tactics will be improved as time goes by, and this file should ultimately be useless. In the meanwhile, you will probably find it very useful.
Hopefully, Coq tactics will be improved as time goes by, and this file should ultimately be useless. In the meanwhile, you will probably find it very useful.
The main features offered are:
- More convenient syntax for naming hypotheses, with tactics for introduction and inversion that take as input only the name of hypotheses of type Prop, rather than the name of all variables.
- Tactics providing true support for manipulating N-ary conjunctions, disjunctions and existentials, hidding the fact that the underlying implementation is based on binary predicates.
- Convenient support for automation: tactic followed with the symbol "~" or "*" will call automation on the generated subgoals. Symbol "~" stands for auto and "*" for intuition eauto. These bindings can be customized.
- Forward-chaining tactics are provided to instantiate lemmas either with variable or hypotheses or a mix of both.
- A more powerful implementation of apply is provided (it is based on refine and thus behaves better with respect to conversion).
- An improved inversion tactic which substitutes equalities on variables generated by the standard inversion mecanism. Moreover, it supports the elimination of dependently-typed equalities (requires axiom K, which is a weak form of Proof Irrelevance).
- An improved induction tactic that saves the relevant information by introducing equalities before doing the induction and substitutes these equality in each subgoal generated by induction.
- Tactics for saving time when writing proofs, with tactics to asserts hypotheses or sub-goals, and improved tactics for clearing, renaming, and sorting hypotheses.
External credits:
- thanks to Xavier Leroy for providing with the idea of tactic forward,
- thanks to Georges Gonthier for the implementation trick in applys,
- thanks to Hugo Herbelin for useful feedback on several tactics.
Set Implicit Arguments.
exist T1 ... TN, P is a shorthand for
exists T1, ..., exists TN, P.
Notation "'exist' x1 ',' P" :=
(exists x1, P)
(at level 200, x1 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 ',' P" :=
(exists x1, exists x2, P)
(at level 200, x1 ident, x2 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 x3 ',' P" :=
(exists x1, exists x2, exists x3, P)
(at level 200, x1 ident, x2 ident, x3 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 x3 x4 ',' P" :=
(exists x1, exists x2, exists x3, exists x4, P)
(at level 200, x1 ident, x2 ident, x3 ident, x4 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 x3 x4 x5 ',' P" :=
(exists x1, exists x2, exists x3, exists x4, exists x5, P)
(at level 200, x1 ident, x2 ident, x3 ident, x4 ident, x5 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 x3 x4 x5 x6 ',' P" :=
(exists x1, exists x2, exists x3, exists x4, exists x5, exists x6, P)
(at level 200, x1 ident, x2 ident, x3 ident, x4 ident, x5 ident,
x6 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 x3 x4 x5 x6 x7 ',' P" :=
(exists x1, exists x2, exists x3, exists x4, exists x5, exists x6,
exists x7, P)
(at level 200, x1 ident, x2 ident, x3 ident, x4 ident, x5 ident,
x6 ident, x7 ident,
right associativity) : type_scope.
Notation "'exist' x1 x2 x3 x4 x5 x6 x7 x8 ',' P" :=
(exists x1, exists x2, exists x3, exists x4, exists x5, exists x6,
exists x7, exists x8, P)
(at level 200, x1 ident, x2 ident, x3 ident, x4 ident, x5 ident,
x6 ident, x7 ident, x8 ident,
right associativity) : type_scope.
= x is a unary predicate which holds of values equal to x.
It simply denotes the partial application of equality.
Notation "'proj21' P" := (proj1 P) (at level 69, only parsing).
Notation "'proj22' P" := (proj2 P) (at level 69, only parsing).
Notation "'proj31' P" := (proj1 P) (at level 69).
Notation "'proj32' P" := (proj1 (proj2 P)) (at level 69).
Notation "'proj33' P" := (proj2 (proj2 P)) (at level 69).
Notation "'proj41' P" := (proj1 P) (at level 69).
Notation "'proj42' P" := (proj1 (proj2 P)) (at level 69).
Notation "'proj43' P" := (proj1 (proj2 (proj2 P))) (at level 69).
Notation "'proj44' P" := (proj2 (proj2 (proj2 P))) (at level 69).
Notation "'proj51' P" := (proj1 P) (at level 69).
Notation "'proj52' P" := (proj1 (proj2 P)) (at level 69).
Notation "'proj53' P" := (proj1 (proj2 (proj2 P))) (at level 69).
Notation "'proj54' P" := (proj1 (proj2 (proj2 (proj2 P)))) (at level 69).
Notation "'proj55' P" := (proj2 (proj2 (proj2 (proj2 P)))) (at level 69).
ltac_no_arg is a constant that can be used to simulate
optional arguments in tactic definitions.
Use mytactic ltac_no_arg on the tactic invokation,
and use match arg with ltac_no_arg => .. or
match type of arg with ltac_No_arg => .. to
test whether an argument was provided.
Ltac tactics are not allowed to both perform side-effect on the goal
and return a value in the same time. To work around this limitation,
we can use either the current goal or the proof context as a stack
to place return values. To avoid interferences, we box the return
values.
When the goal is used as stack, we use ltac_tag_result to box values. When the context is used, we use the type Carrier to box values.
When the goal is used as stack, we use ltac_tag_result to box values. When the context is used, we use the type Carrier to box values.
build_result E changes the goal from G to
ltac_tag_result T -> G where T is the type of E.
Ltac build_result t :=
match type of t with ?T =>
let H := fresh "TEMP" in
assert (H : ltac_tag_result T);
[ unfold ltac_tag_result; exact t | generalize H; clear H ]
end.
if_is_result is the identity on a goal of the form ltac_tag_result T -> G
and fails otherwise.
name_result H expects a of the form ltac_tag_result T -> G
and changes the goal to G by introducing an hypothesis H:T.
Tactic Notation "name_result" simple_intropattern(H) :=
match goal with |- ltac_tag_result _ -> _ =>
unfold ltac_tag_result at 1;
first [ intros H
| let H' := fresh "NameAlreadyUsed" in intros H'] end.
With the type Carrier, we implement the three following tactics:
- _put x is used to return a value (leaving it on the stack)
- _get is used to obtain the last returned value
- _rem is used to remove the last returned value from the context.
_put x adds on hypothesis of type Carrier x.
_put2 x y and _put3 x y z can be used for functions
that return pairs or triples of values.
Ltac _put x :=
generalize (carrier x); intro.
Ltac _put2 x y :=
_put y; _put x.
Ltac _put3 x y z :=
_put z; _put y; _put x.
_get returns the value x contained in the last hypothesis of
type Carrier x available in the context. If fails if there
is no such hypothesis.
_rem clears the last hypothesis of type Carrier _.
If fails if there is no such hypothesis.
ltac_wild is a constant that can be used to simulate
wildcard arguments in tactic definitions. Notation is __.
ltac_wilds is another constant that can be used to simulate
a sequence of N wildcards, with N chosen appropriately
depending on the context. Notation is ___.
Inductive ltac_Wilds : Set :=
| ltac_wilds : ltac_Wilds.
Notation "'___'" := ltac_wilds : ltac_scope.
Boxer is a datatype such that the type list Boxer can be used
to manipulate list of values in ltac.
Inductive Boxer : Type :=
| boxer : forall (A:Type), A -> Boxer.
Notation "'>>>'" :=
(@nil Boxer)
(at level 0)
: ltac_scope.
Notation "'>>>' v1" :=
((boxer v1)::nil)
(at level 0, v1 at level 0)
: ltac_scope.
Notation "'>>>' v1 v2" :=
((boxer v1)::(boxer v2)::nil)
(at level 0, v1 at level 0, v2 at level 0)
: ltac_scope.
Notation "'>>>' v1 v2 v3" :=
((boxer v1)::(boxer v2)::(boxer v3)::nil)
(at level 0, v1 at level 0, v2 at level 0, v3 at level 0)
: ltac_scope.
Notation "'>>>' v1 v2 v3 v4" :=
((boxer v1)::(boxer v2)::(boxer v3)::(boxer v4)::nil)
(at level 0, v1 at level 0, v2 at level 0, v3 at level 0,
v4 at level 0)
: ltac_scope.
Notation "'>>>' v1 v2 v3 v4 v5" :=
((boxer v1)::(boxer v2)::(boxer v3)::(boxer v4)::(boxer v5)::nil)
(at level 0, v1 at level 0, v2 at level 0, v3 at level 0,
v4 at level 0, v5 at level 0)
: ltac_scope.
Notation "'>>>' v1 v2 v3 v4 v5 v6" :=
((boxer v1)::(boxer v2)::(boxer v3)::(boxer v4)::(boxer v5)
::(boxer v6)::nil)
(at level 0, v1 at level 0, v2 at level 0, v3 at level 0,
v4 at level 0, v5 at level 0, v6 at level 0)
: ltac_scope.
Notation "'>>>' v1 v2 v3 v4 v5 v6 v7" :=
((boxer v1)::(boxer v2)::(boxer v3)::(boxer v4)::(boxer v5)
::(boxer v6)::(boxer v7)::nil)
(at level 0, v1 at level 0, v2 at level 0, v3 at level 0,
v4 at level 0, v5 at level 0, v6 at level 0, v7 at level 0)
: ltac_scope.
Open Scope ltac_scope.
ltac_inst is a datatype that describes the four instantiation
modes that can be used in tactics specializes, lets,
applys and forwards.
- Args: all arguments are to be provided,
- Hyps: only hypotheses are to be provided (hypotheses = arguments not used dependently)
- Vars: only variables are to be provided (variables = arguments used dependently)
- Hnts: the arguments provided are used as hints and are affected to the first argument of matching type.
Inductive ltac_inst : Set :=
| Args : ltac_inst
| Hyps : ltac_inst
| Vars : ltac_inst
| Hnts : ltac_inst.
ltac_args inputs a term E and returns a term of type "list boxer":
- if E is already of type "list Boxer" that starts with the value of an instantiation mode, it returns E,
- otherwise if E is already of type "list Boxer", it returns (boxer Hnts)::E, in other words, mode Hnts is the default,
- otherwise, it returns the list containing (boxer Hnts)::(boxer E)::nil, describing the fact that there is only one argument provided, in the mode Hnts.
Ltac ltac_args E :=
match type of E with
| List.list Boxer =>
match E with
| (@boxer ltac_inst _)::_ => constr:(E)
| _ => constr:((boxer Hnts)::E)
end
| _ => constr:((boxer Hnts)::(boxer E)::nil)
end.
show tac executes a tactic tac that produces a result (not
performing any side-effect on the goal) and then display its result.
Tactic Notation "show" tactic(tac) :=
let R := tac in pose R.
dup N produces N copies of the current goal. It is useful
for building examples on which to illustrate behaviour of tactics.
dup is short for dup 2.
Lemma dup_lemma : forall P, P -> P -> P.
Proof. auto. Qed.
Ltac dup_tactic N :=
match N with
| 0 => idtac
| S 0 => idtac
| S ?N' => apply dup_lemma; [ | dup_tactic N' ]
end.
Tactic Notation "dup" constr(N) :=
dup_tactic N.
Tactic Notation "dup" :=
dup 2.
get_head E is a tactic that returns the head constant of the
term E, ie, when applied to a term of the form P x1 ... xN
it returns P. If E is not an application, it returns E.
Ltac get_head E :=
match E with
| ?P _ _ _ _ _ _ _ _ _ => constr:(P)
| ?P _ _ _ _ _ _ _ _ => constr:(P)
| ?P _ _ _ _ _ _ _ => constr:(P)
| ?P _ _ _ _ _ _ => constr:(P)
| ?P _ _ _ _ _ => constr:(P)
| ?P _ _ _ _ => constr:(P)
| ?P _ _ _ => constr:(P)
| ?P _ _ => constr:(P)
| ?P _ => constr:(P)
| ?P => constr:(P)
end.
Ltac is_metavar E :=
match E with
| ?P _ _ _ _ _ _ _ _ _ => constr:(false)
| ?P _ _ _ _ _ _ _ _ => constr:(false)
| ?P _ _ _ _ _ _ _ => constr:(false)
| ?P _ _ _ _ _ _ => constr:(false)
| ?P _ _ _ _ _ => constr:(false)
| ?P _ _ _ _ => constr:(false)
| ?P _ _ _ => constr:(false)
| ?P _ _ => constr:(false)
| ?P _ => constr:(false)
| ?P => constr:(true)
end.
lets H: E adds an hypothesis H : T to the context, where T is
the type of term E. If H is an introduction pattern, it will
destruct H according to the pattern.
Tactic Notation "lets" simple_intropattern(I) ":" constr(E) :=
generalize E; intros I.
lets H1 .. HN : E is the same as
lets \[H1 \[H2 \[.. HN\]\]\]\:E], and thus equivalent to
destruct E as \[H1 \[H2 \[.. HN\]\]\]\].
Tactic Notation "lets" simple_intropattern(I1)
simple_intropattern(I2) ":" constr(E) :=
lets [I1 I2]: E.
Tactic Notation "lets" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) ":" constr(E) :=
lets [I1 [I2 I3]]: E.
Tactic Notation "lets" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) ":" constr(E) :=
lets [I1 [I2 [I3 I4]]]: E.
Tactic Notation "lets" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5) ":" constr(E) :=
lets [I1 [I2 [I3 [I4 I5]]]]: E.
Tactic Notation "lets" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5)
simple_intropattern(I6) ":" constr(E) :=
lets [I1 [I2 [I3 [I4 [I5 I6]]]]]: E.
lets_simpl H: E is the same as lets H: E excepts that it
calls simpl on the hypothesis H.
Tactic Notation "lets_simpl" ident(H) ":" constr(E) :=
lets H: E; simpl in H.
lets_hnf H: E is the same as lets H: E excepts that it
calls hnf to set the definition in head normal form.
Tactic Notation "lets_hnf" ident(H) ":" constr(E) :=
lets H: E; hnf in H.
lets: E is equivalent to lets H: E, only the name H is
automatically chosen by Coq. It is useful to type-check a
term (like the top-level command Check), but also to add
facts that are going to be used by automation.
Syntax lets: E1 .. EN is short for lets: E1; ..; lets: EN.
Tactic Notation "lets" ":" constr(E1) :=
generalize E1; intro.
Tactic Notation "lets" ":" constr(E1) constr(E2) :=
lets: E1; lets: E2.
Tactic Notation "lets" ":" constr(E1) constr(E2) constr(E3) :=
lets: E1; lets: E2; lets: E3.
lets_simpl: E is the same as lets_simpl H: E with
the name H being choosed automatically.
Tactic Notation "lets_simpl" ":" constr(T) :=
let H := fresh in lets_simpl H: T.
lets_hnf: E is the same as lets_hnf H: E with
the name H being choosed automatically.
Tactic Notation "lets_hnf" ":" constr(T) :=
let H := fresh in lets_hnf H: T.
put X: E is a synonymous for pose (X := E).
Other syntaxes are put: E.
Tactic Notation "put" ident(X) ":" constr(E) :=
pose (X := E).
Tactic Notation "put" ":" constr(E) :=
let X := fresh "X" in pose (X := E).
applys is a tactic similar to eapply except that it is
based on the refine tactics, and thus is strictly more
powerful (at least in theory :). In short, it is able to perform
on-the-fly conversions when required for arguments to match,
and it is able to instantiate existentials when required.
Tactic Notation "applys" constr(t) :=
first
[ refine (@t)
| refine (@t _)
| refine (@t _ _)
| refine (@t _ _ _)
| refine (@t _ _ _ _)
| refine (@t _ _ _ _ _)
| refine (@t _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _ _ _ _ _ _)
| refine (@t _ _ _ _ _ _ _ _ _ _ _ _ _)
].
The tactics applys_N T, where N is a natural number,
provides a more efficient way of using applys T. It avoids
trying out all possible arities, by specifying explicitely
the arity of function T. This version is to be preferred
for programming intensively-used tactics.
Tactic Notation "applys_0" constr(t) :=
refine (@t).
Tactic Notation "applys_1" constr(t) :=
refine (@t _).
Tactic Notation "applys_2" constr(t) :=
refine (@t _ _).
Tactic Notation "applys_3" constr(t) :=
refine (@t _ _ _).
Tactic Notation "applys_4" constr(t) :=
refine (@t _ _ _ _).
Tactic Notation "applys_5" constr(t) :=
refine (@t _ _ _ _ _).
Tactic Notation "applys_6" constr(t) :=
refine (@t _ _ _ _ _ _).
Tactic Notation "applys_7" constr(t) :=
refine (@t _ _ _ _ _ _ _).
Tactic Notation "applys_8" constr(t) :=
refine (@t _ _ _ _ _ _ _ _).
Tactic Notation "applys_9" constr(t) :=
refine (@t _ _ _ _ _ _ _ _ _).
Tactic Notation "applys_10" constr(t) :=
refine (@t _ _ _ _ _ _ _ _ _ _).
applys_to H E transform the type of hypothesis H by
replacing it by the result of the application of the term
E to H. Intuitively, it is equivalent to lets H: (E H).
Tactic Notation "applys_to" hyp(H) constr(E) :=
let H' := fresh in rename H into H';
(first [ lets H: (E H')
| lets H: (E _ H')
| lets H: (E _ _ H')
| lets H: (E _ _ _ H')
| lets H: (E _ _ _ _ H')
| lets H: (E _ _ _ _ _ H')
| lets H: (E _ _ _ _ _ _ H')
| lets H: (E _ _ _ _ _ _ _ H')
| lets H: (E _ _ _ _ _ _ _ _ H')
| lets H: (E _ _ _ _ _ _ _ _ _ H') ]
); clear H'.
applys_in H E transform the hypothesis H by replacing it
by the result of the application of H to the term E
Intuitively, it is equivalent to lets H: (H E).
DEPRECATED: use specializes H E instead.
Tactic Notation "applys_in" hyp(H) constr(E) :=
let H' := fresh in rename H into H';
(first [ lets H: (H' E)
| lets H: (H' _ E)
| lets H: (H' _ _ E)
| lets H: (H' _ _ _ E)
| lets H: (H' _ _ _ _ E)
| lets H: (H' _ _ _ _ _ E)
| lets H: (H' _ _ _ _ _ _ E)
| lets H: (H' _ _ _ _ _ _ _ E)
| lets H: (H' _ _ _ _ _ _ _ _ E)
| lets H: (H' _ _ _ _ _ _ _ _ _ E) ]
); clear H'.
applys_clear E performs applys E and then calls clear on
the head term of E. It fails if this head term is not an hypothesis
or if it used dependently.
Tactic Notation "applys_clear" constr(E) :=
applys E; let H := get_head E in clear E.
Tactic Notation "apply_clear" constr(E) :=
applys_clear E.
false_goal replaces any goal by the goal False.
Contrary to the tactic false (below), it does not try to do
anything else
false replaces any goal by the goal False.
Furthermore, it discharges the obligation if the context contains
False or an hypothesis of the form C x1 .. xN = D y1 .. yM.
Tactic Notation "false" :=
false_goal; try assumption; try discriminate.
tryfalse tries to solve a goal by contradiction, and leaves
the goal unchanged if it cannot solve it.
It is equivalent to try solve \[ false \].
Tactic Notation "tryfalse" :=
try solve [ false ].
tryfalse by tac / is that same as tryfalse except that
it tries to solve the goal using tactic tac if assumption
and discriminate do not apply.
It is equivalent to try solve \[ false; tac \].
Tactic Notation "tryfalse" "by" tactic(tac) "/" :=
try solve [ false; tac ].
false T is equivalent to false; apply T. It also
solves the goal if T has type C x1 .. xN = D y1 .. yM.
Ltac false_with_plus T Tac :=
false_goal; first
[ first [ apply T | eapply T | applys T]; Tac
| let H := fresh in lets H: T; discriminate H ].
Tactic Notation "false" constr(T) :=
false_with_plus T ltac:(idtac).
asserts H: T is another syntax for assert (H : T), which
also works with introduction patterns. For instance, we can write:
asserts \[x P\] (exists n, n = 3), or
asserts \[H|H\] (n = 0 \/ n = 1).
Tactic Notation "asserts" simple_intropattern(I) ":" constr(T) :=
let H := fresh in assert (H : T);
[ | generalize H; clear H; intros I ].
asserts H1 .. HN: T is the same as
asserts \[H1 \[H2 \[.. HN\]\]\]\: T].
Tactic Notation "asserts" simple_intropattern(I1)
simple_intropattern(I2) ":" constr(T) :=
asserts [I1 I2]: T.
Tactic Notation "asserts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) ":" constr(T) :=
asserts [I1 [I2 I3]]: T.
Tactic Notation "asserts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) ":" constr(T) :=
asserts [I1 [I2 [I3 I4]]]: T.
Tactic Notation "asserts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5) ":" constr(T) :=
asserts [I1 [I2 [I3 [I4 I5]]]]: T.
Tactic Notation "asserts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5)
simple_intropattern(I6) ":" constr(T) :=
asserts [I1 [I2 [I3 [I4 [I5 I6]]]]]: T.
asserts: T as H is another syntax for asserts H: T
Tactic Notation "asserts" ":" constr(T) "as" simple_intropattern(I) :=
asserts I : T.
asserts: T is asserts H: T with H being chosen automatically.
Tactic Notation "asserts" ":" constr(T) :=
let H := fresh in asserts H : T.
cuts H: T is the same as asserts H: T except that the two subgoals
generated are swapped: the subgoal T comes second. Note that contrary
to cut, it introduces the hypothesis.
Tactic Notation "cuts" simple_intropattern(I) ":" constr(T) :=
cut (T); [ intros I | idtac ].
cuts: T is cuts H: T with H being chosen automatically.
Tactic Notation "cuts" ":" constr(T) :=
let H := fresh in cuts H: T.
cuts H1 .. HN: T is the same as
cuts \[H1 \[H2 \[.. HN\]\]\]\: T].
Tactic Notation "cuts" simple_intropattern(I1)
simple_intropattern(I2) ":" constr(T) :=
cuts [I1 I2]: T.
Tactic Notation "cuts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) ":" constr(T) :=
cuts [I1 [I2 I3]]: T.
Tactic Notation "cuts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) ":" constr(T) :=
cuts [I1 [I2 [I3 I4]]]: T.
Tactic Notation "cuts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5) ":" constr(T) :=
cuts [I1 [I2 [I3 [I4 I5]]]]: T.
Tactic Notation "cuts" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5)
simple_intropattern(I6) ":" constr(T) :=
cuts [I1 [I2 [I3 [I4 [I5 I6]]]]]: T.
The instantiation tactics are used to instantiate a lemma E
(whose type is a product) on some arguments. The type of E is
made of implications and universal quantifications, e.g.
forall x, P x -> forall y z, Q x y z -> R z.
The first possibility is to provide arguments in order: first x, then a proof of P x, then y etc... In this mode, called "Args", all the arguments are to be provided. If a wildcard is provided (written __), then an existential variable will be introduced in place of the argument.
It often saves a lot of time to give only the dependent variables, (here x, y and z), and have the hypotheses generated as subgoals. In this "Vars" mode, only variables are to be provided. For instance, lemma E applied to 3 and 4 is a term of type forall z, Q 3 4 z -> R z, and P 3 is a new subgoal. It is possible to use wildcards to introduce existential variables.
However, there are situations where some of the hypotheses already exists, and it saves time to instantiate the lemma E using the hypotheses. For instance, suppose F is a term of type P 2. Then the application of E to F in this "Hyps" mode is a term of type forall y z, Q 2 y z -> R z. Each wildcard use will generate an assertion instead, for instance if G has type Q 2 3 4, then the application of E to a wildcard and to G in mode-h is a term of type R 4, and P 2 is a new subgoal.
It is very convenient to give some arguments the lemma should be instantiated on, and let the tactic find out automatically where underscores should be insterted. The technique is simple: try to place an argument, and if it does not work insert an underscore. In this "Hints" mode (Hnts for short), underscore __ would be useless, since they can be omitted. So, we interpret underscore as follows: an underscore means that we want to skip the argument that has the same type as the next real argument provided (real means not an underscore). If there is no real argument after underscore, then the the underscore is used for the first possible argument.
There are four modes of instantiation:
Finally, if the argument EN given is a triple-underscore ___, then it is equivalent to providing a list of wildcards, with the appropriate number of wildcards. This means that all the remaining arguments of the lemma will be instantiated.
The first possibility is to provide arguments in order: first x, then a proof of P x, then y etc... In this mode, called "Args", all the arguments are to be provided. If a wildcard is provided (written __), then an existential variable will be introduced in place of the argument.
It often saves a lot of time to give only the dependent variables, (here x, y and z), and have the hypotheses generated as subgoals. In this "Vars" mode, only variables are to be provided. For instance, lemma E applied to 3 and 4 is a term of type forall z, Q 3 4 z -> R z, and P 3 is a new subgoal. It is possible to use wildcards to introduce existential variables.
However, there are situations where some of the hypotheses already exists, and it saves time to instantiate the lemma E using the hypotheses. For instance, suppose F is a term of type P 2. Then the application of E to F in this "Hyps" mode is a term of type forall y z, Q 2 y z -> R z. Each wildcard use will generate an assertion instead, for instance if G has type Q 2 3 4, then the application of E to a wildcard and to G in mode-h is a term of type R 4, and P 2 is a new subgoal.
It is very convenient to give some arguments the lemma should be instantiated on, and let the tactic find out automatically where underscores should be insterted. The technique is simple: try to place an argument, and if it does not work insert an underscore. In this "Hints" mode (Hnts for short), underscore __ would be useless, since they can be omitted. So, we interpret underscore as follows: an underscore means that we want to skip the argument that has the same type as the next real argument provided (real means not an underscore). If there is no real argument after underscore, then the the underscore is used for the first possible argument.
There are four modes of instantiation:
- "Args": give all arguments,
- "Vars": give only variables,
- "Hyps": give only hypotheses,
- "Hnts": give some arguments.
Finally, if the argument EN given is a triple-underscore ___, then it is equivalent to providing a list of wildcards, with the appropriate number of wildcards. This means that all the remaining arguments of the lemma will be instantiated.
Ltac app_arg t P v cont :=
let H := fresh "TEMP" in
assert (H : P); [ apply v | cont(t H); try clear H ].
Ltac app_assert t P cont :=
let H := fresh "TEMP" in
assert (H : P); [ | cont(t H); clear H ].
Ltac app_evar t A cont :=
let x := fresh "TEMP" in
evar (x:A);
let t' := constr:(t x) in
let t'' := (eval unfold x in t') in
subst x; cont t''.
Ltac build_app_alls t final :=
let rec go t :=
match type of t with
| ?P -> ?Q => app_assert t P go
| forall _:?A, _ => app_evar t A go
| _ => final t
end in
go t.
Ltac build_app_args t vs final :=
let rec go t vs :=
match vs with
| nil => first [ final t | fail 1 ]
| (boxer ltac_wilds)::_ => first [ build_app_alls t final | fail 1 ]
| (boxer ?v)::?vs' =>
let cont t' := go t' vs' in
match v with
| ltac_wild =>
match type of t with
| ?P -> ?Q => first [ app_assert t P cont | fail 3 ]
| forall _:?A, _ => first [ app_evar t A cont | fail 20 ]
end
| _ =>
match type of t with
| ?P -> ?Q => first [ app_arg t P v cont | fail 3 ]
| forall _:?A, _ => first [ cont (t v) | fail 3 ]
end
end
end in
go t vs.
Ltac build_app_vars t vs final :=
let rec go t vs :=
match vs with
| nil => first [ final t | fail 1 ]
| (boxer ltac_wilds)::_ => first [ build_app_alls t final | fail 1 ]
| (boxer ?v)::?vs' =>
match type of t with
| ?P -> ?Q =>
let cont t' := go t' vs in
first [ app_assert t P cont | fail 3 ]
| forall _:?A, _ =>
let cont t' := go t' vs' in
match v with
| ltac_wild => first [ app_evar t A cont | fail 3 ]
| _ => first [ cont(t v) | fail 3 ]
end
end
end in
go t vs.
Ltac build_app_hyps t vs final :=
let rec go t vs :=
match vs with
| nil => first [ final t | fail 1 ]
| (boxer ltac_wilds)::_ => first [ build_app_alls t final | fail 1 ]
| (boxer ?v)::?vs' =>
match type of t with
| ?P -> ?Q =>
let cont t' := go t' vs' in
match v with
| ltac_wild => first [ app_assert t P cont | fail 3 ]
| _ => first [ app_arg t P v cont | fail 3 ]
end
| forall _:?A, _ =>
let cont t' := go t' vs in
first [ app_evar t A cont | fail 3 ]
end
end in
go t vs.
Ltac boxerlist_next_type vs :=
match vs with
| nil => constr:(ltac_wild)
| (boxer ltac_wild)::?vs' => boxerlist_next_type vs'
| (boxer ltac_wilds)::_ => constr:(ltac_wild)
| (@boxer ?T _)::_ => constr:(T)
end.
Ltac build_app_hnts t vs final :=
let rec go t vs :=
match vs with
| nil => first [ final t | fail 1 ]
| (boxer ltac_wilds)::_ => first [ build_app_alls t final | fail 1 ]
| (boxer ?v)::?vs' =>
let cont t' := go t' vs in
let cont' t' := go t' vs' in
match v with
| ltac_wild =>
first [ let T := boxerlist_next_type vs' in
match T with
| ltac_wild =>
match type of t with
| ?P -> ?Q => first [ app_assert t P cont' | fail 3 ]
| forall _:?A, _ => first [ app_evar t A cont' | fail 3 ]
end
| _ =>
match type of t with
| T -> ?Q => first [ app_assert t T cont' | fail 3 ]
| forall _:T, _ => first [ app_evar t T cont' | fail 3 ]
| ?P -> ?Q => first [ app_assert t P cont | fail 3 ]
| forall _:?A, _ => first [ app_evar t A cont | fail 3 ]
end
end
| fail 2 ]
| _ =>
match type of t with
| ?P -> ?Q => first [ app_arg t P v cont'
| app_assert t P cont
| fail 3 ]
| forall _:?A, _ => first [ cont' (t v)
| app_evar t A cont
| fail 3 ]
end
end
end in
go t vs.
Ltac build_app t boxlist final :=
let args := ltac_args boxlist in
first [
match args with (boxer ?mode)::?vs =>
match mode with
| Args => build_app_args t vs final
| Vars => build_app_vars t vs final
| Hyps => build_app_hyps t vs final
| Hnts => build_app_hnts t vs final
end end
| fail 1 "Instantiation fails for:" t args].
lets H: E of (>>> E1 E2 .. EN) will instantiate lemma E
on the arguments Ei (which may be wildcards __),
and name H the resulting term (H may be an introduction
pattern or a sequence of introduction patterns I1 I2 IN).
The keyword "ok" may be replaced with "of_vars" or "of_hyps"
for providing only variables or only hypotheses. If the last
argument EN is ___ (triple-underscore), then all
arguments of H will be instantiated.
Ltac lets_build I E boxlist :=
build_app E boxlist ltac:(fun R => lets I: R).
Tactic Notation "lets" simple_intropattern(I) ":" constr(E) constr(A) :=
lets_build I E A.
Tactic Notation "lets" ":" constr(E) constr(A) :=
let H := fresh in lets H: E A.
Tactic Notation "lets" simple_intropattern(I1) simple_intropattern(I2)
":" constr(E) constr(A) :=
lets [I1 I2]: E A.
Tactic Notation "lets" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) ":" constr(E) constr(A) :=
lets [I1 [I2 I3]]: E A.
Tactic Notation "lets" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) ":" constr(E) constr(A) :=
lets [I1 [I2 [I3 I4]]]: E A.
Tactic Notation "lets" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5)
":" constr(E) constr(A) :=
lets [I1 [I2 [I3 [I4 I5]]]]: E A.
Tactic Notation "lets" simple_intropattern(I) ":" constr(E)
constr(A1) constr(A2) :=
lets I: E (>>> A1 A2).
Tactic Notation "lets" simple_intropattern(I) ":" constr(E)
constr(A1) constr(A2) constr(A3) :=
lets I: E (>>> A1 A2 A3).
Tactic Notation "lets" simple_intropattern(I) ":" constr(E)
constr(A1) constr(A2) constr(A3) constr(A4) :=
lets I: E (>>> A1 A2 A3 A4).
Tactic Notation "lets" simple_intropattern(I) ":" constr(E)
constr(A1) constr(A2) constr(A3) constr(A4) constr(A5) :=
lets I: E (>>> A1 A2 A3 A4 A5).
forwards I: E A is short for lets I: E A, which means
that it will instantiate E on all arguments, and name I the
result. Similarly, fowards_in H: E is short for
applys_in H ___ and will instantiate all arguments of H.
Tactic Notation "forwards" simple_intropattern(I) ":" constr(E) :=
lets I: E ___.
Tactic Notation "forwards" simple_intropattern(I) ":" constr(E) constr(A) :=
let A' := ltac_args A in
let A'' := (eval simpl in (A' ++ ((boxer ___)::nil))) in
lets I: E A''.
Tactic Notation "forwards" ":" constr(E) :=
let H := fresh in forwards H: E.
Tactic Notation "forwards" ":" constr(E) constr(A) :=
let H := fresh in forwards H: E A.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
":" constr(E) :=
forwards [I1 I2]: E.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) ":" constr(E) :=
forwards [I1 [I2 I3]]: E.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) ":" constr(E) :=
forwards [I1 [I2 [I3 I4]]]: E.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5)
":" constr(E) :=
forwards [I1 [I2 [I3 [I4 I5]]]]: E.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
":" constr(E) constr(A) :=
forwards [I1 I2]: E A.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) ":" constr(E) constr(A) :=
forwards [I1 [I2 I3]]: E A.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) ":" constr(E) constr(A) :=
forwards [I1 [I2 [I3 I4]]]: E A.
Tactic Notation "forwards" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5)
":" constr(E) constr(A) :=
forwards [I1 [I2 [I3 [I4 I5]]]]: E A.
applys E (>>>Mode E1 E2 .. EN) will instantiate lemma E
on the arguments Ei (which may be wildcards __),
and apply the resulting term to the current goal.
applys E E1 E2 .. EN is short
for applys E (>>>Hnts E1 E2 .. EN).
Ltac applys_build E boxlist :=
build_app E boxlist ltac:(fun R =>
first [ apply R | eapply R | applys R | fail 20 "does not apply" ]).
Tactic Notation "applys" constr(E) constr(A) :=
applys_build E A.
Tactic Notation "applys" constr(E) constr(A1) constr(A2) :=
applys E (>>> A1 A2).
Tactic Notation "applys" constr(E) constr(A1) constr(A2) constr(A3) :=
applys E (>>> A1 A2 A3).
Tactic Notation "applys" constr(E) constr(A1) constr(A2) constr(A3) constr(A4) :=
applys E (>>> A1 A2 A3 A4).
Tactic Notation "applys" constr(E) constr(A1) constr(A2) constr(A3) constr(A4) constr(A5) :=
applys E (>>> A1 A2 A3 A4 A5).
specializes H (>>> E1 E2 .. EN) will instantiate hypothesis H
on the arguments Ei (which may be wildcards __).
The keyword "ok" may be replaced with "of_vars" or "of_hyps"
for providing only variables or only hypotheses. If the last
argument EN is ___ (triple-underscore), then all
arguments of H will be instantiated.
Ltac specializes_build H boxlist :=
let H' := fresh "TEMP" in rename H into H';
build_app H' boxlist ltac:(fun R => lets H: R);
clear H'.
Tactic Notation "specializes" hyp(H) constr(A) :=
specializes_build H A.
Tactic Notation "specializes" hyp(H) constr(A1) constr(A2) :=
specializes H (>>> A1 A2).
Tactic Notation "specializes" hyp(H) constr(A1) constr(A2) constr(A3) :=
specializes H (>>> A1 A2 A3).
Tactic Notation "specializes" hyp(H) constr(A1) constr(A2) constr(A3) constr(A4) :=
specializes H (>>> A1 A2 A3 A4).
Tactic Notation "specializes" hyp(H) constr(A1) constr(A2) constr(A3) constr(A4) constr(A5) :=
specializes H (>>> A1 A2 A3 A4 A5).
fapply is a version of apply based on forwards.
Tactic Notation "fapply" constr(E) :=
let H := fresh in forwards H: E;
first [ apply H | eapply H | hnf; apply H
| hnf; eapply H | applys H ].
sapply stands for "super apply". It tries
apply, eapply, applys and fapply,
plus hnfapply; and hnfeapply;.
Tactic Notation "sapply" constr(H) :=
first [ apply H | eapply H | applys H
| hnf; apply H | hnf; eapply H | hnf; applys H
| fapply H ].
introv iterates intro on all universally-quantified "variables"
at the head of the goal. More precisely, it introduces only the
dependently-used variables. For example, introv applied
to the goal forall x y, P x -> Q introduces x and y but not
the hypothesis P x. If the goal is a definition, then it will
unfold this definition.
introv H1 .. HN calls introv then introduces an hypothesis as H1, then call introv again and introduces an hypothesis as H2, and so on. It provides a convenient way of introducing all the arguments of a theorem and name only the non-dependent hypotheses.
introv H1 .. HN calls introv then introduces an hypothesis as H1, then call introv again and introduces an hypothesis as H2, and so on. It provides a convenient way of introducing all the arguments of a theorem and name only the non-dependent hypotheses.
Ltac introv_rec :=
match goal with
| |- (ltac_tag_subst (?x = ?y) -> ?Q) =>
let H := fresh "Aux" in
intro H; unfold ltac_tag_subst in H;
try subst x; introv_rec
| |- ?P -> ?Q => idtac
| |- forall _, _ => intro; introv_rec
| |- _ => idtac
end.
Ltac introv_to :=
match goal with
| |- (ltac_tag_subst (?x = ?y) -> ?Q) =>
let H := fresh "Aux" in
intro H; unfold ltac_tag_subst in H;
try subst x; introv_to
| |- ?P -> ?Q => idtac
| |- forall _, _ => intro; introv_to
| |- ?G => let P := get_head G in progress (unfold P); introv_to
end.
Ltac introv_base :=
match goal with
| |- forall _, _ => introv_rec
| |- ?G => let P := get_head G in unfold P; introv_rec
end.
Tactic Notation "introv" :=
introv_base.
Tactic Notation "introv" simple_intropattern(I) :=
introv_to; intros I.
Tactic Notation "introv" simple_intropattern(I1) simple_intropattern(I2) :=
introv_to; intros I1; introv_to; intros I2.
Tactic Notation "introv" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) :=
introv; intros I1; introv; intros I2; introv; intros I3.
Tactic Notation "introv" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) :=
introv; intros I1; introv; intros I2; introv; intros I3;
introv; intros I4.
Tactic Notation "introv" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5) :=
introv; intros I1; introv; intros I2; introv; intros I3;
introv; intros I4; introv; intros I5.
Tactic Notation "introv" simple_intropattern(I1) simple_intropattern(I2)
simple_intropattern(I3) simple_intropattern(I4) simple_intropattern(I5)
simple_intropattern(I6) :=
introv; intros I1; introv; intros I2; introv; intros I3;
introv; intros I4; introv; intros I5; introv; intros I6.
intros_all repeats intro as long as possible. Contrary to intros,
it unfolds any definition on the way. Remark that it also unfolds the
definition of negation, so applying introz to a goal of the form
forall x, P x -> ~Q will introduce x and P x and Q, and will
leave False in the goal.
Tactic Notation "intros_all" :=
repeat intro.
gen X1 .. XN is a shorthand for calling generalize dependent
successively on variables XN...X1. Note that the variables
are generalized in reverse order, following the convention of
the generalize tactic: it means that X1 will be the first
quantified variable in the resulting goal.
Tactic Notation "gen" ident(X1) :=
generalize dependent X1.
Tactic Notation "gen" ident(X1) ident(X2) :=
gen X2; gen X1.
Tactic Notation "gen" ident(X1) ident(X2) ident(X3) :=
gen X3; gen X2; gen X1.
Tactic Notation "gen" ident(X1) ident(X2) ident(X3) ident(X4) :=
gen X4; gen X3; gen X2; gen X1.
Tactic Notation "gen" ident(X1) ident(X2) ident(X3) ident(X4) ident(X5) :=
gen X5; gen X4; gen X3; gen X2; gen X1.
Tactic Notation "gen" ident(X1) ident(X2) ident(X3) ident(X4) ident(X5)
ident(X6) :=
gen X6; gen X5; gen X4; gen X3; gen X2; gen X1.
Tactic Notation "gen" ident(X1) ident(X2) ident(X3) ident(X4) ident(X5)
ident(X6) ident(X7) :=
gen X7; gen X6; gen X5; gen X4; gen X3; gen X2; gen X1.
Tactic Notation "gen" ident(X1) ident(X2) ident(X3) ident(X4) ident(X5)
ident(X6) ident(X7) ident(X8) :=
gen X8; gen X7; gen X6; gen X5; gen X4; gen X3; gen X2; gen X1.
generalizes X is a shorthand for calling generalize X; clear X.
It is weaker than tactic gen X since it does not support
dependencies. It is mainly intended for writing tactics.
Tactic Notation "generalizes" hyp(X) :=
generalize X; clear X.
sets X: E is the same as set (X := E) in *, that is,
it replaces all occurences of E by a fresh meta-variable X
whose definition is E.
Tactic Notation "sets" ident(X) ":" constr(E) :=
set (X := E) in *.
def_to_eq E X H applies when X := E is a local
definition. It adds an assumption H: X = E
and then clears the definition of X.
def_to_eq_sym is similar except that it generates
the equality H: E = X.
Ltac def_to_eq X HX E :=
assert (HX : X = E) by reflexivity; clearbody X.
Ltac def_to_eq_sym X HX E :=
assert (HX : E = X) by reflexivity; clearbody X.
set_eq X H: E generates the equality H: X = E,
for a fresh name X, and replaces E by X in the
current goal. Syntaxes set_eq X: E and
set_eq: E are also available. Similarly,
set_eq <- X H: E generates the equality H: E = X.
sets_eq X HX: E does the same but replaces E by X everywhere in the goal. sets_eq X HX: E in H replaces in H. set_eq X HX: E in |- performs no substitution at all.
sets_eq X HX: E does the same but replaces E by X everywhere in the goal. sets_eq X HX: E in H replaces in H. set_eq X HX: E in |- performs no substitution at all.
Tactic Notation "set_eq" ident(X) ident(HX) ":" constr(E) :=
set (X := E); def_to_eq X HX E.
Tactic Notation "set_eq" ident(X) ":" constr(E) :=
let HX := fresh "EQ" X in set_eq X HX: E.
Tactic Notation "set_eq" ":" constr(E) :=
let X := fresh "X" in set_eq X: E.
Tactic Notation "set_eq" "<-" ident(X) ident(HX) ":" constr(E) :=
set (X := E); def_to_eq_sym X HX E.
Tactic Notation "set_eq" "<-" ident(X) ":" constr(E) :=
let HX := fresh "EQ" X in set_eq <- X HX: E.
Tactic Notation "set_eq" "<-" ":" constr(E) :=
let X := fresh "X" in set_eq <- X: E.
Tactic Notation "sets_eq" ident(X) ident(HX) ":" constr(E) :=
set (X := E) in *; def_to_eq X HX E.
Tactic Notation "sets_eq" ident(X) ":" constr(E) :=
let HX := fresh "EQ" X in sets_eq X HX: E.
Tactic Notation "sets_eq" ":" constr(E) :=
let X := fresh "X" in sets_eq X: E.
Tactic Notation "sets_eq" "<-" ident(X) ident(HX) ":" constr(E) :=
set (X := E) in *; def_to_eq_sym X HX E.
Tactic Notation "sets_eq" "<-" ident(X) ":" constr(E) :=
let HX := fresh "EQ" X in sets_eq <- X HX: E.
Tactic Notation "sets_eq" "<-" ":" constr(E) :=
let X := fresh "X" in sets_eq <- X: E.
Tactic Notation "set_eq" ident(X) ident(HX) ":" constr(E) "in" hyp(H) :=
set (X := E) in H; def_to_eq X HX E.
Tactic Notation "set_eq" ident(X) ":" constr(E) "in" hyp(H) :=
let HX := fresh "EQ" X in set_eq X HX: E in H.
Tactic Notation "set_eq" ":" constr(E) "in" hyp(H) :=
let X := fresh "X" in set_eq X: E in H.
Tactic Notation "set_eq" "<-" ident(X) ident(HX) ":" constr(E) "in" hyp(H) :=
set (X := E) in H; def_to_eq_sym X HX E.
Tactic Notation "set_eq" "<-" ident(X) ":" constr(E) "in" hyp(H) :=
let HX := fresh "EQ" X in set_eq <- X HX: E in H.
Tactic Notation "set_eq" "<-" ":" constr(E) "in" hyp(H) :=
let X := fresh "X" in set_eq <- X: E in H.
Tactic Notation "set_eq" ident(X) ident(HX) ":" constr(E) "in" "|-" :=
set (X := E) in |-; def_to_eq X HX E.
Tactic Notation "set_eq" ident(X) ":" constr(E) "in" "|-" :=
let HX := fresh "EQ" X in set_eq X HX: E in |-.
Tactic Notation "set_eq" ":" constr(E) "in" "|-" :=
let X := fresh "X" in set_eq X: E in |-.
Tactic Notation "set_eq" "<-" ident(X) ident(HX) ":" constr(E) "in" "|-" :=
set (X := E) in |-; def_to_eq_sym X HX E.
Tactic Notation "set_eq" "<-" ident(X) ":" constr(E) "in" "|-" :=
let HX := fresh "EQ" X in set_eq <- X HX: E in |-.
Tactic Notation "set_eq" "<-" ":" constr(E) "in" "|-" :=
let X := fresh "X" in set_eq <- X: E in |-.
ltac_pattern E at K is the same as pattern E at K except that
K is a Coq natural rather than a Ltac integer. Syntax
ltac_pattern E as K in H is also available.
Tactic Notation "ltac_pattern" constr(E) "at" constr(K) :=
match K with
| 1 => pattern E at 1
| 2 => pattern E at 2
| 3 => pattern E at 3
| 4 => pattern E at 4
| 5 => pattern E at 5
| 6 => pattern E at 6
| 7 => pattern E at 7
| 8 => pattern E at 8
end.
Tactic Notation "ltac_pattern" constr(E) "at" constr(K) "in" hyp(H) :=
match K with
| 1 => pattern E at 1 in H
| 2 => pattern E at 2 in H
| 3 => pattern E at 3 in H
| 4 => pattern E at 4 in H
| 5 => pattern E at 5 in H
| 6 => pattern E at 6 in H
| 7 => pattern E at 7 in H
| 8 => pattern E at 8 in H
end.
ltac_action_at K of E do Tac isolates the K-th occurence of E in the
goal, setting it in the form P E for some named pattern P,
then calls tactic Tac, and finally unfolds P. Syntax
ltac_action_at K of E in H do Tac is also available.
Tactic Notation "ltac_action_at" constr(K) "of" constr(E) "do" tactic(Tac) :=
let p := fresh in ltac_pattern E at K;
match goal with |- ?P _ => set (p:=P) end;
Tac; unfold p; clear p.
Tactic Notation "ltac_action_at" constr(K) "of" constr(E) "in" hyp(H) "do" tactic(Tac) :=
let p := fresh in ltac_pattern E at K in H;
match type of H with ?P _ => set (p:=P) in H end;
Tac; unfold p in H; clear p.
protects E do Tac temporarily assigns a name to the expression E
so that the execution of tactic Tac will not modify E. This is
useful for instance to restrict the action of simpl.
Tactic Notation "protects" constr(E) "do" tactic(T) :=
let x := fresh in sets_eq x: E; T; subst x.
Tactic Notation "protects" constr(E) "do" tactic(T) "/" :=
protects E do T.
rewrite_all E iterates version of rewrite E as long as possible.
Warning: this tactic can easily get into an infinite loop.
Syntax for rewriting from right to left and/or into an hypothese
is similar to the one of rewrite.
Tactic Notation "rewrite_all" constr(E) :=
repeat rewrite E.
Tactic Notation "rewrite_all" "<-" constr(E) :=
repeat rewrite <- E.
Tactic Notation "rewrite_all" constr(E) "in" ident(H) :=
repeat rewrite E in H.
Tactic Notation "rewrite_all" "<-" constr(E) "in" ident(H) :=
repeat rewrite <- E in H.
Tactic Notation "rewrite_all" constr(E) "in" "*" :=
repeat rewrite E in *.
Tactic Notation "rewrite_all" "<-" constr(E) "in" "*" :=
repeat rewrite <- E in *.
asserts_rewrite E asserts that an equality E holds (generating a
corresponding subgoal) and rewrite it straight away in the current
goal. It avoids giving a name to the equality and later clearing it.
Syntax for rewriting from right to left and/or into an hypothese
is similar to the one of rewrite. Note: the tactic replaces
plays a similar role.
Ltac asserts_rewrite_tactic E action :=
let EQ := fresh in (assert (EQ : E);
[ idtac | action EQ; clear EQ ]).
Tactic Notation "asserts_rewrite" constr(E) :=
asserts_rewrite_tactic E ltac:(fun EQ => rewrite EQ).
Tactic Notation "asserts_rewrite" "<-" constr(E) :=
asserts_rewrite_tactic E ltac:(fun EQ => rewrite <- EQ).
Tactic Notation "asserts_rewrite" constr(E) "in" hyp(H) :=
asserts_rewrite_tactic E ltac:(fun EQ => rewrite EQ in H).
Tactic Notation "asserts_rewrite" "<-" constr(E) "in" hyp(H) :=
asserts_rewrite_tactic E ltac:(fun EQ => rewrite <- EQ in H).
cuts_rewrite E is the same as asserts_rewrite E except
that subgoals are permuted.
Ltac cuts_rewrite_tactic E action :=
let EQ := fresh in (cuts EQ: E;
[ action EQ; clear EQ | idtac ]).
Tactic Notation "cuts_rewrite" constr(E) :=
cuts_rewrite_tactic E ltac:(fun EQ => rewrite EQ).
Tactic Notation "cuts_rewrite" "<-" constr(E) :=
cuts_rewrite_tactic E ltac:(fun EQ => rewrite <- EQ).
Tactic Notation "cuts_rewrite" constr(E) "in" hyp(H) :=
cuts_rewrite_tactic E ltac:(fun EQ => rewrite EQ in H).
Tactic Notation "cuts_rewrite" "<-" constr(E) "in" hyp(H) :=
cuts_rewrite_tactic E ltac:(fun EQ => rewrite <- EQ in H).
rewrite_except H EQ rewrites equality EQ everywhere
but in hypothesis H.
Ltac rewrite_except H EQ :=
let K := fresh in let T := type of H in
set (K := T) in H;
rewrite EQ in *; unfold K in H; clear K.
rewrites E at K applies when E is of the form T1 = T2
rewrites the equality E at the K-th occurence of T1
in the current goal.
Syntaxes rewrites <- E at K and rewrites E at K in H
are also available.
Tactic Notation "rewrites" constr(E) "at" constr(K) :=
match type of E with ?T1 = ?T2 =>
ltac_action_at K of T1 do (rewrite E) end.
Tactic Notation "rewrites" "<-" constr(E) "at" constr(K) :=
match type of E with ?T1 = ?T2 =>
ltac_action_at K of T2 do (rewrite <- E) end.
Tactic Notation "rewrites" constr(E) "at" constr(K) "in" hyp(H) :=
match type of E with ?T1 = ?T2 =>
ltac_action_at K of T1 in H do (rewrite E in H) end.
Tactic Notation "rewrites" "<-" constr(E) "at" constr(K) "in" hyp(H) :=
match type of E with ?T1 = ?T2 =>
ltac_action_at K of T2 in H do (rewrite <- E in H) end.
replaces E with F is the same as replace E with F except that
the equality E = F is generated as first subgoal. Syntax
replaces E with F in H is also available. Note that contrary to
replace, replaces does not try to solve the equality
by assumption. Note: asserts_rewrite plays a similar role.
Tactic Notation "replaces" constr(E) "with" constr(F) :=
let T := fresh in assert (T: E = F); [ | replace E with F; clear T ].
Tactic Notation "replaces" constr(E) "with" constr(F) "in" hyp(H) :=
let T := fresh in assert (T: E = F); [ | replace E with F in H; clear T ].
replaces E at K with F replaces the K-th occurence of E
with F in the current goal. Syntax replaces E at K with F in H
is also available.
Tactic Notation "replaces" constr(E) "at" constr(K) "with" constr(F) :=
let T := fresh in assert (T: E = F); [ | rewrites T at K; clear T ].
Tactic Notation "replaces" constr(E) "at" constr(K) "with" constr(F) "in" hyp(H) :=
let T := fresh in assert (T: E = F); [ | rewrites T at K in H; clear T ].
renames X1 to Y1, ..., XN to YN is a shorthand for a sequence of
renaming operations rename Xi into Yi.
Tactic Notation "renames" ident(X1) "to" ident(Y1) :=
rename X1 into Y1.
Tactic Notation "renames" ident(X1) "to" ident(Y1) ","
ident(X2) "to" ident(Y2) :=
renames X1 to Y1; renames X2 to Y2.
Tactic Notation "renames" ident(X1) "to" ident(Y1) ","
ident(X2) "to" ident(Y2) "," ident(X3) "to" ident(Y3) :=
renames X1 to Y1; renames X2 to Y2, X3 to Y3.
Tactic Notation "renames" ident(X1) "to" ident(Y1) ","
ident(X2) "to" ident(Y2) "," ident(X3) "to" ident(Y3) ","
ident(X4) "to" ident(Y4) :=
renames X1 to Y1; renames X2 to Y2, X3 to Y3, X4 to Y4.
Tactic Notation "renames" ident(X1) "to" ident(Y1) ","
ident(X2) "to" ident(Y2) "," ident(X3) "to" ident(Y3) ","
ident(X4) "to" ident(Y4) "," ident(X5) "to" ident(Y5) :=
renames X1 to Y1; renames X2 to Y2, X3 to Y3, X4 to Y4, X5 to Y5.
Tactic Notation "renames" ident(X1) "to" ident(Y1) ","
ident(X2) "to" ident(Y2) "," ident(X3) "to" ident(Y3) ","
ident(X4) "to" ident(Y4) "," ident(X5) "to" ident(Y5) ","
ident(X6) "to" ident(Y6) :=
renames X1 to Y1; renames X2 to Y2, X3 to Y3, X4 to Y4, X5 to Y5, X6 to Y6.
unfolds unfolds the head definition in the goal, i.e. if the
goal has form P x1 ... xN then it calls unfold P.
If the goal is not in this form, it tries and call intros first.
Ltac unfolds_callback E cont :=
let go E := let P := get_head E in cont P in
match E with
| ?A = ?B => first [ go A | go B ]
| ?A => go A
end.
Ltac unfolds_base :=
match goal with |- ?G =>
unfolds_callback G ltac:(fun P => unfold P) end.
Tactic Notation "unfolds" :=
unfolds_base.
unfolds in H unfolds the head definition of hypothesis H, i.e. if
H has type P x1 ... xN then it calls unfold P in H.
Ltac unfolds_in_base H :=
match type of H with ?G =>
unfolds_callback G ltac:(fun P => unfold P in H) end.
Tactic Notation "unfolds" "in" hyp(H) :=
unfolds_in_base H.
unfolds P1,..,PN is a shortcut for unfold P1,..,PN in *.
Tactic Notation "unfolds" reference(F1) :=
unfold F1 in *.
Tactic Notation "unfolds" reference(F1) "," reference(F2) :=
unfold F1,F2 in *.
Tactic Notation "unfolds" reference(F1) "," reference(F2)
"," reference(F3) :=
unfold F1,F2,F3 in *.
Tactic Notation "unfolds" reference(F1) "," reference(F2)
"," reference(F3) "," reference(F4) :=
unfold F1,F2,F3,F4 in *.
folds P1,..,PN is a shortcut for fold P1 in *; ..; fold PN in *.
Tactic Notation "folds" constr(H) :=
fold H in *.
Tactic Notation "folds" constr(H1) "," constr(H2) :=
folds H1; folds H2.
Tactic Notation "folds" constr(H1) "," constr(H2) "," constr(H3) :=
folds H1; folds H2; folds H3.
Tactic Notation "folds" constr(H1) "," constr(H2) "," constr(H3)
"," constr(H4) :=
folds H1; folds H2; folds H3; folds H4.
Tactic Notation "folds" constr(H1) "," constr(H2) "," constr(H3)
"," constr(H4) "," constr(H5) :=
folds H1; folds H2; folds H3; folds H4; folds H5.
simpls is a shortcut for simpl in *.
Tactic Notation "simpls" :=
simpl in *.
simpls P1,..,PN is a shortcut for
simpl P1 in *; ..; simpl PN in *.
Tactic Notation "simpls" reference(F1) :=
simpl F1 in *.
Tactic Notation "simpls" reference(F1) "," reference(F2) :=
simpls F1; simpls F2.
Tactic Notation "simpls" reference(F1) "," reference(F2)
"," reference(F3) :=
simpls F1; simpls F2; simpls F3.
Tactic Notation "simpls" reference(F1) "," reference(F2)
"," reference(F3) "," reference(F4) :=
simpls F1; simpls F2; simpls F3; simpls F4.
unsimpl E replaces all occurence of X by E, where X is
the result which the tactic simpl would give when applied to E.
It is useful to undo what simpl has simplified too far.
Tactic Notation "unsimpl" constr(E) :=
let F := (eval simpl in E) in change F with E.
unsimpl E in H is similar to unsimpl E but it applies
inside a particular hypothesis H.
Tactic Notation "unsimpl" constr(E) "in" hyp(H) :=
let F := (eval simpl in E) in change F with E in H.
unsimpl E in * applies unsimpl E everywhere possible.
Tactic Notation "unsimpl" constr(E) "in" "*" :=
let F := (eval simpl in E) in change F with E in *.
substs does the same as subst, except that it does not fail
when there are circular equalities in the context.
Tactic Notation "substs" :=
repeat (match goal with H: ?x = ?y |- _ =>
first [ subst x | subst y ] end).
Implementation of substs below.
Ltac substs_below limit :=
match goal with H: ?T |- _ =>
match T with
| limit => idtac
| ?x = ?y =>
first [ subst x; substs_below limit
| subst y; substs_below limit
| generalizes H; substs_below limit; intro ]
end end.
substs below body E applies subst on all equalities that appear
in the context below the first hypothesis whose body is E.
If there is no such hypothesis in the context, it is equivalent
to subst. For instance, if H is an hypothesis, then
substs below H will substitute equalities below hypothesis H.
Tactic Notation "substs" "below" "body" constr(M) :=
substs_below M.
substs below H applies subst on all equalities that appear
in the context below the hypothesis named H. Note that
the current implementation is technically incorrect since it
will confuse different hypotheses with the same body.
Tactic Notation "substs" "below" hyp(H) :=
match type of H with ?M => substs below body M end.
fequal is a variation on f_equal which has a better behaviour
on equalities between n-ary tuples.
Ltac fequal_base :=
let go := f_equal; [ fequal_base | ] in
match goal with
| |- (_,_,_) = (_,_,_) => go
| |- (_,_,_,_) = (_,_,_,_) => go
| |- (_,_,_,_,_) = (_,_,_,_,_) => go
| |- (_,_,_,_,_,_) = (_,_,_,_,_,_) => go
| |- _ => f_equal
end.
Tactic Notation "fequal" :=
fequal_base.
fequals is the same as fequal except that it tries and solve
all trivial subgoals, using reflexivity and congruence.
(It applies to goals of the form f x1 .. xN = f y1 .. yN
and may produce subgoals of the form xi = yi).
Ltac fequal_post :=
first [ reflexivity | congruence | idtac ].
Tactic Notation "fequals" :=
fequal; fequal_post.
fequals_rec calls fequals recursively.
It is equivalent to repeat progress( fequals).
Tactic Notation "fequals_rec" :=
repeat (progress fequals).
ltac_Mark and ltac_mark are dummy definitions used as sentinel
by tactics. The ltac_mark has type ltac_Mark.
gen_until_mark repeats generalize on hypotheses from the
context, starting from the bottom and stopping as soon as reaching
an hypothesis of type Mark. If fails if Mark does not
appear in the context.
Ltac gen_until_mark :=
match goal with H: ?T |- _ =>
match T with
| ltac_Mark => clear H
| _ => generalizes H; gen_until_mark
end end.
intro_until_mark repeats intro until reaching an hypothesis of
type Mark. It throws away the hypothesis Mark.
It fails if Mark does not appear as an hypothesis in the
goal.
Ltac intro_until_mark :=
match goal with
| |- (ltac_Mark -> _) => intros _
| _ => intro; intro_until_mark
end.
invert keep H is same to inversion H except that it puts all the
facts obtained in the goal. The keyword keep means that the
hypothesis H should not be removed.
invert keep H as X1 .. XN is the same as inversion H as ... except
that only hypotheses which are not variable need to be named
explicitely, in a similar fashion as introv is used to name
only hypotheses.
Tactic Notation "invert" "keep" hyp(H) "as" simple_intropattern(I1) :=
invert keep H; introv I1.
Tactic Notation "invert" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
invert keep H; introv I1 I2.
Tactic Notation "invert" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
invert keep H; introv I1 I2 I3.
invert H is same to inversion H except that it puts all the
facts obtained in the goal and clears hypothesis H.
In other words, it is equivalent to invert keep H; clear H.
Tactic Notation "invert" hyp(H) :=
invert keep H; clear H.
invert H as X1 .. XN is the same as invert keep H as X1 .. XN
but it also clears hypothesis H.
Tactic Notation "invert_tactic" hyp(H) tactic(tac) :=
let H' := fresh in rename H into H'; tac H'; clear H'.
Tactic Notation "invert" hyp(H) "as" simple_intropattern(I1) :=
invert_tactic H (fun H => invert keep H as I1).
Tactic Notation "invert" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
invert_tactic H (fun H => invert keep H as I1 I2).
Tactic Notation "invert" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
invert_tactic H (fun H => invert keep H as I1 I2 I3).
Our inversion tactics is able to get rid of dependent equalities
generated by inversion, using proof irrelevance.
Require Import Eqdep.
Ltac inverts_tactic H i1 i2 i3 i4 i5 :=
let rec go i1 i2 i3 i4 i5 :=
match goal with
| |- (ltac_Mark -> _) => intros _
| |- (?x = ?y -> _) => let H := fresh in intro H;
first [ subst x | subst y ];
go i1 i2 i3 i4 i5
| |- (existT ?P ?p ?x = existT ?P ?p ?y -> _) =>
let H := fresh in intro H;
generalize (@EqdepTheory.inj_pair2 _ P p x y H);
clear H; go i1 i2 i3 i4 i5
| |- (?P -> ?Q) => i1; go i2 i3 i4 i5 ltac:(intro)
| |- (forall _, _) => intro; go i1 i2 i3 i4 i5
end in
generalize ltac_mark; invert keep H; go i1 i2 i3 i4 i5.
inverts keep H is same to invert keep H except that it
applies subst to all the equalities generated by the inversion.
Tactic Notation "inverts" "keep" hyp(H) :=
inverts_tactic H ltac:(intro) ltac:(intro) ltac:(intro) ltac:(intro) ltac:(intro).
inverts keep H as X1 .. XN is the same as
invert keep H as X1 .. XN except that it applies subst to all the
equalities generated by the inversion
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1) :=
inverts_tactic H ltac:(intros I1)
ltac:(intro) ltac:(intro) ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2)
ltac:(intro) ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intro) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intros I4) ltac:(intro).
Tactic Notation "inverts" "keep" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) :=
inverts_tactic H ltac:(intros I1) ltac:(intros I2) ltac:(intros I3)
ltac:(intros I4) ltac:(intros I5).
inverts H is same to inverts keep H except that it
clears hypothesis H.
Tactic Notation "inverts" hyp(H) :=
inverts keep H; clear H.
inverts H as X1 .. XN is the same as inverts keep H as X1 .. XN
but it also clears the hypothesis H.
Tactic Notation "inverts_tactic" hyp(H) tactic(tac) :=
let H' := fresh in rename H into H'; tac H'; clear H'.
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1) :=
invert_tactic H (fun H => inverts keep H as I1).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) :=
invert_tactic H (fun H => inverts keep H as I1 I2).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3 I4).
Tactic Notation "inverts" hyp(H) "as" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) simple_intropattern(I4)
simple_intropattern(I5) :=
invert_tactic H (fun H => inverts keep H as I1 I2 I3 I4 I5).
Underlying implementation of injects
Ltac injects_tactic H :=
let rec go _ :=
match goal with
| |- (ltac_Mark -> _) => intros _
| |- (?x = ?y -> _) => let H := fresh in intro H;
first [ subst x | subst y | idtac ];
go tt
end in
generalize ltac_mark; injection H; go tt.
injects keep H takes an hypothesis H of the form
C a1 .. aN = C b1 .. bN and substitute all equalities
ai = bi that have been generated.
Tactic Notation "injects" "keep" hyp(H) :=
injects_tactic H.
injects H is similar to injects keep H but clears
the hypothesis H.
Tactic Notation "injects" hyp(H) :=
injects_tactic H; clear H.
inject H as X1 .. XN is the same as injection
followed by intros X1 .. XN
Tactic Notation "inject" hyp(H) :=
injection H.
Tactic Notation "inject" hyp(H) "as" ident(X1) :=
injection H; intros X1.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) :=
injection H; intros X1 X2.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) ident(X3) :=
injection H; intros X1 X2 X3.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) ident(X3)
ident(X4) :=
injection H; intros X1 X2 X3 X4.
Tactic Notation "inject" hyp(H) "as" ident(X1) ident(X2) ident(X3)
ident(X4) ident(X5) :=
injection H; intros X1 X2 X3 X4 X5.
The tactics inversions and injections provided in this section
are similar to inverts and injects except that they perform
substitution on all equalities from the context and not only
the ones freshly generated. The counterpart is that they have
simpler implementations.
inversions keep H is the same as inversions H but it does
not clear hypothesis H.
Tactic Notation "inversions" "keep" hyp(H) :=
inversion H; subst.
inversions H is a shortcut for inversion H followed by subst
and clear H.
It is a rough implementation of inverts keep H which behave
badly when the proof context already contains equalities.
It is provided in case the better implementation turns out to be
too slow.
Tactic Notation "inversions" hyp(H) :=
inversion H; subst; clear H.
injections keep H is the same as injection H followed
by intros and subst. It is a rough implementation of
injects keep H which behave
badly when the proof context already contains equalities,
or when the goal starts with a forall or an implication.
Tactic Notation "injections" "keep" hyp(H) :=
injection H; intros; subst.
injections H is the same as injection H followed
by intros and clear H and subst. It is a rough
implementation of injects keep H which behave
badly when the proof context already contains equalities,
or when the goal starts with a forall or an implication.
Tactic Notation "injections" "keep" hyp(H) :=
injection H; clear H; intros; subst.
gen_eq X: E is a tactic whose purpose is to introduce
equalities so as to work around the limitation of the induction
tactic which typically loses information. gen_eq E as X replaces
all occurences of term E with a fresh variable X and the equality
X = E as extra hypothesis to the current conclusion. In other words
a conclusion C will be turned into (X = E) -> C.
gen_eq: E and gen_eq: E as X are also accepted.
Tactic Notation "gen_eq" ident(X) ":" constr(E) :=
let EQ := fresh in sets_eq X EQ: E; revert EQ.
Tactic Notation "gen_eq" ":" constr(E) :=
let X := fresh "X" in gen_eq X: E.
Tactic Notation "gen_eq" ":" constr(E) "as" ident(X) :=
gen_eq X: E.
Tactic Notation "gen_eq" ident(X1) ":" constr(E1) ","
ident(X2) ":" constr(E2) :=
gen_eq X2: E2; gen_eq X1: E1.
Tactic Notation "gen_eq" ident(X1) ":" constr(E1) ","
ident(X2) ":" constr(E2) "," ident(X3) ":" constr(E3) :=
gen_eq X3: E3; gen_eq X2: E2; gen_eq X1: E1.
inductions H is a tactic similar to induction H except
that it is able to introduce equalities in order to avoid
losing information. The syntax inductions H gen X1 .. XN end
is to be used in order to generalize variables before the
induction. Syntaxes inductions H as I and
inductions H as I gen X1 .. XN end are also available.
For technical reasons, the tactic inductions will not introduce equalities for constants (e.g. if H has type P 0 x, inductions will not save the information that the first argument is 0). However, the tactic inductions' aggressively introduces equalities for all arguments, and will apply. The reason why inductions' is not the default tactic is that it may introduce more equalities than necessary, which slightly pollutes the induction hypotheses.
For technical reasons, the tactic inductions will not introduce equalities for constants (e.g. if H has type P 0 x, inductions will not save the information that the first argument is 0). However, the tactic inductions' aggressively introduces equalities for all arguments, and will apply. The reason why inductions' is not the default tactic is that it may introduce more equalities than necessary, which slightly pollutes the induction hypotheses.
Ltac gen_eq_for_inductions H E :=
let X := fresh "aux" in
let HX := fresh "H" X in
set (X := E) in H |-;
assert (HX : ltac_tag_subst (X = E));
[ unfold ltac_tag_subst; reflexivity
| clearbody X; generalizes HX ].
Ltac apply_to_all_args T go :=
match T with
| ?P ?E1 ?E2 ?E3 ?E4 ?E5 ?E6 => go E1; go E2; go E3; go E4; go E5; go E6
| ?P ?E1 ?E2 ?E3 ?E4 ?E5 => go E1; go E2; go E3; go E4; go E5
| ?P ?E1 ?E2 ?E3 ?E4 => go E1; go E2; go E3; go E4
| ?P ?E1 ?E2 ?E3 => go E1; go E2; go E3
| ?P ?E1 ?E2 => go E1; go E2
| ?P ?E1 => go E1
end.
Tactic Notation "gen_eqs'" hyp(H) :=
let T := type of H in
let go E := try gen_eq_for_inductions H E in
apply_to_all_args T go.
Tactic Notation "gen_eqs" hyp(H) :=
let T := type of H in
let go E :=
match is_metavar E with
| true => idtac
| false => try gen_eq_for_inductions H E
end in
apply_to_all_args T go.
Ltac intros_head_ltac_tag_subst :=
try match goal with |- (ltac_tag_subst (?x = ?y) -> ?Q) =>
let H := fresh "Aux" in
intro H; unfold ltac_tag_subst in H;
try subst x; intros_head_ltac_tag_subst
end.
Ltac inductions_post :=
intros_head_ltac_tag_subst;
unfold ltac_tag_subst in * |-.
Tactic Notation "inductions" hyp(H) :=
gen_eqs H; induction H; inductions_post.
Tactic Notation "inductions" hyp(H) "as" simple_intropattern(I) :=
gen_eqs H; induction H as I; inductions_post.
Tactic Notation "inductions" hyp(H) tactic(Gen) "end" :=
gen_eqs H; Gen; induction H; inductions_post.
Tactic Notation "inductions" hyp(H) "as" simple_intropattern(I)
tactic(Gen) "end" :=
gen_eqs H; Gen; induction H as I; inductions_post.
Tactic Notation "inductions'" hyp(H) :=
gen_eqs' H; induction H; inductions_post.
Tactic Notation "inductions'" hyp(H) "as" simple_intropattern(I) :=
gen_eqs' H; induction H as I; inductions_post.
Tactic Notation "inductions'" hyp(H) tactic(Gen) "end" :=
gen_eqs' H; Gen; induction H; inductions_post.
Tactic Notation "inductions'" hyp(H) "as" simple_intropattern(I)
tactic(Gen) "end" :=
gen_eqs' H; Gen; induction H as I; inductions_post.
induction_wf IH: E X is used to apply the well-founded induction
principle, for a given well-founded relation. It applies to a goal
PX where PX is a proposition on X. First, it sets up the
goal in the form fun( a => P a) X, using pattern X, and then
it applies the well-founded induction principle instantiated on E,
where E is a term of type well_founded R, and R is a binary
relation.
Syntaxes induction_wf: E X and induction_wf E X
and induction_wf E X as IH are also available.
Tactic Notation "induction_wf" ident(IH) ":" constr(E) ident(X) :=
pattern X; apply (well_founded_ind E); clear X; intros X IH.
Tactic Notation "induction_wf" ":" constr(E) ident(X) :=
let IH := fresh "IH" in induction_wf IH: E X.
Tactic Notation "induction_wf" ":" constr(E) ident(X) "as" ident(IH) :=
induction_wf IH: E X.
Tactic Notation "induction_wf" ":" constr(E) ident(X) :=
induction_wf: E X.
decides_equality is the same as decide equality excepts that it
is able to unfold definitions at head of the current goal.
Ltac decides_equality_tactic :=
first [ decide equality | progress(unfolds); decides_equality_tactic ].
Tactic Notation "decides_equality" :=
decides_equality_tactic.
N-ary Conjunctions Splitting in Goals
Underlying implementation of splits.
Ltac splits_tactic N :=
match N with
| O => fail
| S O => idtac
| S ?N' => split; [| splits_tactic N']
end.
Ltac unfold_goal_until_conjunction :=
match goal with
| |- _ /\ _ => idtac
| _ => progress(unfolds); unfold_goal_until_conjunction
end.
Ltac get_term_conjunction_arity T :=
match T with
| _ /\ _ /\ _ /\ _ /\ _ /\ _ /\ _ /\ _ => constr:(8)
| _ /\ _ /\ _ /\ _ /\ _ /\ _ /\ _ => constr:(7)
| _ /\ _ /\ _ /\ _ /\ _ /\ _ => constr:(6)
| _ /\ _ /\ _ /\ _ /\ _ => constr:(5)
| _ /\ _ /\ _ /\ _ => constr:(4)
| _ /\ _ /\ _ => constr:(3)
| _ /\ _ => constr:(2)
| _ -> ?T' => get_term_conjunction_arity T'
| _ => let P := get_head T in
let T' := eval unfold P in T in
match T' with
| T => fail 1
| _ => get_term_conjunction_arity T'
end
end.
Ltac get_goal_conjunction_arity :=
match goal with |- ?T => get_term_conjunction_arity T end.
splits applies to a goal of the form (T1 /\ .. /\ TN) and
destruct it into N subgoals T1 .. TN. If the goal is not a
conjunction, then it unfolds the head definition.
Tactic Notation "splits" :=
unfold_goal_until_conjunction;
let N := get_goal_conjunction_arity in
splits_tactic N.
splits N is similar to splits, except that it will unfold as many
definitions as necessary to obtain an N-ary conjunction.
Tactic Notation "splits" constr(N) :=
splits_tactic N.
splits_all will recursively split any conjunction, unfolding
definitions when necessary.
Tactic Notation "splits_all" :=
repeat split.
N-ary Conjunctions Deconstruction
Underlying implementation of destructs.
Ltac destructs_conjunction_tactic N T :=
match N with
| 2 => destruct T as [? ?]
| 3 => destruct T as [? [? ?]]
| 4 => destruct T as [? [? [? ?]]]
| 5 => destruct T as [? [? [? [? ?]]]]
| 6 => destruct T as [? [? [? [? [? ?]]]]]
| 7 => destruct T as [? [? [? [? [? [? ?]]]]]]
end.
destructs T allows destructing a term T which is a N-ary
conjunction. It is equivalent to destruct T as (H1 .. HN),
except that it does not require to manually specify N different
names.
Tactic Notation "destructs" constr(T) :=
let TT := type of T in
let N := get_term_conjunction_arity TT in
destructs_conjunction_tactic N T.
destructs N T is equivalent to destruct T as (H1 .. HN),
except that it does not require to manually specify N different
names. Remark that it is not restricted to N-ary conjunctions.
Tactic Notation "destructs" constr(N) constr(T) :=
destructs_conjunction_tactic N T.
Proving goals which are N-ary disjunctions
Underlying implementation of branch.
Ltac branch_tactic K N :=
match constr:(K,N) with
| (_,0) => fail 1
| (0,_) => fail 1
| (1,1) => idtac
| (1,_) => left
| (S ?K', S ?N') => right; branch_tactic K' N'
end.
Ltac unfold_goal_until_disjunction :=
match goal with
| |- _ \/ _ => idtac
| _ => progress(unfolds); unfold_goal_until_disjunction
end.
Ltac get_term_disjunction_arity T :=
match T with
| _ \/ _ \/ _ \/ _ \/ _ \/ _ \/ _ \/ _ => constr:(8)
| _ \/ _ \/ _ \/ _ \/ _ \/ _ \/ _ => constr:(7)
| _ \/ _ \/ _ \/ _ \/ _ \/ _ => constr:(6)
| _ \/ _ \/ _ \/ _ \/ _ => constr:(5)
| _ \/ _ \/ _ \/ _ => constr:(4)
| _ \/ _ \/ _ => constr:(3)
| _ \/ _ => constr:(2)
| _ -> ?T' => get_term_disjunction_arity T'
| _ => let P := get_head T in
let T' := eval unfold P in T in
match T' with
| T => fail 1
| _ => get_term_disjunction_arity T'
end
end.
Ltac get_goal_disjunction_arity :=
match goal with |- ?T => get_term_disjunction_arity T end.
branch N applies to a goal of the form
P1 \/ ... \/ PK \/ ... \/ PN and leaves the goal PK.
It only able to unfold the head definition (if there is one),
but for more complex unfolding one should use the tactic
branch K of N.
Tactic Notation "branch" constr(K) :=
unfold_goal_until_disjunction;
let N := get_goal_disjunction_arity in
branch_tactic K N.
branch K of N is similar to branch K except that the
arity of the disjunction N is given manually, and so this
version of the tactic is able to unfold definitions.
In other words, applies to a goal of the form
P1 \/ ... \/ PK \/ ... \/ PN and leaves the goal PK.
Tactic Notation "branch" constr(K) "of" constr(N) :=
branch_tactic K N.
N-ary Disjunction Deconstruction
Underlying implementation of branches.
Ltac destructs_disjunction_tactic N T :=
match N with
| 2 => destruct T as [? | ?]
| 3 => destruct T as [? | ? | ?]
| 4 => destruct T as [? | ? | ? | ?]
| 5 => destruct T as [? | ? | ? | ? | ?]
| 6 => destruct T as [? | ? | ? | ? | ? | ?]
| 7 => destruct T as [? | ? | ? | ? | ? | ? | ?]
end.
branches T allows destructing a term T which is a N-ary
disjunction. It is equivalent to destruct T as [ H1 | .. | HN ] ,
and produces N subgoals corresponding to the N possible cases.
Tactic Notation "branches" constr(T) :=
let TT := type of T in
let N := get_term_disjunction_arity TT in
destructs_disjunction_tactic N T.
branches N T is the same as branches T except that the arity is
forced to N. This version is useful to unfold definitions
on the fly.
Tactic Notation "branches" constr(N) constr(T) :=
destructs_disjunction_tactic T N.
N-ary Existentials
Ltac get_term_existential_arity T :=
match T with
| exist x1 x2 x3 x4 x5 x6 x7 x8, _ => constr:(8)
| exist x1 x2 x3 x4 x5 x6 x7, _ => constr:(7)
| exist x1 x2 x3 x4 x5 x6, _ => constr:(6)
| exist x1 x2 x3 x4 x5, _ => constr:(5)
| exist x1 x2 x3 x4, _ => constr:(4)
| exist x1 x2 x3, _ => constr:(3)
| exist x1 x2, _ => constr:(2)
| exist x1, _ => constr:(1)
| _ -> ?T' => get_term_existential_arity T'
| _ => let P := get_head T in
let T' := eval unfold P in T in
match T' with
| T => fail 1
| _ => get_term_existential_arity T'
end
end.
Ltac get_goal_existential_arity :=
match goal with |- ?T => get_term_existential_arity T end.
exists T1 ... TN is a shorthand for exists T1; ...; exists TN.
It is intended to prove goals of the form exist X1 .. XN, P.
If an argument provided is __ (double underscore), then an
evar is introduced. Calling exists without any argument is
equivalent to calling exists __ ... __ for the appropriate
number of arguments. Last, exists__ N is short for
exists __ ... __ with N double-underscores.
Tactic Notation "exists_original" constr(T1) :=
exists T1.
Tactic Notation "exists" constr(T1) :=
match T1 with
| ltac_wild => esplit
| _ => exists T1
end.
Tactic Notation "exists" constr(T1) constr(T2) :=
exists T1; exists T2.
Tactic Notation "exists" constr(T1) constr(T2) constr(T3) :=
exists T1; exists T2; exists T3.
Tactic Notation "exists" constr(T1) constr(T2) constr(T3) constr(T4) :=
exists T1; exists T2; exists T3; exists T4.
Tactic Notation "exists" constr(T1) constr(T2) constr(T3) constr(T4)
constr(T5) :=
exists T1; exists T2; exists T3; exists T4; exists T5.
Tactic Notation "exists" constr(T1) constr(T2) constr(T3) constr(T4)
constr(T5) constr(T6) :=
exists T1; exists T2; exists T3; exists T4; exists T5; exists T6.
Tactic Notation "exists__" constr(N) :=
let rec aux N :=
match N with
| 0 => idtac
| S ?N' => esplit; aux N'
end in
aux N.
Tactic Notation "exists__" :=
let N := get_goal_existential_arity in
exists__ N.
The two following tactics defined the default behaviour of
"light automation" and "strong automation". These tactics
may be redefined at any time using the syntax Ltac .. ::= ...
auto_tilde is the tactic which will be called each time a symbol
~ is used after a tactic.
Ltac auto_tilde := auto.
auto_star is the tactic which will be called each time a symbol
* is used after a tactic.
Ltac auto_star := try solve [ auto | intuition eauto ].
auto~ is a notation for tactic auto_tilde. It may be followed
by lemmas (or proofs terms) which auto will be able to use
for solving the goal.
Tactic Notation "auto" "~" :=
auto_tilde.
Tactic Notation "auto" "~" constr(E1) :=
lets: E1; auto_tilde.
Tactic Notation "auto" "~" constr(E1) constr(E2) :=
lets: E1 E2; auto_tilde.
Tactic Notation "auto" "~" constr(E1) constr(E2) constr(E3) :=
lets: E1 E2 E3; auto_tilde.
auto* is a notation for tactic auto_star. It may be followed
by lemmas (or proofs terms) which auto will be able to use
for solving the goal.
Tactic Notation "auto" "*" :=
auto_star.
Tactic Notation "auto" "*" constr(E1) :=
lets: E1; auto_star.
Tactic Notation "auto" "*" constr(E1) constr(E2) :=
lets: E1 E2; auto_star.
Tactic Notation "auto" "*" constr(E1) constr(E2) constr(E3) :=
lets: E1 E2 E3; auto_star.
Tactic Notation "f_equal" :=
f_equal.
Tactic Notation "constructor" :=
constructor.
Tactic Notation "simple" :=
simpl.
Any tactic followed by the symbol ~ will have auto_tilde called
on all of its subgoals. Three exceptions:
- cuts and asserts only call auto on their first subgoal,
- apply~ relies on sapply rather than apply,
- tryfalse~ is defined as tryfalse by auto_tilde.
Tactic Notation "apply" "~" constr(H) :=
sapply H; auto_tilde.
Tactic Notation "applys" "~" constr(H) :=
sapply H; auto_tilde.
Tactic Notation "apply_clear" "~" constr(H) :=
apply_clear H; auto_tilde.
Tactic Notation "applys_clear" "~" constr(H) :=
applys_clear H; auto_tilde.
Tactic Notation "destruct" "~" constr(H) :=
destruct H; auto_tilde.
Tactic Notation "destruct" "~" constr(H) "as" simple_intropattern(I) :=
destruct H as I; auto_tilde.
Tactic Notation "f_equal" "~" :=
f_equal; auto_tilde.
Tactic Notation "induction" "~" constr(H) :=
induction H; auto_tilde.
Tactic Notation "inversion" "~" constr(H) :=
inversion H; auto_tilde.
Tactic Notation "split" "~" :=
split; auto_tilde.
Tactic Notation "subst" "~" :=
subst; auto_tilde.
Tactic Notation "right" "~" :=
right; auto_tilde.
Tactic Notation "left" "~" :=
left; auto_tilde.
Tactic Notation "constructor" "~" :=
constructor; auto_tilde.
Tactic Notation "false" "~" :=
false; auto_tilde.
Tactic Notation "false" "~" constr(T) :=
false_with_plus T ltac:(auto_tilde).
Tactic Notation "tryfalse" "~" :=
tryfalse by auto_tilde/.
Tactic Notation "asserts" "~" simple_intropattern(H) ":" constr(E) :=
asserts H: E; [ auto_tilde | idtac ].
Tactic Notation "cuts" "~" simple_intropattern(H) ":" constr(E) :=
cuts H: E; [ auto_tilde | idtac ].
Tactic Notation "cuts" "~" ":" constr(E) :=
cuts: E; [ auto_tilde | idtac ].
Tactic Notation "lets" "~" simple_intropattern(I) ":" constr(E) :=
lets I: E; auto_tilde.
Tactic Notation "lets" "~" ":" constr(E1) :=
lets: E1; auto_tilde.
Tactic Notation "lets" "~" ":" constr(E1) constr(E2) :=
lets: E1 E2; auto_tilde.
Tactic Notation "lets" "~" ":" constr(E1) constr(E2) constr(E3) :=
lets: E1 E2 E3; auto_tilde.
Tactic Notation "applys" "~" constr(E) constr(A) :=
applys E A; auto_tilde.
Tactic Notation "specializes" "~" hyp(H) constr(A) :=
specializes H A; auto_tilde.
Tactic Notation "lets" "~" simple_intropattern(I) ":" constr(E) constr(A) :=
lets I: E A; auto_tilde.
Tactic Notation "lets" "~" simple_intropattern(I) ":" constr(E) constr(A1) constr(A2) :=
lets I: E A1 A2; auto_tilde.
Tactic Notation "lets" "~" simple_intropattern(I) ":" constr(E) constr(A1) constr(A2) constr(A3) :=
lets I: E A1 A2 A3; auto_tilde.
Tactic Notation "forwards" "~" simple_intropattern(I) ":" constr(E) :=
forwards I: E; auto_tilde.
Tactic Notation "forwards" "~" ":" constr(E) :=
forwards: E; auto_tilde.
Tactic Notation "fapply" "~" constr(E) :=
fapply E; auto_tilde.
Tactic Notation "sapply" "~" constr(E) :=
sapply E; auto_tilde.
Tactic Notation "intros_all" "~" :=
intros_all; auto_tilde.
Tactic Notation "unfolds" "~" reference(F1) :=
unfolds F1; auto_tilde.
Tactic Notation "unfolds" "~" reference(F1) reference(F2) :=
unfolds F1, F2; auto_tilde.
Tactic Notation "unfolds" "~" reference(F1) reference(F2) reference(F3) :=
unfolds F1, F2, F3; auto_tilde.
Tactic Notation "unfolds" "~" reference(F1) reference(F2) reference(F3)
reference(F4) :=
unfolds F1, F2, F3, F4; auto_tilde.
Tactic Notation "simple" "~" :=
simpl; auto_tilde.
Tactic Notation "simple" "~" "in" hyp(H) :=
simpl in H; auto_tilde.
Tactic Notation "simpls" "~" :=
simpls; auto_tilde.
Tactic Notation "substs" "~" :=
substs; auto_tilde.
Tactic Notation "rewrite" "~" constr(E) :=
rewrite E; auto_tilde.
Tactic Notation "rewrite" "~" "<-" constr(E) :=
rewrite <- E; auto_tilde.
Tactic Notation "rewrite" "~" constr(E) "in" hyp(H) :=
rewrite E in H; auto_tilde.
Tactic Notation "rewrite" "~" "<-" constr(E) "in" hyp(H) :=
rewrite <- E in H; auto_tilde.
Tactic Notation "rewrite_all" "~" constr(E) :=
rewrite_all E; auto_tilde.
Tactic Notation "rewrite_all" "~" "<-" constr(E) :=
rewrite_all <- E; auto_tilde.
Tactic Notation "rewrite_all" "~" constr(E) "in" ident(H) :=
rewrite_all E in H; auto_tilde.
Tactic Notation "rewrite_all" "~" "<-" constr(E) "in" ident(H) :=
rewrite_all <- E in H; auto_tilde.
Tactic Notation "rewrite_all" "~" constr(E) "in" "*" :=
rewrite_all E in *; auto_tilde.
Tactic Notation "rewrite_all" "~" "<-" constr(E) "in" "*" :=
rewrite_all <- E in *; auto_tilde.
Tactic Notation "asserts_rewrite" "~" constr(E) :=
asserts_rewrite E; auto_tilde.
Tactic Notation "asserts_rewrite" "~" "<-" constr(E) :=
asserts_rewrite <- E; auto_tilde.
Tactic Notation "asserts_rewrite" "~" constr(E) "in" hyp(H) :=
asserts_rewrite E in H; auto_tilde.
Tactic Notation "asserts_rewrite" "~" "<-" constr(E) "in" hyp(H) :=
asserts_rewrite <- E in H; auto_tilde.
Tactic Notation "cuts_rewrite" "~" constr(E) :=
cuts_rewrite E; auto_tilde.
Tactic Notation "cuts_rewrite" "~" "<-" constr(E) :=
cuts_rewrite <- E; auto_tilde.
Tactic Notation "cuts_rewrite" "~" constr(E) "in" hyp(H) :=
cuts_rewrite E in H; auto_tilde.
Tactic Notation "cuts_rewrite" "~" "<-" constr(E) "in" hyp(H) :=
cuts_rewrite <- E in H; auto_tilde.
Tactic Notation "fequal" "~" :=
fequal; auto_tilde.
Tactic Notation "fequals" "~" :=
fequals; auto_tilde.
Tactic Notation "invert" "~" hyp(H) :=
invert H; auto_tilde.
Tactic Notation "inverts" "~" hyp(H) :=
inverts H; auto_tilde.
Tactic Notation "injects" "~" hyp(H) :=
injects H; auto_tilde.
Tactic Notation "inversions" "~" hyp(H) :=
inversions H; auto_tilde.
Tactic Notation "decides_equality" "~" :=
decides_equality; auto_tilde.
Tactic Notation "splits" "~" :=
splits; auto_tilde.
Tactic Notation "splits" "~" constr(N) :=
splits N; auto_tilde.
Tactic Notation "destructs" "~" constr(T) :=
destructs T; auto_tilde.
Tactic Notation "destructs" "~" constr(N) constr(T) :=
destructs N T; auto_tilde.
Tactic Notation "branch" "~" constr(N) :=
branch N; auto_tilde.
Tactic Notation "branch" "~" constr(K) "of" constr(N) :=
branch K of N; auto_tilde.
Tactic Notation "branches" "~" constr(T) :=
branches T; auto_tilde.
Tactic Notation "branches" "~" constr(N) constr(T) :=
branches N T; auto_tilde.
Tactic Notation "exists__" "~" :=
exists__; auto_tilde.
Tactic Notation "exists" "~" constr(T1) :=
exists T1; auto_tilde.
Tactic Notation "exists" "~" constr(T1) constr(T2) :=
exists T1 T2; auto_tilde.
Tactic Notation "exists" "~" constr(T1) constr(T2) constr(T3) :=
exists T1 T2 T3; auto_tilde.
Tactic Notation "exists" "~" constr(T1) constr(T2) constr(T3) constr(T4) :=
exists T1 T2 T3 T4; auto_tilde.
Tactic Notation "exists" "~" constr(T1) constr(T2) constr(T3) constr(T4)
constr(T5) :=
exists T1 T2 T3 T4 T5; auto_tilde.
Tactic Notation "exists" "~" constr(T1) constr(T2) constr(T3) constr(T4)
constr(T5) constr(T6) :=
exists T1 T2 T3 T4 T5 T6; auto_tilde.
Any tactic followed by the symbol * will have auto* called
on all of its subgoals. The exceptions to these rules are the
same as for light automation.
Tactic Notation "apply" "*" constr(H) :=
sapply H; auto_star.
Tactic Notation "applys" "*" constr(H) :=
sapply H; auto_star.
Tactic Notation "apply_clear" "*" constr(H) :=
applys_clear H; auto_star.
Tactic Notation "applys_clear" "*" constr(H) :=
applys_clear H; auto_star.
Tactic Notation "destruct" "*" constr(H) :=
destruct H; auto_star.
Tactic Notation "destruct" "*" constr(H) "as" simple_intropattern(I) :=
destruct H as I; auto_star.
Tactic Notation "f_equal" "*" :=
f_equal; auto_star.
Tactic Notation "induction" "*" constr(H) :=
induction H; auto_star.
Tactic Notation "inversion" "*" constr(H) :=
inversion H; auto_star.
Tactic Notation "split" "*" :=
split; auto_star.
Tactic Notation "subst" "*" :=
subst; auto_star.
Tactic Notation "right" "*" :=
right; auto_star.
Tactic Notation "left" "*" :=
left; auto_star.
Tactic Notation "constructor" "*" :=
constructor; auto_star.
Tactic Notation "false" "*" :=
false; auto_star.
Tactic Notation "false" "*" constr(T) :=
false_with_plus T ltac:(auto_star).
Tactic Notation "tryfalse" "*" :=
tryfalse by auto_star/.
Tactic Notation "asserts" "*" simple_intropattern(H) ":" constr(E) :=
asserts H: E; [ auto_star | idtac ].
Tactic Notation "cuts" "*" simple_intropattern(H) ":" constr(E) :=
cuts H: E; [ auto_star | idtac ].
Tactic Notation "cuts" "*" ":" constr(E) :=
cuts: E; [ auto_star | idtac ].
Tactic Notation "lets" "*" simple_intropattern(I) ":" constr(E) :=
lets I: E; auto_star.
Tactic Notation "lets" "*" ":" constr(E1) :=
lets: E1; auto_star.
Tactic Notation "lets" "*" ":" constr(E1) constr(E2) :=
lets: E1 E2; auto_star.
Tactic Notation "lets" "*" ":" constr(E1) constr(E2) constr(E3) :=
lets: E1 E2 E3; auto_star.
Tactic Notation "applys" "*" constr(E) constr(A) :=
applys E A; auto_star.
Tactic Notation "specializes" "*" hyp(H) constr(A) :=
specializes H A; auto_star.
Tactic Notation "lets" "*" simple_intropattern(I) ":" constr(E) constr(A) :=
lets I: E A; auto_star.
Tactic Notation "lets" "*" simple_intropattern(I) ":" constr(E) constr(A1) constr(A2) :=
lets I: E A1 A2; auto_star.
Tactic Notation "lets" "*" simple_intropattern(I) ":" constr(E) constr(A1) constr(A2) constr(A3) :=
lets I: E A1 A2 A3; auto_star.
Tactic Notation "forwards" "*" simple_intropattern(I) ":" constr(E) :=
forwards I: E; auto_star.
Tactic Notation "forwards" "*" ":" constr(E) :=
forwards: E; auto_star.
Tactic Notation "fapply" "*" constr(E) :=
fapply E; auto_star.
Tactic Notation "sapply" "*" constr(E) :=
sapply E; auto_star.
Tactic Notation "intros_all" "*" :=
intros_all; auto_star.
Tactic Notation "unfolds" "*" reference(F1) :=
unfolds F1; auto_star.
Tactic Notation "unfolds" "*" reference(F1) reference(F2) :=
unfolds F1, F2; auto_star.
Tactic Notation "unfolds" "*" reference(F1) reference(F2) reference(F3) :=
unfolds F1, F2, F3; auto_star.
Tactic Notation "unfolds" "*" reference(F1) reference(F2) reference(F3)
reference(F4) :=
unfolds F1, F2, F3, F4; auto_star.
Tactic Notation "simple" "*" :=
simpl; auto_star.
Tactic Notation "simple" "*" "in" hyp(H) :=
simpl in H; auto_star.
Tactic Notation "simpls" "*" :=
simpls; auto_star.
Tactic Notation "substs" "*" :=
substs; auto_star.
Tactic Notation "rewrite" "*" constr(E) :=
rewrite E; auto_star.
Tactic Notation "rewrite" "*" "<-" constr(E) :=
rewrite <- E; auto_star.
Tactic Notation "rewrite" "*" constr(E) "in" hyp(H) :=
rewrite E in H; auto_star.
Tactic Notation "rewrite" "*" "<-" constr(E) "in" hyp(H) :=
rewrite <- E in H; auto_star.
Tactic Notation "rewrite_all" "*" constr(E) :=
rewrite_all E; auto_star.
Tactic Notation "rewrite_all" "*" "<-" constr(E) :=
rewrite_all <- E; auto_star.
Tactic Notation "rewrite_all" "*" constr(E) "in" ident(H) :=
rewrite_all E in H; auto_star.
Tactic Notation "rewrite_all" "*" "<-" constr(E) "in" ident(H) :=
rewrite_all <- E in H; auto_star.
Tactic Notation "rewrite_all" "*" constr(E) "in" "*" :=
rewrite_all E in *; auto_star.
Tactic Notation "rewrite_all" "*" "<-" constr(E) "in" "*" :=
rewrite_all <- E in *; auto_star.
Tactic Notation "asserts_rewrite" "*" constr(E) :=
asserts_rewrite E; auto_star.
Tactic Notation "asserts_rewrite" "*" "<-" constr(E) :=
asserts_rewrite <- E; auto_star.
Tactic Notation "asserts_rewrite" "*" constr(E) "in" hyp(H) :=
asserts_rewrite E; auto_star.
Tactic Notation "asserts_rewrite" "*" "<-" constr(E) "in" hyp(H) :=
asserts_rewrite <- E; auto_star.
Tactic Notation "cuts_rewrite" "*" constr(E) :=
cuts_rewrite E; auto_star.
Tactic Notation "cuts_rewrite" "*" "<-" constr(E) :=
cuts_rewrite <- E; auto_star.
Tactic Notation "cuts_rewrite" "*" constr(E) "in" hyp(H) :=
cuts_rewrite E in H; auto_star.
Tactic Notation "cuts_rewrite" "*" "<-" constr(E) "in" hyp(H) :=
cuts_rewrite <- E in H; auto_star.
Tactic Notation "fequal" "*" :=
fequal; auto_star.
Tactic Notation "fequals" "*" :=
fequals; auto_star.
Tactic Notation "invert" "*" hyp(H) :=
invert H; auto_star.
Tactic Notation "inverts" "*" hyp(H) :=
inverts H; auto_star.
Tactic Notation "injects" "*" hyp(H) :=
injects H; auto_star.
Tactic Notation "inversions" "*" hyp(H) :=
inversions H; auto_star.
Tactic Notation "decides_equality" "*" :=
decides_equality; auto_star.
Tactic Notation "splits" "*" :=
splits; auto_star.
Tactic Notation "splits" "*" constr(N) :=
splits N; auto_star.
Tactic Notation "destructs" "*" constr(T) :=
destructs T; auto_star.
Tactic Notation "destructs" "*" constr(N) constr(T) :=
destructs N T; auto_star.
Tactic Notation "branch" "*" constr(N) :=
branch N; auto_star.
Tactic Notation "branch" "*" constr(K) "of" constr(N) :=
branch K of N; auto_star.
Tactic Notation "branches" "*" constr(T) :=
branches T; auto_star.
Tactic Notation "branches" "*" constr(N) constr(T) :=
branches N T; auto_star.
Tactic Notation "exists__" "*" :=
exists__; auto_star.
Tactic Notation "exists" "*" constr(T1) :=
exists T1; auto_star.
Tactic Notation "exists" "*" constr(T1) constr(T2) :=
exists T1 T2; auto_star.
Tactic Notation "exists" "*" constr(T1) constr(T2) constr(T3) :=
exists T1 T2 T3; auto_star.
Tactic Notation "exists" "*" constr(T1) constr(T2) constr(T3) constr(T4) :=
exists T1 T2 T3 T4; auto_star.
Tactic Notation "exists" "*" constr(T1) constr(T2) constr(T3) constr(T4)
constr(T5) :=
exists T1 T2 T3 T4 T5; auto_star.
Tactic Notation "exists" "*" constr(T1) constr(T2) constr(T3) constr(T4)
constr(T5) constr(T6) :=
exists T1 T2 T3 T4 T5 T6; auto_star.
sort sorts out hypotheses from the context by moving all the
propositions (hypotheses of type Prop) to the bottom of the context.
Ltac sort_tactic :=
match goal with H: ?T |- _ =>
match type of T with Prop =>
generalizes H; (try sort_tactic); intro
end end.
Tactic Notation "sort" :=
sort_tactic.
clears X1 ... XN is a variation on clear which clears
the variables X1..XN as well as all the hypotheses which
depend on them. Contrary to clear, it never fails.
Tactic Notation "clears" ident(X1) :=
let rec doit _ :=
match goal with
| H:context[X1] |- _ => clear H; try (doit tt)
| _ => clear X1
end in doit tt.
Tactic Notation "clears" ident(X1) ident(X2) :=
clears X1; clears X2.
Tactic Notation "clears" ident(X1) ident(X2) ident(X3) :=
clears X1; clears X2; clear X3.
Tactic Notation "clears" ident(X1) ident(X2) ident(X3) ident(X4) :=
clears X1; clears X2; clear X3; clear X4.
Tactic Notation "clears" ident(X1) ident(X2) ident(X3) ident(X4)
ident(X5) :=
clears X1; clears X2; clear X3; clear X4; clear X5.
Tactic Notation "clears" ident(X1) ident(X2) ident(X3) ident(X4)
ident(X5) ident(X6) :=
clears X1; clears X2; clear X3; clear X4; clear X5; clear X6.
clears (without any argument) clears all the unused variables
from the context. In other words, it removes any variable
which is not a proposition (i.e. not of type Prop) and which
does not appear in another hypothesis nor in the goal.
Ltac clears_tactic :=
match goal with H: ?T |- _ =>
match type of T with
| Prop => generalizes H; (try clears_tactic); intro
| ?TT => clear H; (try clears_tactic)
| ?TT => generalizes H; (try clears_tactic); intro
end end.
Tactic Notation "clears" :=
clears_tactic.
clear_last clears the last hypothesis in the context.
clear_last N clears the last N hypothesis in the context.
Tactic Notation "clear_last" :=
match goal with H: ?T |- _ => clear H end.
Ltac clear_last_base n :=
match n with
| 0 => idtac
| S ?p => clear_last; clear_last_base p
end.
Tactic Notation "clear_last" constr(n) :=
clear_last_base n.
The skip tactic can be used at any time to admit the current
goal. Using skip is much more efficient than using the Focus
top-level command to reach a particular subgoal.
There are two possible implementations of skip. The first one relies on the use of an existential variable. The second one relies on an axiom of type False. Remark that the builtin tactic admit is not applicable if the current goal contains uninstantiated variables.
The advantage of the first technique is that a proof using skip must end with Admitted, since Qed will be rejected with the message "uninstantiated existential variables". It is thereafter clear that the development is incomplete.
The advantage of the second technique is exactly the converse: one may conclude the proof using Qed, and thus one saves the pain from renaming Qed into Admitted and vice-versa all the time. Note however, that it is still necessary to instantiate all the existential variables introduced by other tactics in order for Qed to be accepted.
The two implementation are provided, so that you can select the one that suits you best. By default skip' uses the first implementation, and skip uses the second implementation.
There are two possible implementations of skip. The first one relies on the use of an existential variable. The second one relies on an axiom of type False. Remark that the builtin tactic admit is not applicable if the current goal contains uninstantiated variables.
The advantage of the first technique is that a proof using skip must end with Admitted, since Qed will be rejected with the message "uninstantiated existential variables". It is thereafter clear that the development is incomplete.
The advantage of the second technique is exactly the converse: one may conclude the proof using Qed, and thus one saves the pain from renaming Qed into Admitted and vice-versa all the time. Note however, that it is still necessary to instantiate all the existential variables introduced by other tactics in order for Qed to be accepted.
The two implementation are provided, so that you can select the one that suits you best. By default skip' uses the first implementation, and skip uses the second implementation.
Ltac skip_with_existential :=
match goal with |- ?G =>
let H := fresh in evar(H:G); eexact H end.
Variable skip_axiom : False.
Ltac skip_with_axiom :=
assert False; [ apply skip_axiom | contradiction ].
Tactic Notation "skip" :=
skip_with_axiom.
Tactic Notation "skip'" :=
skip_with_existential.
skip H: T adds an assumption named H of type T to the
current context, blindly assuming that it is true.
skip: T and skip H_asserts: T and skip_asserts: T
are other possible syntax.
Note that H may be an intro pattern.
The syntax skip H1 .. HN: T can be used when T is a
conjunction of N items.
Tactic Notation "skip" simple_intropattern(I) ":" constr(T) :=
asserts I: T; [ skip | ].
Tactic Notation "skip" ":" constr(T) :=
let H := fresh in skip H: T.
Tactic Notation "skip" simple_intropattern(I1)
simple_intropattern(I2) ":" constr(T) :=
skip [I1 I2]: T.
Tactic Notation "skip" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3) ":" constr(T) :=
skip [I1 [I2 I3]]: T.
Tactic Notation "skip" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) ":" constr(T) :=
skip [I1 [I2 [I3 I4]]]: T.
Tactic Notation "skip" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5) ":" constr(T) :=
skip [I1 [I2 [I3 [I4 I5]]]]: T.
Tactic Notation "skip" simple_intropattern(I1)
simple_intropattern(I2) simple_intropattern(I3)
simple_intropattern(I4) simple_intropattern(I5)
simple_intropattern(I6) ":" constr(T) :=
skip [I1 [I2 [I3 [I4 [I5 I6]]]]]: T.
Tactic Notation "skip_asserts" simple_intropattern(I) ":" constr(T) :=
skip I: T.
Tactic Notation "skip_asserts" ":" constr(T) :=
skip: T.
skip_cuts T simply replaces the current goal with T.
Tactic Notation "skip_cuts" constr(T) :=
cuts: T; [ skip | ].
skip_goal H applies to any goal. It simply assumes
the current goal to be true. The assumption is named "H".
It is useful to set up proof by induction or coinduction.
Syntax skip_goal is also accepted.
Tactic Notation "skip_goal" ident(H) :=
match goal with |- ?G => skip H: G end.
Tactic Notation "skip_goal" :=
let IH := fresh "IH" in skip_goal IH.
skip_rewrite T can be applied when T is an equality.
It blindly assumes this equality to be true, and rewrite it in
the goal.
Tactic Notation "skip_rewrite" constr(T) :=
let M := fresh in skip_asserts M: T; rewrite M; clear M.
skip_rewrite T in H is similar as rewrite_skip, except that
it rewrites in hypothesis H.
Tactic Notation "skip_rewrite" constr(T) "in" hyp(H) :=
let M := fresh in skip_asserts M: T; rewrite M in H; clear M.
skip_rewrites_all T is similar as rewrite_skip, except that
it rewrites everywhere (goal and all hypotheses).
Tactic Notation "skip_rewrite_all" constr(T) :=
let M := fresh in skip_asserts M: T; rewrite_all M; clear M.
skip_induction E applies to any goal. It simply assumes
the current goal to be true (the assumption is named "IH" by
default), and call destruct E instead of induction E.
It is useful to try and set up a proof by induction
first, and fix the applications of the induction hypotheses
during a second pass on the proof.
Tactic Notation "skip_induction" constr(E) :=
let IH := fresh "IH" in skip_goal IH; destruct E.
Ltac duplicate H1 H2 :=
match type of H1 with
| ?t => assert t as H2 by assumption
end.
Ltac remove_if_same H1 H2 :=
match type of H1 with
| ?t => match type of H2 with
| t => clear H2
end
|_ => idtac
end.
Ltac inv h := inversion H ; subst.