| G1 |- t1 G2 |- t2 |
| G1ÈG2 |- t1 Ù t2 |
| 1 |
- show_assums := true; val it = () : unit - val Th1 = ASSUME ``A:bool`` and Th2 = ASSUME ``B:bool``; > val Th1 = [A] |- A : thm val Th2 = [B] |- B : thm - val Th3 = CONJ Th1 Th2; > val Th3 = [A, B] |- A /\ B : thm |
term list * term. An achievement of
such a goal is a theorem t1,...,tn |- t. A tactic
is an ML function that when applied to a goal generates subgoals
together with a justification function or validation,
which will be an ML derived inference rule, that can be used to
infer an achievement of the original goal from achievements of the
subgoals.thm list -> thm.T /\ T, where T is a constant that stands for
true:| 2 |
- val goal1 =([]:term list, ``T /\ T``);
> val goal1 = ([], ``T /\ T``) : term list * term
- CONJ_TAC goal1;
> val it =
([([], ``T``), ([], ``T``)], fn)
: (term list * term) list * (thm list -> thm)
- val (goal_list,just_fn) = it;
> val goal_list =
[([], ``T``), ([], ``T``)]
: (term list * term) list
val just_fn = fn : thm list -> thm
|
| 3 |
- TRUTH; > val it = [] |- T : thm |
| 4 |
- just_fn [TRUTH,TRUTH]; > val it = [] |- T /\ T : thm |
fun CONJ_TAC (asl,w) = let
val (l,r) = dest_conj w
in
([(asl,l), (asl,r)], fn [th1,th2] => CONJ th1 th2)
end
/\t2``) is a
goal, then CONJ_TAC splits it into the list of two subgoals
(asl,t1) and (asl,t2). The justification function,
fn [th1,th2] => CONJ th1 th2 takes a list
[th1,th2] of theorems and applies the rule CONJ to
th1 and th2.thm list -> thm. = ([g1,...,gn],p).
The idea is that g1 , ... , gn are subgoals and p is a
`justification' of the reduction of goal g to subgoals g1 ,
... , gn. Suppose further that the subgoals g1 , ...
, gn have been solved. This would mean that theorems th1 ,
... , thn had been proved such that each thi (1£ i£
n) `achieves' the goal gi. The justification p (produced by
applying T to g) is an ML function which when applied to the
list
[th1,...,thn]
returns a theorem, th, which `achieves' the original goal g. Thus
p is a function for converting a solution of the subgoals to a
solution of the original goal. If p does this successfully, then the
tactic T is called valid. Invalid tactics cannot result in
the proof of invalid theorems; the worst they can do is result in
insolvable goals or unintended theorems being proved. If T were
invalid and were used to reduce goal g to subgoals g1 , ...
, gn, then effort might be spent proving theorems th1 , ...
, thn to achieve the subgoals g1 , ... , gn, only to
find out after the work is done that this is a blind alley because
p[th1,...,thn]
doesn't achieve g (i.e. it fails, or else it achieves some other
goal). |- t
([u1,...,un],u)
([``x=y``, ``y=z``, ``z=w``], ``x=z``) is achieved by
the theorem [x=y, y=z] |- x=z (the assumption
``z=w`` is not needed). = ([],p).
If this is the case and if T is valid, then p[]
will evaluate to a theorem achieving g.
Thus if T solves g then the ML expression
snd(T g)[] evaluates to
a theorem achieving g.| goal |
| goal1 goal2 ··· goaln |
CONJ_TAC is described by| t1 /\ t2 | |
| t1 | t2 |
CONJ_TAC reduces a goal of the form
(G,``t1/\t2``)
to subgoals
(G,``t1``) and (G,``t2``).
The fact that the assumptions of the top-level goal
are propagated unchanged to the two subgoals is indicated by the absence
of assumptions in the notation.numLib.INDUCT_TAC, the tactic for
doing mathematical induction on the natural numbers:| !n.t[n] | |
| t[0] | {t[n]} t[SUC n] |
INDUCT_TAC reduces a goal
(G,``!n.t[n]``) to a basis subgoal
(G,``t[0]``)
and an induction step subgoal
(GÈ{``t[n]``},``t[SUC n]``).
The extra induction assumption ``t[n]``
is indicated in the tactic notation with set brackets.| 5 |
- numLib.INDUCT_TAC([], ``!m n. m+n = n+m``);
> val it =
([([], ``!n. 0 + n = n + 0``),
([``!n. m + n = n + m``], ``!n. SUC m + n = n + SUC m``)], fn)
: (term list * term) list * (thm list -> thm)
|
CONJ_TAC will fail if it is applied to a goal whose
conclusion is not a conjunction. Some tactics never fail, for example
ALL_TAC| t |
| t |
(G,t) to the
single subgoal
(G,t)---i.e. it
has no effect. ALL_TAC is useful for writing complex
tactics using tacticals. val (gl,p) = T g val th = p[]| 6 |
- val goal2 = ([]:term list, ``T /\ x ==> x \/ (y /\ F)``); > val goal2 = ([], ``T /\ x ==> x \/ y /\ F``) : (term list # term) - REWRITE_TAC [] goal2; > val it = ([], -) : (term list * term) list * (thm list -> thm) - #2 it []; > val it = [] |- T /\ x ==> x \/ y /\ F : thm |
(string * term * tactic) -> thm facilitates the use
of tactics:
store_thm("foo",t,T) proves
the goal ([],t) (i.e. the goal with no
assumptions and conclusion t) using tactic T and saves the resulting
theorem with name foo on the current theory.(term * tactic) -> thm can be used. Evaluating
prove(t,T) proves the goal
([],t) using T and returns the result without
saving it. In both cases the evaluation fails if T does not solve the
goal ([],t).| 7 |
- open arithmeticTheory numLib; > ... - ADD; > val it = |- (!n. 0 + n = n) /\ (!m n. (SUC m) + n = SUC(m + n)) : thm |
| 1 |
- g `!m. m+0=m`;
> val it =
Proof manager status: 1 proof.
1. Incomplete:
Initial goal:
!m. m + 0 = m
|
| 2 |
- e INDUCT_TAC;;
OK..
2 subgoals:
> val it =
SUC m + 0 = SUC m
------------------------------------
m + 0 = m
0 + 0 = 0
|
| 3 |
- e(REWRITE_TAC[ADD]);
OK..
Goal proved.
[] |- 0 + 0 = 0
Remaining subgoals:
> val it =
SUC m + 0 = SUC m
------------------------------------
m + 0 = m
|
(SUC m) + 0 = SUC m under the assumption
m + 0 = m. This goal can be solved by rewriting first with the
definition of addition:| 4 |
- e(REWRITE_TAC[ADD]);
OK..
1 subgoal:
> val it =
SUC (m + 0) = SUC m
------------------------------------
m + 0 = m
|
| 5 |
- e(ASM_REWRITE_TAC[]);
OK..
Goal proved.
[m + 0 = m] |- SUC (m + 0) = SUC m
Goal proved.
[m + 0 = m] |- SUC m + 0 = SUC m
> val it =
Initial goal proved.
[] |- !m. m + 0 = m
: GoalstackPure.goalstack
|
| 6 |
- top_thm(); val it = [] |- !m. m + 0 = m : thm |
| 7 |
- b();
> val it =
SUC (m + 0) = SUC m
------------------------------------
m + 0 = m
- b();
> val it =
SUC m + 0 = SUC m
------------------------------------
m + 0 = m
|
| 8 |
- e(ASM_REWRITE_TAC[ADD]);
OK..
Goal proved.
[m + 0 = m] |- SUC m + 0 = SUC m
> val it =
Initial goal proved.
[] |- !m. m + 0 = m
: GoalstackPure.goalstack
|
| 9 |
- b(); b();
> ...
> val it =
SUC m + 0 = SUC m
------------------------------------
m + 0 = m
0 + 0 = 0
- r 1;
> val it =
0 + 0 = 0
SUC m + 0 = SUC m
------------------------------------
m + 0 = m
|
| 10 |
- e(ASM_REWRITE_TAC[ADD]);
OK..
Goal proved.
[m + 0 = m] |- SUC m + 0 = SUC m
Remaining subgoals:
> val it =
0 + 0 = 0
|
THENL
[T1;...;Tn]
is a tactic which first applies T and then applies Ti to the
ith subgoal produced by T. The tactical THENL is
useful if one wants to do different things to different subgoals.| 1 |
- g `!m. m + 0 = m`;
> val it =
Proof manager status: 1 proof.
1. Incomplete:
Initial goal:
!m. m + 0 = m
- e(INDUCT_TAC THENL [REWRITE_TAC[ADD], ASM_REWRITE_TAC[ADD]]);
OK..
> val it =
Initial goal proved.
[] |- !m. m + 0 = m
|
INDUCT_TAC THENL [REWRITE_TAC[ADD];ASM_REWRITE_TAC[ADD]]
first applies INDUCT_TAC and then applies
REWRITE_TAC[ADD] to the first subgoal (the basis) and
ASM_REWRITE_TAC[ADD] to the second subgoal (the step).THENL is useful for doing different things
to different subgoals. The tactical THEN can be used to apply the
same tactic to all subgoals.THEN is an ML infix. If T1 and T2
are tactics, then the ML expression T1 THEN T2
evaluates to a tactic which first applies T1 and then applies T2
to all the subgoals produced by T1.| 1 |
- g `!m. m+0 = m`;
> val it =
Proof manager status: 1 proof.
1. Incomplete:
Initial goal:
!m. m + 0 = m
- e(INDUCT_TAC THEN ASM_REWRITE_TAC[ADD]);
OK..
> val it =
Initial goal proved.
[] |- !m. m + 0 = m
|
val ADD_0 = prove (
``!m. m + 0 = m``,
INDUCT_TAC THEN ASM_REWRITE_TAC[ADD]);
val ADD_SUC = prove (
``!m n. SUC(m + n) = m + SUC n``,
INDUCT_TAC THEN ASM_REWRITE_TAC[ADD]);
val ADD_CLAUSES = prove (
``(0 + m = m) /\
(m + 0 = m) /\
(SUC m + n = SUC(m + n)) /\
(m + SUC n = SUC(m + n))``,
REWRITE_TAC[ADD, ADD_0, ADD_SUC]);
val ADD_COMM = prove (
``!m n. m + n = n + m``,
INDUCT_TAC THEN ASM_REWRITE_TAC[ADD_0, ADD, ADD_SUC]);
ORELSE is an ML infix. If T1 and
T2 are tactics,
then T1 ORELSE T2 evaluates to a tactic which
applies T1 unless that fails; if it fails, it applies T2.
ORELSE is defined in ML as a curried infix by3(T1 ORELSE T2) g
= T1 g handle _ => T2 g
REPEAT T is a tactic which
repeatedly applies T until it fails. This can be illustrated in
conjunction with GEN_TAC, which is specified by:!x.t[x] |
| t[x'] |
| 2 |
- g `!x y z. x+(y+z) = (x+y)+z`;
> val it =
Proof manager status: 1 proof.
1. Incomplete:
Initial goal:
!x y z. x + (y + z) = x + y + z
- e GEN_TAC;
OK..
1 subgoal:
> val it =
!y z. x + (y + z) = x + y + z
- e(REPEAT GEN_TAC);
OK..
1 subgoal:
> val it =
x + (y + z) = x + y + z
|
thm_tactic to
abbreviate thm->tactic, and the type
conv4 to
abbreviate term->thm.REWRITE_TAC[th1,...,thn]
simplifies the goal by rewriting
it with the explicitly given theorems th1, ... , thn,
and various built-in rewriting rules.| {t1, ... , tm}t |
| {t1, ... , tm}t' |
basic_rewrites.
ASM_REWRITE_TAC adds the assumptions of the goal
to the list of theorems used for rewriting.
PURE_REWRITE_TAC uses neither the assumptions nor
the built-in rewrites.
bossLib.RW_TAC of type simpLib.simpset -> thm
list -> tactic. A simpset is a special collection of
rewriting theorems and other theorem-proving functionality. Values
defined by HOL include bossLib.base_ss, which has basic
knowledge of the boolean connectives, bossLib.arith_ss which
``knows'' all about arithmetic, and HOLSimps.hol_ss, which
includes theorems appropriate for lists, pairs, and arithmetic.
Additional theorems for rewriting can be added using the second
argument of RW_TAC.
``t1/\t2`` into two subgoals ``t1``
and ``t2``.| t1 /\ t2 | |
| t1 | t2 |
CONJ_TAC is invoked by STRIP_TAC (see below).EQ_TAC
splits an equational goal into two implications (the `if-case' and
the `only-if' case):| u = v | |
| u ==> v | v ==> u |
u ==> v |
| {u}v |
``u ==> v`` by assuming ``u`` and then solving
``v``.
STRIP_TAC (see below) will invoke DISCH_TAC on implicative goals.
!x.t[x] |
| t[x'] |
REPEAT GEN_TAC strips off all
universal quantifiers and is often the first thing one does in a proof.
STRIP_TAC (see below) applies GEN_TAC to universally quantified goals.
PROVE_TAC does a search for possible proofs of the
goal. Eventually fails if the search fails to find a proof shorter
than a reasonable depth.
STRIP_TAC removes one outer connective from the goal, using
CONJ_TAC, DISCH_TAC, GEN_TAC, etc.
If the goal is
t1/\···/\tn ==> t
then DISCH_TAC makes each ti into a separate assumption.REPEAT STRIP_TAC.
SUBST_TAC[|-u1=v1,...,|-un=vn]
converts a goal t[u1,... ,un] to the subgoal form
t[v1,... ,vn].REWRITE_TAC is too general or would loop.
ACCEPT_TAC th
is a tactic that solves any goal that is
achieved by th.THEN
(see DESCRIPTION).REPEAT
in DESCRIPTION).
THENL; for example, if tactic T produces two subgoals
and we want to apply T1
to the first one but to do nothing to the second, then
the tactic to use is T THENL[T1;ALL_TAC].