Science of Computer North-Holland
Programming
EQUATIONAL Tobias
123
12 (1989) 123-149
REASONING
IN ISABELLE
NIPKOW*
Laboratory for Cornpurer Science, Massachuserls Institute of Technology, 545 Technology Square, Cambridge, MA 02139, U.S.A.
Communicated by C.B. Jones Received August 1988 Revised January 1989
1. Introduction
This paper reports on a case study carried out with the generic theorem prover Isabelle. The purpose of this study was to investigate the process of instantiating a generic system like Isabelle for a particular logic (equational logic in this case). Isabelle is like a compiler-compiler, except that it compiles a logic definition into an LCF-style theorem prover for that logic. To make that theorem prover usable, a number of logic-specific “tactics”, embodying useful theorem proving techniques in that logic, must be built on top. Equational logic was chosen as a test case because it comes with a large body of well-tried algorithms. It will be shown how a variety of different algorithms in the area of equational reasoning can be implemented in a unified way using a powerful tactic language. The problems tackled are term rewriting, unification and inductive theorem proving and the tactic language is ML. The main advantages of this approach can be summarized as follows: Correctness. Our approach any changes to the inference algorithms
are presented
of incorporating rewriting techniques does not require engine of the theorem prover. Particular term rewriting
as tactics which use the basic equational
rules, very much in the style of [20]. The obvious soundness of all deductions (provided the underlying
calculus
inference
advantage is the guaranteed theorem prover is sound). In
addition an algorithm in terms of inference rules is sometimes easier to understand than one written to operate directly on the underlying tree representation of formulae. Particular examples are the unification and matching algorithms presented in Section 4.4.1. Extensibility. The resulting system is not closed but can be extended at two levels: by new tactics and by new inference rules. The former is a consequence of the * Part of this research was carried out at the University of Manchester with support from Alvey grant GR/D/60294. At MIT the author was supported in part by NYNEX, NSF grant CCR-8706652, and by the Advanced Research Projects Agency of the DOD, monitored by the ONR under contract N00014-83-K0125.
0167-6423/89/$3.50
@ 1989, Elsevier
Science
Publishers
B.V. (North-Holland)
7: Nipkow
124
LCF-philosophy whereas the latter is due to the generic nature of Isabelle. openness has permitted us to incorporate inductive theorem proving techniques our collection similar
of tactics,
development
Simplicity. symbolic
Isabelle’s
theorem
something
is reported primitives
proving.
which
goes beyond
pure equational
This into
logic. A
in [4]. provide
In particular
most
of the functionality
it offers both functional
required
for
and logic program-
ming, albeit at different levels. Functional programming is available through ML, and logic programming is embodied in Isabelle’s Horn clause formalization of inference rules, including logical variables. As a consequence most of the algorithms could be implemented
in a short time and required
little code.
The contribution of this paper does not lie in the originality of the algorithms but in their presentation in the framework of general theorem proving. This is in contrast to most other systems performing equational deduction, for example [5, 161. They are built to execute particular algorithms in an efficient way. If one is interested in proving theorems that go beyond the equational calculus, these systems offer little or no support-that is not what they were designed for. On the other hand it seems unlikely that a general purpose theorem prover will perform as well as these specialized systems when applied to equational problems. Isabelle certainly does not, which is hardly surprising given the overhead incurred by generality. The structure of the paper is as follows. Section 2 introduces the generic theorem prover Isabelle. In particular the representation of logics, the basic tactics, and the combinators for combining them into complex proof procedures are described. Section 3 shows how equational logic is represented in Isabelle. Section 4 is concerned with the general problem of finding normal forms of terms w.r.t. some rewrite relation. In particular we consider rewriting with unconditional and conditional equations, rewriting with assumptions, rewriting modulo equations, and rewriting via higher-order matching. Section 5 presents a simple tactic for inductive proofs of equations based on the rewriting procedures of Section 4, and closes with a series of sample proofs about natural numbers. Isabelle is written in Standard ML (ML in the sequel), as are all of the tactics presented in this paper. We assume that the reader is familiar with the ML syntax for function definitions as for example in [8]. Throughout the paper the sanserif font indicates either ML code or Isabelle input/output. In the latter case some changes have been made to improve the presentation. Lines starting with > are type information inferred by the ML system.
2. IsabeIIe Isabelle is an interactive theorem prover developed and implemented in ML by Larry Paulson at the University of Cambridge. This section gives only a very sketchy account of Isabelle, just enough to make the paper self-contained. A more detailed introduction to Isabelle can be found in [7]. A first explanation of the principles
Equational
underlying using
Isabelle
is contained
higher-order
reasoning
in [21],
irl Isabeile
a formalization
125
of Isabelle’s
metalogic
logic is given in [23].
2.1. Representing
logics
What distinguishes Isabelle from most, if not all, other theorem provers is the fact that it can be parameterized by the object-logic to be used. The definition of a logic consists of the declaration/definition of all - basic types (for example terms, formulae, etc.); - logical constants (operators like =, 3 and V) and variables (x, y, z, P, Q, etc.) with their arity; valid arities are the basic types and function types over them; - inference rules. Section 3 shows how equational logic is represented in Isabelle. The central notion in Isabelle is that of a rule, written as p , . . . p, P
’
where the Pi and P are well-typed A-calculus terms over the logical constants and variables. The Pi are called the premises and P the conclusion. If m = 0, the rule is called a theorem. Theorem proving rules. The principal
in Isabelle amounts to combining the basic rules to form derived method for combining two rules is resolution: given two rules
* = p, . . . pt?l P
and a substitution
and
u which
q=
9, . ..Qn Q ’
unifies
P with
Q, for some i, resolving
p and q yields
the new rule
Q,...
(+
(
Qi-1 f’, . . . Pm Qi+l . . . Qn >. Q
This brings us to another important than just matching (as for example
(1)
Isabelle feature: it is based on unification rather LCF). Since Isabelle formulae are h-expressions,
Isabelle contains an implementation of higher-order unification which is described in [21]. This means that unification may yield a potentially infinite stream of unifiers; it may even be undecidable. How this is dealt with is described in Section 2.2. However, most of the time all our terms are first-order, i.e. they do not contain function variables or lambdas. Hence there exists at most one most general unifier. Every variable introduced with the definition of a logic can be used in two ways: as an ordinary and as a logical variable. The difference is that logical variables can be instantiated during the resolution process whereas ordinary variables act like constants. The latter should be thought of as universally quantified at the metalogical level (see [23]). Logical variables are distinguished from ordinary ones by being .-_-c.._> . . ...I_ - ‘L-i?? preuxeu WI111 a ! .
T. Nipkow
126
All this sounds
very much
seen as an implementation Finally
I should
mention
like logic programming,
of typed higher-order that the version
and in fact Isabelle
logic programming
of Isabelle
described
can be
(see also [21]). in this paper
is
Isabelle-86. Meanwhile Isabelle has been extended significantly and both the foundations described in [23] and the user manual [24] pertain to the latest version. 2.2. Basic tactics and tacticals Tactics
are a concept
originating
with
LCF
[6, 221. They
are the functional
programmer’s answer to the challenge posed by the length and repetitiveness of proofs from first principles. An Isabelle tactic is a function from a rule to a (potentially infinite) sequence of rules. type tactic = rule + rule sequence; sequence is an abstract type implementing lazy lists. Tactics need to produce sequences of rules to allow for backtracking and also because the basic inference rule, resolution, may produce an infinite number of results due to higher-order unification. In Isabelle the state of a proof is just a rule, where the premises should be thought of as the goals to be solved, and the conclusion the formula to be proved. A proof of the formula P starts with the trivially correct rule f and proceeds backwards by resolution with the premises. Hence the most basic tactic is
> val resolve_tac Applying resolve_tac rl with the ith premise
=fn : rule list + int + tactic
rl i to some rule r yields the stream of resolvents of rules in of r. Resolution includes “standardizing apart”, i.e. renaming
the variables in rl so they are disjoint from the ones in r. The two infix operators RESN and RES resolve two rules directly, derived
yielding
a new
rule: > val RESN =fn : rule * (int * rule) -+ rule > val RES =fn : rule * rule-, rule
Given
two rules p and q, q RESN (i, p) yields the result of resolving
p with the ith
premise of q as in (I), provided it is unique. p RES q is equivalent to p RESN (1, q). Although a!! derived rules are ultimately proved via single resolution steps, Isabelle provides tactics and tacticals (functions for combining tactics) to build up complex proof strategies: > vat all_tac=fn > val no_tac=fn
:tactic
:tactic > val THEN =fn : tactic * tactic + tactic > val ORELSE=fn :tactic * tactictactic > val COND =fn : (rule + bool) + tactic * tactic + tactic
>val REPEAT=fn:tactic+tactic > val UNTIL=fn:(rule+ bool)+
tactic-,
> val UNTlL_THM =fn :tactic+ tactic >val SELECT_lst_GOAL=fn:tactic+tactic
tactic
Equational
In the following identifying
explanation
reasoning
of these tactics
the type sequence
127
in Isabelle
and tacticals
we simplify
with the type list. Hence we abstract
slightly
from problems
arising from representing infinite lists. The basic ML operations on lists are [ 1, the empty list, :: , the infix operator putting provides
an element
in front
the following >val
of a list and @, the infix append
additional
>val
hd=fn:‘a list+‘a map=fn:(‘a+‘b)+‘a
>val
flat=fn
by
operator.
for
Isabelle
functions:
list+‘b
list
:‘a list list+ list
hd yields the first element of a nonempty list, map applies a function to every element of a list, and flat distributes @ over a list of lists. If ML lists were lazy, the following pseudodefinitions would actually work. The tactic all_tac passes all rules through unchanged: fun all_tac
The tactic no_tac
always
fun no-tat
The tactical
rule= [rule];
fails, i.e. returns
rule=[
1;
THEN performs
fun (tacl
an empty list:
one tactic followed
THEN tac2)
rule=flat
by another:
(map tac2 (tacl
rule));
The tactical ORELSE uses the first tactic that returns a nonempty LCF, ORELSE commits to either tacl or tac2 immediately: fun (tacl
The conditional
ORELSE
tactical
Like in
tac2) rule=
rule =[ ] then tad
if tacl
sequence.
selects
rule else tacl
either tacl
rule;
or tac2, depending
on testfun:
fun COND testfun (tacl, tac2) rule= if testfun rule then tacl rule else tac2 rule; REPEAT applies
tat
repeatedly
fun REPEAT tat UNTIL applies
tat
fun UNTlL COND As a particular
rule=((tac
THEN
until satfun reports satfun satfun
instance
REPEAT tat)
no backtracking: ORELSE
all_tac)
rule;
success:
tat rule= (all-tat,
tat THEN
UNTIL
satfun tat)
rule;
we have
val UNTIL_THM=UNTIL which repeats
until it fails. It performs
is-theorem;
a tactic until a theorem
(is-theorem),
i.e. a rule without
premises,
is
obtained. makes the tactic to it.
SELECT_lst_GOAL
and applies
a rule look like it has only one premise,
the first one,
T. Nipkow
128
In addition
to the tactics and tacticals
of other functions,
mostly
described
here, Isabelle
provides
to do with rule and term manipulation,
we discuss. Any function which is only mentioned be assumed to be one of those.or to be a simple
in passing combination
a collection
none
of which
but is not defined of those.
can
3. Representing equational logic Equational logic, a fragment of first-order predicate calculus, is based on the two basic types term and form of terms and formulae. The only logical constant is = of type term + term + form. In addition we can have a selection of function symbols (or extra-logical constants) of type term + * . . + term for building up terms. The letters
u to z stand
for variables
of type term.
y2, etc.) to create new variables. The inference rules of the equational refl=-
calculus
Isabelle consist
allows
“subscripting”
of the three equality
(xl, rules
?x=?x ?x=?y
wm=, .y trans =
.x
?x=?y
?x=?z
and a congruence
rule
?xl =?yl f(?xl,
?y=?z
. . . ?xn=?yn
. . , ?xn)=f(?yl,.
for every n-ary, n 3 0, function
. . , ?yn) symbol
f. In the sequel tongs
such congruence rules for a given set of function symbols. For later use in equational proofs we define the following
denotes
the list of all
tactics:
val trams_tat = resolve_tac [trans] 1 ; val refl_tac=resolve_tac [refl] 1; val sym-tat = resolve_tac [sym] 1; val cong_tac = resolve_tac tongs 1 ; Notice that they all resolve with the first premise
which is usually
the current
focus.
4. Normalization The most basic
mechanism
rewriting, or more precisely,
some given rewrite relation.
in any implementation
of equatorial
logic is term
the process of finding normal forms of terms w.r.t. to For a concise introduction to equational logic and term
Equa/ional
rewriting
see for example
of terms in some theory Although theorem
this problem
proving
reasoning
Isabel/e
129
[lo]. Suffice it to say that under certain can be decided
by comparing
the problem can be expressed normalized and ?x the variable
conditions
their normal
looks like one of transforming
system we have to rephrase
often concerned
in
equality
forms.
terms, in the context
it in terms of inference
of a
rules. In Isabelle
as the equation s = ?x, where s is the term to be we want to bind the normal form to. Hence we are
with rules of the form
s=?x
H (2)
P
where s is a ?-free term, H any further list of formulae and P a formula. Remember that theorem proving in Isabelle is based on backwards reasoning, i.e. the current proof state takes the form of a rule whose premises are the subgoals. Given such a rule, a normalization tactic shall solve the first subgoal, i.e. instantiate ?x to the normal form of s, and return the rule $. When writing tactics for subterm replacement one has to distinguish the two aspects of tree traversal and rule application which are independent of each other. A normalization tactic is a particular tree traversal scheme parameterized by a tactic for applying rewrite rules. The advantage of this approach is one of greater modularity: it is possible to define a collection of tactics, from rewriting of terms to proving equations, without commitment to the kind of rewrite rules they apply to. This is the topic of Section 4.1. The subsequent sections are devoted to instantiating those tactics to perform unconditional and conditional rewriting, rewriting with assumptions, and rewriting module equations. In a final section we study a radically different approach to rewriting based on the higher-order capabilities of Isabelle. 4.1. Basic rewriting As a simple
tools
example
of a reduction
strategy
we present
a bottom
up scheme
which normalizes all subterms of a term before it attempts to apply a rewrite rule at the root. Here is a tactical which applies its argument tactic rew_tac to all subterms of a term: fun ALL_SUBTS
rew_tac=SELECT_lst_GOAL ((tong_tat THEN UNTIL-THM rew_tac) > val ALL_SUBTS =fn : tactics + tactic
ORELSE
refl_tat);
Applied to a rule of the form (2), the goal s = ?x is selected. If s is of the form f(s1,. . , sn), tong-tat instantiates ?x with f(?xl, , ?xn) and yields the subgoals sl =?xl, . , sn=?xn. To these subgoals rew_tac is applied until they have all been solved. If tong-tat fails, s is a constant (or an ordinary variable), i.e. there are no subterms, and the goal can simply be solved by reflexivity. SELECT-1 St-GOAL limits attention to the subgoals created by tong_tat. Otherwise it would not be clear when they have all been solved. It is used to similar effect in the tactics to come.
7: Nipkow
130
Here is the tactical
for bottom-up
fun BU rew-tat
normalization
of terms.
rule=
(SELECT_lst_GOAL (UNTIL_THM (trans_tat
THEN ALL_SUBTS(BU
((trans_tat
THEN rew-tat)
> val BU =fn :tactic+
rew-tat))
ORELSE
THEN
refl_tat))))
rule;
rule-, rule sequence
Note that due to the call-by-value semantics of ML, we need to mention the parameter rule-otherwise the recursive call BU rew_tac would lead to infinite unfolding. As a consequence, the type-checker infers the above expanded type instead of the equivalent but simpler tactic+ tactic. As before, laziness would solve this inconvenience. We assume be applied to to some t such by taking all
that the parameter rew_tac represents a rewrite relation + and can any rule of the form (2), yielding a rule F, where ?x has been bound that s + t holds. Roughly speaking + represents the relation obtained instances of all rules in a term rewriting system and BU yields the
closure of + w.r.t. all congruences in tongs. Let us quickly do a symbolic trace of BU: given a rule of the form (2), the first premise is selected and the main body of BU is applied to it until no subgoal remains; trans_tat yields s=?y, ?y=?x; ALL_SUBTS(BU rew_tac) binds ?y to the result of normalizing all subterms of s, say t, yielding t = ?x; another application of trams_tat yields t = ?z, ?z = ?x; if rew_tac succeeds, ?z is bound to some r with t + r, leaving us in the same situation as initially, but with r instead of S. If rew_tac fails, it follows that t is in normal form: its subterms are (due to ALL_SUBTS(BU rew_tat)), and rewriting at the root has failed too. Hence trans._tat THEN rew_tac as a whole fails, we are back at t = ?x, which is finally solved by reflexivity, instantiating ?x to the normal form t, as required. In order to use BU in conjunction with a list of equations (rewrite rules) rwrls, we simply need to instantiate its parameter by a tactic for resolving with those very rules: fun REW_TAC >val
rwrls=resolve_tac
REW_TAC=fn:rule
rwrls 1;
list+ tactic
fun NORM_LHS-TAC rwrls=BU (REW-TAC > val NORM_LHS_TAC=fn : rule list + tactic
rwrls);
The understanding is that rwrls is a list of equations of the form I = r. Unifying with an equation s = ?x, where s is ?-free, clearly has the effect of instantiating with the result of a single I = r rewrite of s at the root.
Example
4.1. Let N_rules=[O+?y=?y,
s(?x)+?Y=s(?x+?Y)]
that ?x
Equarional
be a simple
equational
definition
We look at the application
reasoning in Isabel/e
of addition
131
(+) in terms of 0 and successor
of NORM_LHS_TAC
N-rules
(s).
to a rule with the first
premise
trans_tat: ALL_SUBTS(BU
rew-tat):
trans-tat rew_tac:
s(0 +O) =?x s(O+O)=?y3
trans_tat: ALL_SUBTS(BU
?y=?x ?yl =?x ?y3=?x
rew_tac:
s(O)=?x ?y4 = ?x s(0) =?y4 fails, back to s(O)=?x
refl_tat:
solves the subgoal,
rew-tat):
trans_tat:
Instead outermost.
s(0) +o=?x s(0) +0=?y s(0) +0=?x s(0) +O=?yl
binding
?x to s(0).
of bottom-up, we can also implement other strategies like leftmostIn order not to overwhelm the reader with ML code this one example
will have to suffice. In the sequel we are not just interested in simplifying individual expressions both sides of an equation. This can be accomplished by splitting the equation according to the following rule: ?s=?x split-eqn
?t=?y
=
?x=?y
?s=?t
which can be derived
from the basic equational
laws as follows:
?x=?y ?y=?x
?t=?y ?s=?x
but first
?t=?x ?x=?t ?s=?t
In Isabelle
this linearizes
to
val split_eqn=trans RESN (2, sym RES (trans RESN > val split_eqn =? : rule
(2, sym)));
Since rule is an abstract ML type, its representation is hidden, which the system indicates by printing a ?. We leave it to the reader to verify that this composition does indeed yield the desired rule. Simplifying both sides of an equation is just a matter of splitting the equation via split_eqn and simplifying both sides separately: fun NORM-EON norm_lhs_tac= resolve_tac [split_eqn] 1 THEN norm_lhs_tac > val NORM_EQN=fn:tactic+ tactic
THEN
norm-lhs-tat;
T. Nipkow
132
Proving an equation reflexivity:
is achieved
fun PROVE-EQN NORM-EQN
by normalizing
it followed
by an application
of
norm_lhs_tac= norm_lhs_tac
THEN
refl_tat;
= fn : tactic + tactic
> val PROVE_EQN
Of course this yields only a decision procedure if norm-lhs-tat represents a confluent and terminating set of rewrite rules. However, even if that is the case, we can still only derive equalities in Isabelle. The fact that an equation is nor provable is a statement
in the meta-logic and cannot be derived in the object-logic. The above definition of PROVE-EON, although correct, can be very inefficient due to the subtleties of backtracking: if the equation we try to prove is not valid, the final application of refl_tac will fail. That, however, leads to backtracking in the tactic for normalizing both sides of the equation. This means that, because of backtracking, the normalization tactic enumerates all normal forms of a term (w.r.t. the given rewriting strategy). If the congruence closure of + is confluent, all normal forms are identical, i.e. the enumeration is wasteful. This can be stopped with the DETERM tactical available in Isabelle which chops off further results, thus making the result of a tactic application deterministic: fun DETERM
tat
rule=if
tat rule=[]
> val DETERM =fn :tactic+
Thus
a
more
efficient
version
of
then [] else hd(tac rule);
tactic PROVE-EQN
uses
DETERM(NORM_EQN
norm_lhs_tac). Now that we have introduced the basic mechanisms for rewriting, let us apply them to more advanced targets like conditional equations or rewriting with assump-
tions. 4.2. Conditional
rewrite rules
So far we have only given
one instantiation of our rewriting mechanism using simple equations (REW_TAC and NORM_LHS-TAC). However, for many theorem proving applications this is too restrictive. Hence we present an extension to conditional equations. There are different possibilities predicate calculus, for example sl=tl
of representing conditional as theorems of the form
equations
in first-order
& . . . & sn=tn*s=t
where & is conjunction and + inference rules of the form sl=tl . ..sn=tn s=t
implication.
(3)
We have chosen
to represent
them as
(4)
Both representations are logically equivalent but the second one is easier to handle: applying (4) to some goal equation is achieved by resolving with it. For (3) simple
Equational
resolution
does not work because
It is easy to provide
a function
reasoning
the logical
in Isabel/e
133
connectives
for the translation
+
and & get in the way,
from (3) to (4): repeated
resolution
with the rules imp_elim
=
?P
?P3?0
and
?Q
conj_intr=
?P
?Q
?P & ?Q
does the job. Using the basic rewriting tactics in conjunction with conditional equations of the form (4) is quite simple. All that is required is a tactic for applying conditional equations. A conditional equation is applicable if all its premises can be proved: fun CREW_TAC
prove_eqn-tat
crwrls=SELECT_lst_GOAL
(resolve_tac crwrls 1 THEN UNTIL-THM prove_eqn_tac); > val CREW_TAC =fn : tactic + rule list + tactic
This tactic has two parameters: crwrls is a list of conditional rewrite rules and prove_eqn_tac a tactic for proving the premises of rules in crwrls. In its simplest form, prove_eqn_tac does again involve conditional rewriting. This cycle is expressed in the following recursive tactic for normalizing the left-hand side of an equation. Instead of BU any other reduction strategy could be used. fun CNORM-LHS_TAC
crwrls=
let fun crew-tat rule = CREW-TAC prove_eqn_tac and cnorm_lhs_tac rule = BU crew_tac rule and prove-eqn-tat
rule = PROVE-EON
in cnorm_lhs_tac end; > val CNORM_LHS_TAC=fn
be the following
R,=
RI=
and apply
CNORM_LHS-TAC
rule
list of conditional
rewrite
rules
s(?x)
?x
cnorm_lhs_tac
: rule list+ tactic
Example 4.2. Let L-rules
?x
crwrls rule
?x
to the inference
rule
=?x s(0) =?x
s(s(s(0))) -c s(0) s(s(s(0))) <
Because all subterms of the left-hand side are in normal form, rewriting will eventually reach the root. At that point resolve_tat L-rules 1 (in CREW_TAC) results in the application of rule Rj,since R, and R2 do not match s(s(s(0)))
T. Nipkow
128
In addition
to the tactics and tacticals
of other functions,
mostly
described
here, Isabelle
provides
to do with rule and term manipulation,
we discuss. Any function which is only mentioned be assumed to be one of those.or to be a simple
in passing combination
a collection
none
of which
but is not defined of those.
can
3. Representing equational logic Equational logic, a fragment of first-order predicate calculus, is based on the two basic types term and form of terms and formulae. The only logical constant is = of type term + term + form. In addition we can have a selection of function symbols (or extra-logical constants) of type term + * . . + term for building up terms. The letters
u to z stand
for variables
of type term.
y2, etc.) to create new variables. The inference rules of the equational refl=-
calculus
Isabelle consist
allows
“subscripting”
of the three equality
(xl, rules
?x=?x ?x=?y
wm=, .y trans =
.x
?x=?y
?x=?z
and a congruence
rule
?xl =?yl f(?xl,
?y=?z
. . . ?xn=?yn
. . , ?xn)=f(?yl,.
for every n-ary, n 3 0, function
. . , ?yn) symbol
f. In the sequel tongs
such congruence rules for a given set of function symbols. For later use in equational proofs we define the following
denotes
the list of all
tactics:
val trams_tat = resolve_tac [trans] 1 ; val refl_tac=resolve_tac [refl] 1; val sym-tat = resolve_tac [sym] 1; val cong_tac = resolve_tac tongs 1 ; Notice that they all resolve with the first premise
which is usually
the current
focus.
4. Normalization The most basic
mechanism
rewriting, or more precisely,
some given rewrite relation.
in any implementation
of equatorial
logic is term
the process of finding normal forms of terms w.r.t. to For a concise introduction to equational logic and term
Equational reasoning in
In order to avoid this trap, universally:
the sequent
ALL permits
us to obtain
free variables
ALL X.X+0=x arbitrarily
135
Isabel/e
in assumptions
need to be quantified
I- 1 +(0 +0) =O is provable.
many instances
is possible, the quantifiers have to be removed. tions are removed by resolution with
of a formula.
Universal
The presence
of
Before rewriting
quantifiers
in the assump-
?A, ?P’(?x), ?B F ?P a”-‘ntr-asm a simple derived tification should self-explanatory:
=?A, ALL x.?P’(x), ?B !- ?P
rule in predicate calculus. The concrete syntax of universal quanbe sufficiently close to ordinary mathematics to make this rule each application of all-intr_asm peels off one quantifier, replacing
the bound variable by a ?-variable. The formalization treated in more detail in Section 4.6. Now rewriting with assumptions becomes
of quantifiers
in Isabelle
val strip_asm_tac=REPEAT(resolve_tac [all_intr_asm] 1); > val strip_asm_tac=fn :tactic vat asm_rew_tac=strip_asm_tac THEN resolve_tat [assume] > val asm_rew_tac =fn : tactic
is
1;
If (after strip_asm_tac) the first goal is of the form A, I = r, B I- s = ?x and s is an instance of I, resolving with assume binds ?X to the corresponding instance of r. Rewriting with assumptions alone is not interesting. Instead we look at the combination of assumptions and conditional equations as in Section 4.2. Due to the modular construction of our tactics we just need to change the definition of crew_tac in CNORM-LHS-TAC to
fun crew_tac rule= (CREW_TAC prove-eqn-tat
crwrls
ORELSE asm_rew_tac)
This tactic first tries to rewrite with the given list of (possibly
conditional)
rule
equations
crwrls, and if that fails, tries to use an assumption.
There is a subtle point here concerning the removal of universal quantifiers in each rewrite step: there should exist another “copy” of the quantified assumptions which can be used in subsequent reduction steps. A careful analysis of the above rewriting tactic shows that this second copy is the result of the application of trans_tat before rew_tac in BU: the transitivity rule (5) generates two subgoals, duplicating the assumptions. Although the above definition of asm-rew_tac only works for quantified unconditional equations, it is not difficult to extend it to implicative assumptions. Since that only involves a new predicate calculus rule but no new ideas, it is not discussed here. 4.4. Rewriting
module equations
It is often advantageous to integrate them directly
not to formulate certain properties as rewrite rules but into the rewriting machinery. This does not only enhance
T. Nipkow
136
the expressive
power of term rewriting
to termination
problems,
rewrite
certain
rules. To overcome
this problem,
split into a set of equations
but may actually
between
rewriting
equations
%-equivalence is rather
be mandatory:
like commutativity, presentations
8 and a set of rewrite
is now a relation modulo
systems
properties,
classes
subtle
cannot
of equational
theories
rules 9. Conceptually of terms.
The general
and is discussed
due
be used as are
rewriting theory
for example
of
in [12,
141. Let us just say that under certain conditions rewriting modulo equations reduces to normal rewriting, except that matching has to be performed modulo 8. Hence two things are required: a general tactic for applying rewrite rules which is parameterized by the matching tactic to be used, and a matching tactic for each equational theory of interest. Below the problems are tackled in exactly this order. The following tactical EQ-REW_TAC takes a set of rules and a tactic for matching two terms and returns a tactic for rewriting module matching. fun EQ_REW_TAC
match-tat rwrIs= trans_tat THEN resolve_tat rwrls 2 THEN match_tat; > val EQ_REW-TAC =fn : tactic + rule list + tactic
match_tat
should s=t
be a tactic taking
a rule of the form
H ’
P
where s is ?-free, and enumerating (via backtracking) all possible matches oft with s. This means it returns the rule $, having instantiated the variables in t yielding some t’ such that s=t’ holds. Here is a symbolic trace of an application of EQ_REW_TAC to a rule with the premises trans-tat: resolve_tac
rwrls 2:
match_tat:
s=?x s=?y
H ?y=?x
s=l
H
H [?x==r] [?x=r,
H
a]
The rightmost column shows the resulting variable bindings: I = r is some equation in rwrls and u a substitution generated by match_tat such that s and a(l) are equal. After having defined the generic tactic for rule application, let us now look at some popular
equational
theories
and their matching
problems.
UniJication and matching In this section unification and matching algorithms for commutativity, associativity, and associativity+ commutativity are described. I do not claim that the algorithms themselves are original but, apart from commutativity, I am not aware of a similar presentation in terms of proof rules. The basic idea is due to Martelli and Montanari: systems of equations are solved by successive transformation [18]. This idea has been elevated to a principle in Claude Kirchner’s work on unification (e.g. [13]). In Isabelle it is realized as transformations between rules of the form 4.4.1.
ul =wl
. . un=wn P
(6)
E9uotional reasoning in Isabel/e
where the equations should
simplify
equations
above the line are the ones to be solved.
the equations
have disappeared,
on the way constitute two kinds
137
in some
The process
i.e. have been solved.
a solution.
of simplifications,
sense.
In contrast
decomposition
Each transformation terminates
The variable
to Claude
Kirchner,
and mutation,
bindings
when
all
created
who distinguishes
we are less specific and
simply assume that the transformations are expressed as conditional equations of the form (4). In Prolog terms the equations above the line in (6) are the query and the Horn clause transformation rules the program to answer it. To simplify the termination problem, equations of the form ?x = t (or vice versa) are treated separately: they do not need further simplification and can be solved directly by reflexivity, i.e. by binding ?x to t. Notice that reflexivity fails if ?x occurs in t (occur-check!). Hence this principle is only adequate for so-called simple theories, i.e. theories where the above equation does not have a solution. The theories discussed in this section happen to meet this restriction. A second observation is that all congruence rules (tongs) are valid decomposition rules and do not jeopardize
termination.
Thus the following
general tactic is obtained:
fun unify decomp_rules=SELECT_lst_GOAL (UNTIL_THM (COND var (refl-tat, resolve_tat ORELSE refl_tat))); > val unify=fn : rule list + tactic
(tongs
@ decomp_rules)
1
where val var=fn : rule + boQl is a function which tests whether the first premise of a rule is an equation with a ?-variable on either side. The application of the decomposition rules is guarded by ORELSE refl-tat to take care of equations c = c for constants c which cannot be decomposed further. The first premise is assumed to be the original unification problem. Decomposition and reflexivity are applied to it until all equations A trivial instantiation need as decomposition
have been solved. of unify yields the Robinson unification laws are the congruence rules tongs:
algorithm.
All we
val robinson = unify [ 1;
> val robinson =fn : tactic Of course Isabelle provides higher-order and thus in particular Robinson unification anyway. The simplest nontrivial example is commutative unification. Commutativity gives rise to two decomposition rules: the original congruence rule and congruence modified by commutativity. Lemma 4.3. {f(x, y) =f(y, If g is in Fc
Let Fc be the set of commutative function symbols, let C = x)/f6 F,}, and let s = g(s,, . . . , s,) and t = g(tl, . . . , t,) be fwo terms.
27 Nipkow
138
Otherwise s =c t
e Vi. si =c t,.
All we need to do is to generate
the second
rule
?x2=?yl
?xl =?y2 comm-cong=f(?xl,
decomposition
?x2)=f(?yl,
?y2)
from the rules ?xl =?yl
?x2 = ?y2
and
Cong=f(?xl,?x2)=f(?yl,?y2)
comm=
f(?xl, ?x2)=f(?x2,
?xl)
It is not difficult to see that trans RES tong RES comm yields comm-tong. the following function computes the required decomposition rule fun c_decomp(cong,
comm)=trans
RES comm
Thus
RES tong;
=fn : rule * rule + rule
> val c-decomp
and unify [c_decomp(cong, camp)] yields a commutative unification algorithm for the function symbol f. The same algorithm can be found many times in the literature, also in [ 131, where it is presented in terms of the above two decomposition rules too. A more traditional account can be found in [9], where this algorithm is also shown to terminate. It yields a complete but not minimal set of unifiers. The same decomposition idea works for associativity too.
Lemma
4.4.
Let
F_, be
the
set
{ftf(x, y), z) =f(x,f(y, z>> Ife W, two terms. Zf g is in FA s
=A
t e
(SI
=A
t, A s2 =A
of
associative
and let s = g(s,,
t2) v (s,
v (ds,,
=A
g(h,
u,
function
u,
=A
symbols,
. . . , s,) and t = g(tl,.
A du,
t, n s2 =A
s2)
d”,
=A
let
A =
. . , r,) be
t2)
b))
for some term u. Otherwise s
=A
t
c3
vi. si
=A
tj.
In exactly the same way as for commutativity, it yields a unification algorithm. However, the process of deriving the two additional decomposition rules in Lemma 4.4 by resolution of the basic equality rules and associativity is more complex. Given ?xl =?yl Cong=f(?xl,
?x2 =?y2
?x2)=f(?yl,?y2)
and
asso=
f(f(?x, ?y)* ?z)=f(?x,
f(?y. ?z))
Equational reasoning in Isabel/e
139
the ML expression (trans RESN (2,cong RES refl)) RES ((trans RES (tong RESN (2, refl)))
evaluates
RESN (2, asso))
to acongl
In a similar
?x=f(?z,
=
f(?u, ?y)=?t
f(?x, ?y)=f(?z,
manner
acong2 =
?u)
(exercise!) f(?x, ?u)=?z
?t)
one can derive ?y=f(?u,
f(?x. ?y)=f(?z,
?t)
?t)
Since we do not care to spell out this process
in detail,
we simply
assume
there is
a function =fn : rule * rule + rule list
> val a_decomp
which takes a (tong, asso)-pair and returns the list [acongl, acong2]. This list can then be passed to unify to obtain a tactic for associative unification. It should however be added that there are two problems with the resulting tactic. It is well known [25] that complete sets of unifiers modulo associativity may be infinite, as for example in f(a, ?x)=f(?x, a), and that the decision problem is quite complex [17]. Our algorithm can be shown to be equivalent to Plotkin’s [25]. Therefore it enumerates all unifiers but is not guaranteed to terminate, even if there is a finite complete set of unifiers. Still worse, the definition of unify is based on a depth-first strategy (UNTIL) of applying the decomposition rules and may therefore not even enumerate all unifiers but disappear down some infinite branch. This latter problem can be fixed by using a breadth-first version of UNTIL which is also available in Isabelle. Fortunately, matching modulo associativity always leads to finite sets of unifiers and the above algorithm is guaranteed to terminate. Since matching is sufficient for rewriting, our algorithm fits the bill. The last and maybe most useful example is associativity-f commutativity (AC). It can be shown that the following three rules, in addition to the ones for associativity and commutativity alone, constitute a correct and complete system of inference rules for equality module AC. ?x=f(?t,
?u)
f(?u, ?y)=?z
f(?x, ?y) =f(?z, f(?x, ?u)=?t
?t)
?y=f(?u,
?z)
f(?x, ?y) =f(?z, ?t) ?x=f(?ul,?u2)
?y=f(?vl,?v2)
f(?ul,?vl)=?z
f(?x, ?y)=f(?z,
f(?u2,?v2)=?t
?t)
The derivation of these rules is not very difficult. The completeness proof involves a large number of case distinctions and shall not concern us here. The termination result is similar to associativity alone: unification need not terminate (although there
140
T. Nipkow
is always
a finite
surprising
if a naive
unification
complete
problem,
equations.
where
It is pleasant
With respect
set of unifiers),
syntactic
approach all known
enough
matching
It would
have been
could
solve the AC
algorithms
solving
diophantine
that matching
to the ML implementation
does.
like the one above involve
can be done in that fashion.
let us just assume that there is a function
=fn : rule * rule * rule + rule list
> vat ac_decomp
which computes all the required decomposition rules by composing associativity and commutativity-hence three arguments. There are a number of other theories which admit this treatment.
congruence, Usually
the
respective decomposition rules allow only matching, for example distributivity [ 191. Only trivial theories like idempotence yield a unification algorithm. Since we have demonstrated the basic mechanism and looked at some of the more useful theories we will not go into the subject more deeply. 4.5. Some simpli$cation
tactics
This section shows how the various kinds of rewriting introduced in the preceding sections can be combined into more powerful tactics for simplifying equations. Combining rewriting with rules (REW_TAC, Section 4.1) and assumptions (asm_rew_tac, Section 4.3), we obtain fun ASM_REW_TAC Integrating definition
rewriting
rwrls=REW_TAC
modulo
of asm_rew_tac
equations
rwrls ORELSE
asm_rew-tat;
as well is not quite as simple because the
makes no provisions
for a matching
tactic.
Hence
we
first need fun eq_asm_rew_tac strip_left_tac
match_tac=
THEN trans-tat
resolve_tat [assume] > val eq_asm_rew_tac=fn which extends asm_rew_tac
THEN
2 THEN match_tat; :tactic+ tactic
to take matching
into account.
EQASM-REW-TAC
is similar to ASM-REW-TAC: fun EQ_ASM_REW-TAC
match_tat
rwrls=
EQ_REW_TAC match_tat rwrls ORELSE eq_asm_rew_tac match-tat; > val EQ_ASM_REW_TAC=fn : tactic + rule list + tactic Finally
we have two simplification
NORM_EQN
and BU from Section
fun SIMP_TAC > val SIMP_TAC
tactics for equations
based
on the tacticals
4.1:
rwrls = NORM_EQN(BU(ASM_REW_TAC
rwrls));
=fn : rule list + tactic
fun EQ-SlMP_TAC match-tat rwrls= NORM_EQN(BU(EQ_ASM_REW_TAC > val EQ_SIMP_TAC=fn
: tactic+
match_tat
rule list + tactic
rwrls));
Equational
These
simplification
Therefore 4.6.
tactics
we refrain
Rewriting
Up to now
are sufficient
from presenting
by higher-order
we have
reasoning in Isabelle
141
for the sample
the integration
proofs
of conditional
in this
paper.
rewriting
as well.
of Isabelle.
All the
matching
not made
use of any special
feature
algorithms could be implemented on any general purpose theorem prover with a tactic language. What makes Isabelle special is the fact that it is based on higher-order logic, i.e. constants and variables in the object-logic can have function types. Consequently the basic inference engine contains a higher-order unification algorithm. We can use this fact to derive some simple rewriting tactics based on higher-order matching. The congruence rule for equational logic is sometimes expressed
as x=y
crx1= C[Yl ’ where x and y are terms and C stands for an arbitrary “context”. inference rule can be formalized almost exactly like this, assuming term is declared as a function variable:
In Isabelle this that C : term +
?x=?y h"-cong=?C(?x)=?C(?y) Given
a particular
val ho_eq=
equation,
ho_cong
ho_eq =
for example
RES eq yields
?C(O+?x)=?C(?x)
the rule .
Let us see what happens if ho_eq is resolved with an equational goal, for example s(0) +(0 +O)=?y. The unification algorithm instantiates ?C to hz.s(O) +z, ?x to 0 and ?y to s(O) +O. If there is more than one occurrence of 0 +?x, Isabelle’s unification algorithm matches against all of them, thus leading to a simultaneous replacement of multiple subterms: resolving (0 +0) +(0 +0) =?y with ho-eq leads to ?C=hz.z +z, ?x=O and
?y=O +O. Unfortunately, any equational goal can be resolved with ho-eq, even if the pattern O+?x does not appear on its right-hand side: ?C is simply matched to a constant function. Resolving the goal O=?y with ho_eq leads to ?C=Az.O, ?x=?x and ?y=O. Hence any repeated application of rewriting via higher-order matching has to detect normal forms by detecting that a rewrite step has had no effect. Instead of presenting a normalization tactic in the usual style, we can try and be a bit more fancy. Up to now we have only been concerned with normalizing terms and equations. If this is to be used for general theorem proving, we must be able
T. Nipkow
142
to normalize
subterms
of arbitrary
out for free. In the sequel ho_form_cong Resolving
an equation
=
formulae.
With higher-order
let P’ be a variable ?x=?y
variables
this drops
of type term + form and
?P’(?y)
?P’( ?x)
s = t with ho_form_cong
yields the rule M.
Using this rule
backward, we can replace s by t in an arbitrary formula P’. This leads to a simple tactic for normalizing all subterms of the first premise of a rule: fun HO-NORM-FORM let fun unchanged fun check_tat
ho-rules= rule1 rule2 =premisel rule1 =COND
fun ho_rew_tac
(rulel) =premisel
(unchanged
rulel)
(rule2);
(no_tac, all_tat);
rule=
(resolve_tat ho-rules 1 THEN check_tat in REPEAT ho_rew_tac end; >val HO_NORM_FORM=fn:rule list+ tactic
The parameter ho-rules should be a list of rules equations can be brought into this form by
rule) rule
of the form w_ Any list of elementwise resolution with
ho-form-tong.
The main body of the function is pretty obvious: rewriting is applied until it fails, indicating that we have reached a normal form. Rule application consists of resolving the first goal of the current rule with a rule in the list ho-rules, followed by a check that this has actually led to a rewrite, i.e. the first goal has changed. This check is performed in check_tat: depending on whether unchanged returns true or false, COND selects no_tac, i.e. fails, or all_tac, i.e. passes the rule through unchanged. The function premise1 of type rule+ term returns the leftmost premise of a rule and is provided Unfortunately,
by Isabelle. it only works
for unquantified
formulae.
In Isabelle,
quantified
formulae converted
are represented as A-expressions. For example ALL x.0+x=x would be to G =AII(Ax.O +x=x), where All is a logical constant of type (term+ form) + form. Unification of G with ?P’(O +?x) yields only the trivial unifier, i.e. the constant function, for ?P’. The reason is that it is impossible to write G as (At.AII(Ax.t=x))(O-tx)
because
be different. This problem can be solved the correct rule would be
p-conversion by going
would
to a higher
force the x in t=x
and 0+x
to
type level. In the case above,
?R(Ax.x) vho-cong=?R(Ax.O+x)
where ?R is of type (term + term) + form. This scheme works because any subterm of the form O+t can be written as (Ax.O+x)(t). However, it is not possible to give a rule like ho_form_cong which could turn any equation into an inference rule like vho-tong. This is because the exact form
Equorional reasoning
of the higher-order equation.
inference
A higher-order
rule depends
version
in Isabelle
143
on the number
of for example
?x +?y=?y
of free variables
in the
+?x is
?R(Ax.Ay.x +y) ?R(Ax.Ay.y+x) Having presented
the basic ideas, it is not very difficult to generalize
to conditional
rewrite rules. The exact details are omitted here as they do not involve new ideas. On the other hand it is not clear whether rewriting modulo equations and higher-order matching can be combined. The reason is that higher-order matching combines redex selection and matching. Hence it seems difficult to try different matches, for example by permuting arguments. The same is true for built-in notions of equality which the latest version of Isabelle provides. Using higher-order matching or built-in equality yields simpler and/or faster rewriting tactics, doing it by basic equality reasoning is ultimately more flexible if sometimes prohibitively more expensive. 4.7. Termination So far we have not mentioned the problem of termination. th,at the given set of rewrite rules induces a well-founded are a large number of techniques for proving termination (see [l]), all of which share the property that they require tion effort. Hence we have refrained from implementing However, there is no principle problem; all it requires the internal representation of terms in Isabelle.
5. Inductive
theorem
We have tacitly assumed relation on terms. There of term rewriting systems considerable implementaany of them for this work. is some knowledge about
proving
This section presents a simple tactic for inductive proofs of equations and uses it in conjunction with the simplification tactics of Section 4.5 to prove a collection of results about the natural numbers from first principles. The tactic itself is very simple: all subgoals resulting from the application of an induction schema are proved
by rewriting,
where
subgoals
representing
the induction
step can also be
rewritten with the induction hypothesis. In order to formulate induction schemata we need both universal quantification and some form of implication which for example sequents as introduced in Section 4.3 provide. A typical induction rule, the one for natural numbers generated by 0 and s (successor), looks like this: N_ind=?A
I- ?P’(O)
?A, ?P’(x) t ?P’(s(x))
?A F- ALL x. ?P’(x) where P’ is a variable of type term + form and A a list of assumptions. Isabelle provides the means for expressing that x should not occur free in ?P’ or ?A.
(8) also
144
T. Nipkow
We will now discuss
the induction
fun IND_TAC
sch eq-tat
ind_sch_tac
sch
simp_tac
starting
from the top-level
IND_TAC:
var=
sch var THEN simp_qnt_eqns
eq_tac
simp_tat;
=fn : rule + tactic + tactic + string + tactic
> val IND_TAC
The parameters
tactic,
and
var represent
the induction
schema
and
the induction
variable. The latter comes as a string to facilitate interactive use. The tactic ind_sch_tac is responsible for applying the given induction schema with the given variable: fun ind_sch-tat
sch var=
qnt_frees-tat
The function
var THEN resolve_tac
qnt_frees_tac
universally
quantifies
[sch] 1;
all free variables
in the first
premise, ensuring that the outermost quantification involves the induction variable var. We omit all code for qnt_frees_tac because it involves too much Isabelle specific detail and not much “logic”. Suffice it to say that universal quantification over some variable v is achieved by resolving with the predicate calculus rule a,,_e,im
=ALL x. ?P’(x) ?p’(?x)
after having instantiated variables, consider
?x with v. To see the need for explicit
quantification
of all
Example 5.1. We assume the usual equational definition of the data type of lists. For simplicity we use the ML notation for lists. We define two reverse functions on lists and show their equivalence: rev: list + list rev(( I)=[ I rev(a::I)=rev(l)@[a] irev: list x list + list irev([ 1, s)=s irev(a::l, s)=irev(l, a::s)
Structural
induction list_ind =
over lists can be written ?P’([ 1)
as
?P’(I) k (a::l)
ALL x. ?P’(x)
where neither I nor a may occur free in ?P’. By induction on I we show that irev(l, s)=rev(l)@s
(9)
holds. The base case follows by normalizing both sides. The induction step requires us to prove irev(a ::I, s)=rev(a ::I)@s under the assumption of (9). irev(a ::I, S) rewrites to irev(l, a ::s), which can only be rewritten to rev(l)@(a ::s) ifthe assumption is universally quantified over s. Hence (9) needs to be brought into the form ALL 1. ALL s. irev(l, s) = rev(l)@s.
Equational
After application subgoals eq_tac. depends eq_tac
of the induction
by first simplifying
reasoning
schema,
145
in Isabel/e
IND-TAC
them with simp_tac
tries to solve the resulting
and then finishing
This is where the real work is done and the success largely
on the quality
of the simp_tac
them off with
of an inductive
and eq_tac.
proof
In its simplest
form
is just refl_tat. fun simp_qnt-eqns eq-tat simp_tac= COND is-theorem (all_tat, strip_tat THEN simp_tac THEN ((eq-tat THEN simp-qnt-eqns ORELSE all_tac)
eq-tat
simp-tat)
);
The slightly convoluted form of this recursive tactic is due to the fact that it should either stop if all subgoals have been solved (is-theorem) or if a simplified equation cannot be solved by eq_tac. Since all of our simplification tactics expect to work on equations, strip-tat first removes the quantifiers introduced by ind_sch_tac: val strip_tac=REPEAT(resolve_tac AILintroduction
is the following
all_intr =
Isabelle
ensures
[all_intr]
basic predicate
1);
calculus
rule:
P’(x) ALL x. P’(x)
that the new variable x does not occur free in P’. on induction by presenting a longer example which features and the various simplification and matching tactics described in
We close this section
both induction Section 4.
Example 5.2. Let N_ind be as in (8) above, N_rules=[?At O+?y=?y, ?A+O*?y=O, ?A k- sum(O)=O, be the list of equations
defining
and let
?At- s(?x) +?y=s(?x+?y), ?A~s(?x)*?y=?y+(?x*?y), ?A I- sum(s(?x))=s(s(O))
addition,
multiplication
* s(?x) +sum(?x)]
and the sum of the first n
even numbers. Eventually we want to show that sum(x) =x * s(x) holds. On the way to this goal we show associativity and commutativity of both + and *, thus gaining access to rewriting modulo AC. The simplification tactics come from Section 4.5. Initially we have val simp_tac=SlMP_TAC val N_ind_tac=IND_TAC Our first goal is associativity
N-rules; N-ind refl_tac of addition:
goal “At-(x+y)+z=x+(y+z)“; >A~(x+y)+z=x+y+z ~1. A~(x+y)+z=x+y+z
simp_tat;
T. Nipkow
146
The function
goal is part of Isabelle’s
the only imperative formula
simple user interface
to the “subgoal
manager”,
part of the system: it reads a string and makes the corresponding
the first goal to be solved.
The output
(lines
starting
read as an inference
rule where the first line is the conclusion
lines are the premises
(=subgoals).
Remember
rule f and work our way backwards.
with >) should
that we start with the trivially
We attack
subgoal
be
and the numbered
1 by induction
correct
on x:
by(N_ind_tac “xl’); >AI-(x+y)+z=x+y+z No subgoal
has remained,
so the proof went through
by a single induction.
We save
the resulting rule by typing vat pa =top_rule( ) which generalizes (replaces x by ?x) the proved theorem and binds the result to the ML variable pa for later use. Next comes commutativity, which needs three inductions. Nonessential system output like the response to goal has been omitted. goal “A I- x +y=y
+x”;
by(N_ind_tac ‘lx”); >l. Akx4=x4+0 >2.
A, ALL u. x3+u=u+x3t-ALL
u. s(x3)+u=u+s(x3)
We are left with two subgoals: neither the induction basis nor the step could be proved by rewriting. We try another induction on “x4”, but only after reversing the first equation (sym_tac)-otherwise the induction hypothesis in that subproof is the nonterminating equation x4=x4+0. (Odd variable names are due to renaming caused by resolution.) by(sym_tac THEN N-ind_tac “x4”); > 1. A, ALL u. x3+u=u+x3~s(x39+x3)=x39+s(x3)
The first subgoal
has been solved
and simplification
of the second
goal (which
is
now the first) has started. After a final induction on “x39” all subgoals have disappeared. The commutativity theorem is stored in pc. Thus we have proved that + is AC, and we can generate the rules for AC decomposition and the corresponding matching
tactic val p_decomp=ac_decomp(pcong,
pa, PC);
val ac_match_tac=unify(p_decomp);
which gives rise to a more powerful
simplification
and induction
val simp_tac=EQ_SIMP_TAC ac-match-tat val N_ind_tac= ind-tat N_ind ac-match-tat
which helps us to tackle the next theorem,
distributivity
tactic
N-rules; simp-tat;
of * over +:
goal “A F (x +y) * z=x * z +y * z”; by( N_ind_tac
“x”);
One induction proves the equation and we append it to our list of rewrite rules N-rules. The tactics simp-tat and N_ind_tac are redefined with the extended set
Equational
reasoning
of rules.
Next on our list is associativity
relevant
output
in Isabelle
and commutativity
147
of *. Again,
only the
is reproduced:
goal “At-
(x * y) * z=x * y * z”;
by( N_ind_tac
‘lx”);
val ta =top_rule(
);
goal “A k x * y=y * x”; by(N_ind_tac “xl’); >l.At-0=x4*0 >2.A,ALLu.x3*u=u*x3tALLu.s(x3)*u=u*s(x3) by(sym_tac THEN N_ind-tat “x4”); >1.A,ALLu.x3*u=u*x3tx38+x38*x3=x38*s(x3) by(sym_tac
THEN
val tc=top_rule(
N-ind-tat
“x38”);
);
In fact, the proof is rather similar to the one for AC of +. We can now combine the AC decomposition rules to obtain an AC matching algorithm for both + and *:
val t_decomp = ac_decomp(tcong, val ac_match_tac=match_tac(p_decomp The tactics simp_tac Finally we can show:
and
N-ind-tat
ta, tc);
are redefined
@ t-decomp); with the new matching
tactic.
goal “sum(x)=x * s(x)“; by( N_ind_tac ‘lx”);
Since all the proofs have gone through, it is not obvious that we actually needed to prove both + and * AC before we could solve the last proof. Just to give an example of what happens if we try to prove the last theorem directly: N_ind_tac leaves the subgoal > 1. s((x2 +x2 * s(x2)) +s((x2 +x2 * s(x2)) +O))=s(s(x2 around
which requires
+x2 * s(s(x2))))
AC and distributivity.
6. Conclusions We have presented implementations of a number of different techniques for equational proofs in a general theorem proving framework. All of them are rather short, most of them not difficult to understand and some of them even elegant. Partly this is due to the fact that we used a functional language, but mostly to the fact that Isabelle provides the right infrastructure and abstractions (rules, tactics, tacticals). We hope that this is sufficient proof not just of the feasibility but also the attractiveness of our approach. Unfortunately efficiency is still problematic. Ordinary rewriting proceeds at a rate of about 5 reductions per second (on a VAX
T. Nipkow
148
3600). The complexity and the number In addition universal
to the examples
unification
equations A similar
of AC-rewriting
of AC-operators
depends
very much on the size of the term
in it. It is typically presented
an order of magnitude
in the paper
as in [ 111 and the basic Knuth-Bendix
slower.
we have also implemented algorithm
[15], orienting
by user interaction. program
has been carried
out by Larry Paulson
in LCF and reported
in [20]. His rewriting tactics are more general in some respects and less so in others. In particular he does not consider rewriting module equations. On the implementation side the biggest difference is the simplicity of rewriting in Isabelle due to built-in unification, not available in LCF. Another system with similar capabilities to Isabelle is A-Prolog. In [2] it is shown how A-Prolog can be used to specify theorem provers. It does not seem difficult to implement all of our rewriting tactics in A-Prolog. In fact, it would be interesting to see how far one could
go in implementing
them in ordinary
Prolog.
Acknowledgement Many thanks go to Larry Paulson for his Isabelle system and a number of helpful discussions, to Dave Matthews for his Poly/ML system, to Cliff Jones and John Guttag for providing the excellent environment for this research, and to Mario Wolczko for making _ and @ work (almost). Some of the explanations and code in Section 2.2 are directly taken from Isabelle source files.
References [I] N. Dershowitz, Termination of rewriting, J. Symbolic Comput. 3 (1987) 69-117. [2] A. Felty and D. Miller, Specifying theorem provers in a higher-order logic programming language, in: Proceedings CADE-9, Lecture Notes in Computer Science 310 (Springer, Berlin, 1988) 61-80. [3] J.H. Gallier, Logicfor Computer Science (Harper and Row, New York, 1986). [4] S.J. Garland and J.V. Guttag, Inductive methods for reasoning about abstract data types, in: Proceedings 15rh POPL, San Diego, CA (1988) 219-228. [5] A. Geser and H. Hussmann, Experience with the RAP system: A specification interpreter combining term rewriting and resolution, in: Proceedings ESOP-86, Lecture Notes in Computer Science 213 (Springer, Berlin, 1986) 339-350. [6] M.J.C. Gordon, R. Mimer and C.P. Wadsworth, Edinburgh LCF: A Mechanised Logic of Computation, Lecture Notes in Computer Science 78 (Springer, Berlin, 1979). [7] Ph. de Groote, How I Spent My Time in Cambridge with Isabelle, Rept. RR 87-1, Unit6 d’lnformatique, Universite Catholique de Louvain, Belgium (1987). [8] R. Harper, D. MacQueen and R. Milner, Standard ML, Rept. ECS-LFCS-86-2, Lab for Foundations of Computer Science, Department of Computer Science, University of Edinburgh (1986). [9] A. Herold, Combination of unification algorithms in equational theories, Ph.D. Thesis, FB Informatik, Universitat Kaiserslautern (1987). [IO] G. Huet and D.C. Oppen, Equations and rewrite rules: A survey, in: R.V. Book, Ed., Formal Language Theory: Perspectives and Open Probiems (Academic Press, New York, 1980). [ 1 I] H. Hussmann, Unification in conditional-equational theories, in: Proceedings EUROCAL-85, Lecture Notes in Computer Science 204 (Springer, Berlin, 1985).
Equational
reasoning
in Isabelle
149
1121 J.-P. Jouannaud and H. Kirchner, Completion of a set of rules module a set of equations, SIAM J. Comput. 15 (1986) 1155-1194. [13] C. Kirchner, Computing unification algorithms, in: Proceedings Symposium on Logic in Computer Science, Cambridge (1986) 206-217. [14] C. Kirchner and H. Kirchner, REVEUR-3: The implementation of a general completion procedure parameterized by built-in theories and strategies, Sci. Comput. Programming 8 (1987) 69-86. [15] D.E. Knuth and P.B. Bendix, Simple word problems in universal algebra, in: J. Leech, Ed., Computational Problems in Abstract Algebra (Pergamon, Oxford, 1970) 263-297. [16] P. Lescanne, REVE: A rewrite rule laboratory, in: Proceedings CADE-8, Lecture Notes in Computer Science 230 (Springer, Berlin, 1986) 695-696. [17] G.S. Makanin, The problem of solvability of equations in a free semigroup, Soviet Akad. Nauk SSSR 233 (1977). [18]
A. Martelli Languages
and
U. Montanari,
Syst. 4 (2) (1982)
An efficient
unification
algorithm,
ACM
Trans.
Programming
258-282.
J. Mzali, Matching with distributivity, in: Proceedings CADE-8, Lecture Notes in Computer Science 230 (Springer, Berlin, 1986) 496-505. [20] L.C. Paulson, A higher-order implementation of rewriting, Sci. Comput. Programming 3 (1983) [19]
119-149. 1211 L.C. Paulson,
Natural deduction as higher-order resolution, J. Logic Programming 3 (1986) 237-258. L.C. Paulson, Logic and Computation (Cambridge University Press, New York, 1987). [23] L.C. Paulson, The foundation of a generic theorem prover, J. Automated Reasoning (to appear). [24] L.C. Paulson, A preliminary user’s manual for Isabelle, Rept. 133, Computer Laboratory, University of Cambridge (1988). [25] G.D. Plotkin, Building-in equational theories, in B. Meltzerand D. Michie, Eds., Machinelntelligevce 7 (Edinburgh University Press, Edinburgh, 1972) 73-90. [22]