Upload
tobias-nipkow
View
215
Download
1
Embed Size (px)
Citation preview
Science of Computer Programming 12 (1989) 123-149
North-Holland
123
EQUATIONAL REASONING IN ISABELLE
Tobias 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 of incorporating rewriting techniques does not require
any changes to the inference engine of the theorem prover. Particular term rewriting
algorithms are presented as tactics which use the basic equational calculus inference
rules, very much in the style of [20]. The obvious advantage is the guaranteed
soundness of all deductions (provided the underlying 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-K-
0125.
0167-6423/89/$3.50 @ 1989, Elsevier Science Publishers B.V. (North-Holland)
124 7: Nipkow
LCF-philosophy whereas the latter is due to the generic nature of Isabelle. This
openness has permitted us to incorporate inductive theorem proving techniques into
our collection of tactics, something which goes beyond pure equational logic. A
similar development is reported in [4].
Simplicity. Isabelle’s primitives provide most of the functionality required for
symbolic theorem proving. In particular it offers both functional 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 condi-
tional 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 reasoning irl Isabeile 125
underlying Isabelle is contained in [21], a formalization of Isabelle’s metalogic
using higher-order 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 in Isabelle amounts to combining the basic rules to form derived
rules. The principal method for combining two rules is resolution: given two rules
* = p, . . . pt?l 9, . ..Qn P
and q= Q ’
and a substitution u which unifies P with Q, for some i, resolving p and q yields
the new rule
(+ Q,... (
Qi-1 f’, . . . Pm Qi+l . . . Qn Q >. (1)
This brings us to another important Isabelle feature: it is based on unification rather
than just matching (as for example 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 ! .
126 T. Nipkow
All this sounds very much like logic programming, and in fact Isabelle can be
seen as an implementation of typed higher-order logic programming (see also [21]).
Finally I should mention that the version of Isabelle described in this paper is
Isabelle-86. Meanwhile Isabelle has been extended significantly and both the founda-
tions 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 =fn : rule list + int + tactic
Applying resolve_tac rl i to some rule r yields the stream of resolvents of rules in
rl with the ith premise 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, yielding a new
derived 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 :tactic > val no_tac=fn :tactic > val THEN =fn : tactic * tactic + tactic > val ORELSE=fn :tactic * tactic- tactic > val COND =fn : (rule + bool) + tactic * tactic + tactic
>val REPEAT=fn:tactic+tactic > val UNTIL=fn:(rule+ bool)+ tactic-, tactic
> val UNTlL_THM =fn :tactic+ tactic >val SELECT_lst_GOAL=fn:tactic+tactic
Equational reasoning in Isabelle 127
In the following explanation of these tactics and tacticals we simplify slightly by
identifying the type sequence with the type list. Hence we abstract from problems
arising from representing infinite lists.
The basic ML operations on lists are [ 1, the empty list, :: , the infix operator for
putting an element in front of a list and @, the infix append operator. Isabelle
provides the following additional functions:
>val hd=fn:‘a list+‘a >val map=fn:(‘a+‘b)+‘a list+‘b list
>val flat=fn :‘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 rule= [rule];
The tactic no_tac always fails, i.e. returns an empty list:
fun no-tat rule=[ 1;
The tactical THEN performs one tactic followed by another:
fun (tacl THEN tac2) rule=flat (map tac2 (tacl rule));
The tactical ORELSE uses the first tactic that returns a nonempty sequence. Like in
LCF, ORELSE commits to either tacl or tac2 immediately:
fun (tacl ORELSE tac2) rule=
if tacl rule =[ ] then tad rule else tacl rule;
The conditional tactical selects either tacl or tac2, depending on testfun:
fun COND testfun (tacl, tac2) rule= if testfun rule then tacl rule else tac2 rule;
REPEAT applies tat repeatedly until it fails. It performs no backtracking:
fun REPEAT tat rule=((tac THEN REPEAT tat) ORELSE all_tac) rule;
UNTIL applies tat until satfun reports success:
fun UNTlL satfun tat rule=
COND satfun (all-tat, tat THEN UNTIL satfun tat) rule;
As a particular instance we have
val UNTIL_THM=UNTIL is-theorem;
which repeats a tactic until a theorem (is-theorem), i.e. a rule without premises, is
obtained.
SELECT_lst_GOAL makes a rule look like it has only one premise, the first one,
and applies the tactic to it.
128 T. Nipkow
In addition to the tactics and tacticals described here, Isabelle provides a collection
of other functions, mostly to do with rule and term manipulation, none of which
we discuss. Any function which is only mentioned in passing but is not defined can
be assumed to be one of those.or to be a simple combination of those.
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. Isabelle allows “subscripting” (xl,
y2, etc.) to create new variables.
The inference rules of the equational calculus consist of the three equality rules
refl=- ?x=?x
?x=?y wm=, .y .x
trans = ?x=?y ?y=?z
?x=?z
and a congruence rule
?xl =?yl . . . ?xn=?yn
f(?xl, . . , ?xn)=f(?yl,. . . , ?yn)
for every n-ary, n 3 0, function symbol f. In the sequel tongs denotes the list of all
such congruence rules for a given set of function symbols.
For later use in equational proofs we define the following 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 in any implementation of equatorial logic is term
rewriting, or more precisely, the process of finding normal forms of terms w.r.t. to
some given rewrite relation. For a concise introduction to equational logic and term
Equa/ional reasoning in Isabel/e 129
rewriting see for example [lo]. Suffice it to say that under certain conditions equality
of terms in some theory can be decided by comparing their normal forms.
Although this problem looks like one of transforming terms, in the context of a
theorem proving system we have to rephrase it in terms of inference rules. In Isabelle
the problem can be expressed as the equation s = ?x, where s is the term to be
normalized and ?x the variable we want to bind the normal form to. Hence we are
often concerned with rules of the form
s=?x H
P (2)
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 modular-
ity: 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 assump-
tions, 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 tools
As a simple 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) ORELSE refl_tat);
> val ALL_SUBTS =fn : tactics + tactic
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.
130 7: Nipkow
Here is the tactical for bottom-up normalization of terms.
fun BU rew-tat rule=
(SELECT_lst_GOAL
(UNTIL_THM (trans_tat THEN ALL_SUBTS(BU rew-tat)) THEN
((trans_tat THEN rew-tat) ORELSE refl_tat)))) rule;
> val BU =fn :tactic+ 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 incon-
venience.
We assume that the parameter rew_tac represents a rewrite relation + and can
be applied to any rule of the form (2), yielding a rule F, where ?x has been bound
to some t such that s + t holds. Roughly speaking + represents the relation obtained
by taking all 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 rwrls=resolve_tac rwrls 1;
>val REW_TAC=fn:rule list+ tactic
fun NORM_LHS-TAC rwrls=BU (REW-TAC rwrls); > val NORM_LHS_TAC=fn : rule list + tactic
The understanding is that rwrls is a list of equations of the form I = r. Unifying that
with an equation s = ?x, where s is ?-free, clearly has the effect of instantiating ?x
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)]
Equarional reasoning in Isabel/e 131
be a simple equational definition of addition (+) in terms of 0 and successor (s).
We look at the application of NORM_LHS_TAC N-rules to a rule with the first
premise
trans_tat: ALL_SUBTS(BU rew-tat):
trans-tat rew_tac:
trans_tat: ALL_SUBTS(BU rew-tat):
trans_tat:
rew_tac:
refl_tat:
s(0) +o=?x s(0) +0=?y ?y=?x s(0) +0=?x s(0) +O=?yl ?yl =?x
s(0 +O) =?x s(O+O)=?y3 ?y3=?x
s(O)=?x s(0) =?y4 ?y4 = ?x
fails, back to s(O)=?x
solves the subgoal, binding ?x to s(0).
Instead of bottom-up, we can also implement other strategies like leftmost-
outermost. In 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 but
both sides of an equation. This can be accomplished by splitting the equation first
according to the following rule:
split-eqn = ?s=?x ?t=?y ?x=?y
?s=?t
which can be derived from the basic equational laws as follows:
?x=?y ?t=?y -
?s=?x ?y=?x
?t=?x
?x=?t
?s=?t
In Isabelle this linearizes to
val split_eqn=trans RESN (2, sym RES (trans RESN (2, sym))); > val split_eqn =? : rule
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 THEN norm-lhs-tat;
> val NORM_EQN=fn:tactic+ tactic
132 T. Nipkow
Proving an equation is achieved by normalizing it followed by an application of reflexivity:
fun PROVE-EQN norm_lhs_tac=
NORM-EQN norm_lhs_tac THEN refl_tat;
> val PROVE_EQN = fn : tactic + tactic
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=[] then [] else hd(tac rule);
> val DETERM =fn :tactic+ tactic
Thus a more efficient version of 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 of representing conditional equations in first-order
predicate calculus, for example as theorems of the form
sl=tl & . . . & sn=tn*s=t (3)
where & is conjunction and + implication. We have chosen to represent them as
inference rules of the form
sl=tl . ..sn=tn
s=t (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 reasoning in Isabel/e 133
resolution does not work because the logical connectives + and & get in the way,
It is easy to provide a function for the translation from (3) to (4): repeated resolution
with the rules
?P ?P3?0 ?P ?Q imp_elim =
?Q and conj_intr=
?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 crwrls rule and cnorm_lhs_tac rule = BU crew_tac rule
and prove-eqn-tat rule = PROVE-EON cnorm_lhs_tac rule
in cnorm_lhs_tac end; > val CNORM_LHS_TAC=fn : rule list+ tactic
Example 4.2. Let L-rules be the following list of conditional rewrite rules
R, = ?x<?x=tt
RI= s(?x) <?x=ff
?x<?y=tt
R3=?x<s(?y)=tt
?x<?y=ff
R4=s(?x)<?y=ff
and apply CNORM_LHS-TAC L-rules to the inference rule
s(s(s(0))) -c s(0) =?x s(s(s(0))) < s(0) =?x
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))) <o=tt
s(s(s(0)))<s(0)=tt
128 T. Nipkow
In addition to the tactics and tacticals described here, Isabelle provides a collection
of other functions, mostly to do with rule and term manipulation, none of which
we discuss. Any function which is only mentioned in passing but is not defined can
be assumed to be one of those.or to be a simple combination of those.
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. Isabelle allows “subscripting” (xl,
y2, etc.) to create new variables.
The inference rules of the equational calculus consist of the three equality rules
refl=- ?x=?x
?x=?y wm=, .y .x
trans = ?x=?y ?y=?z
?x=?z
and a congruence rule
?xl =?yl . . . ?xn=?yn
f(?xl, . . , ?xn)=f(?yl,. . . , ?yn)
for every n-ary, n 3 0, function symbol f. In the sequel tongs denotes the list of all
such congruence rules for a given set of function symbols.
For later use in equational proofs we define the following 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 in any implementation of equatorial logic is term
rewriting, or more precisely, the process of finding normal forms of terms w.r.t. to
some given rewrite relation. For a concise introduction to equational logic and term
Equational reasoning in Isabel/e 135
In order to avoid this trap, free variables in assumptions need to be quantified
universally: the sequent ALL X.X +0=x I- 1 +(0 +0) =O is provable. The presence of
ALL permits us to obtain arbitrarily many instances of a formula. Before rewriting
is possible, the quantifiers have to be removed. Universal quantifiers in the assump-
tions are removed by resolution with
?A, ?P’(?x), ?B F ?P
a”-‘ntr-asm =?A, ALL x.?P’(x), ?B !- ?P
a simple derived rule in predicate calculus. The concrete syntax of universal quan-
tification should be sufficiently close to ordinary mathematics to make this rule
self-explanatory: each application of all-intr_asm peels off one quantifier, replacing
the bound variable by a ?-variable. The formalization of quantifiers in Isabelle is
treated in more detail in Section 4.6.
Now rewriting with assumptions becomes
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] 1; > val asm_rew_tac =fn : tactic
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) rule
This tactic first tries to rewrite with the given list of (possibly conditional) 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 uncondi-
tional 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 not to formulate certain properties as rewrite rules but
to integrate them directly into the rewriting machinery. This does not only enhance
136 T. Nipkow
the expressive power of term rewriting systems but may actually be mandatory: due
to termination problems, certain properties, like commutativity, cannot be used as
rewrite rules. To overcome this problem, presentations of equational theories are
split into a set of equations 8 and a set of rewrite rules 9. Conceptually rewriting
is now a relation between %-equivalence classes of terms. The general theory of
rewriting modulo equations is rather subtle and is discussed for example 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 parameter-
ized 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 be a tactic taking a rule of the form
s=t 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
s=?x H
trans-tat: s=?y ?y=?x H
resolve_tac rwrls 2: s=l H [?x==r]
match_tat: H [?x=r, 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.
4.4.1. UniJication and matching
In this section unification and matching algorithms for commutativity, associativ-
ity, 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
ul =wl . . un=wn
P (6)
E9uotional reasoning in Isabel/e 137
where the equations above the line are the ones to be solved. Each transformation
should simplify the equations in some sense. The process terminates when all
equations have disappeared, i.e. have been solved. The variable bindings created
on the way constitute a solution. In contrast to Claude Kirchner, who distinguishes
two kinds of simplifications, decomposition and mutation, 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 (tongs @ decomp_rules) 1 ORELSE refl_tat)));
> val unify=fn : rule list + tactic
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 have been solved.
A trivial instantiation of unify yields the Robinson unification algorithm. All we
need as decomposition laws are the congruence rules tongs:
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. Let Fc be the set of commutative function symbols, let C =
{f(x, y) =f(y, x)/f6 F,}, and let s = g(s,, . . . , s,) and t = g(tl, . . . , t,) be fwo terms.
If g is in Fc
138 27 Nipkow
Otherwise
s =c t e Vi. si =c t,.
All we need to do is to generate the second decomposition rule
?xl =?y2 ?x2=?yl
comm-cong=f(?xl, ?x2)=f(?yl, ?y2)
from the rules
?xl =?yl ?x2 = ?y2
Cong=f(?xl,?x2)=f(?yl,?y2) and comm=
f(?xl, ?x2)=f(?x2, ?xl)
It is not difficult to see that trans RES tong RES comm yields comm-tong. Thus
the following function computes the required decomposition rule
fun c_decomp(cong, comm)=trans RES comm RES tong;
> val c-decomp =fn : rule * rule + rule
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 of associative function symbols, let A =
{ftf(x, y), z) =f(x,f(y, z>> Ife W, and let s = g(s,, . . . , s,) and t = g(tl,. . . , r,) be two terms. Zf g is in FA
s =A t e (SI =A t, A s2 =A t2) v (s, =A g(h, u, A du, s2) =A t2)
v (ds,, u, =A t, n s2 =A d”, 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 ?x2 =?y2
Cong=f(?xl, ?x2)=f(?yl,?y2) and asso=
f(f(?x, ?y)* ?z)=f(?x, f(?y. ?z))
Equational reasoning in Isabel/e
the ML expression
139
(trans RESN (2,cong RES refl)) RES ((trans RES (tong RESN (2, refl))) RESN (2, asso))
evaluates to
acongl = ?x=f(?z, ?u) f(?u, ?y)=?t
f(?x, ?y)=f(?z, ?t)
In a similar manner (exercise!) one can derive
acong2 = f(?x, ?u)=?z ?y=f(?u, ?t)
f(?x. ?y)=f(?z, ?t)
Since we do not care to spell out this process in detail, we simply assume there is
a function
> val a_decomp =fn : rule * rule + rule list
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, ?t)
f(?x, ?u)=?t ?y=f(?u, ?z)
f(?x, ?y) =f(?z, ?t)
?x=f(?ul,?u2) ?y=f(?vl,?v2) f(?ul,?vl)=?z f(?u2,?v2)=?t
f(?x, ?y)=f(?z, ?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 complete set of unifiers), matching does. It would have been
surprising if a naive syntactic approach like the one above could solve the AC
unification problem, where all known algorithms involve solving diophantine
equations. It is pleasant enough that matching can be done in that fashion.
With respect to the ML implementation let us just assume that there is a function
> vat ac_decomp =fn : rule * rule * rule + rule list
which computes all the required decomposition rules by composing congruence,
associativity and commutativity-hence three arguments.
There are a number of other theories which admit this treatment. 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 rwrls=REW_TAC rwrls ORELSE asm_rew-tat;
Integrating rewriting modulo equations as well is not quite as simple because the
definition of asm_rew_tac makes no provisions for a matching tactic. Hence we
first need
fun eq_asm_rew_tac match_tac=
strip_left_tac THEN trans-tat THEN
resolve_tat [assume] 2 THEN match_tat; > val eq_asm_rew_tac=fn :tactic+ tactic
which extends asm_rew_tac 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 tactics for equations based on the tacticals
NORM_EQN and BU from Section 4.1:
fun SIMP_TAC rwrls = NORM_EQN(BU(ASM_REW_TAC rwrls));
> val SIMP_TAC =fn : rule list + tactic
fun EQ-SlMP_TAC match-tat rwrls= NORM_EQN(BU(EQ_ASM_REW_TAC match_tat rwrls));
> val EQ_SIMP_TAC=fn : tactic+ rule list + tactic
Equational reasoning in Isabelle 141
These simplification tactics are sufficient for the sample proofs in this paper.
Therefore we refrain from presenting the integration of conditional rewriting as well.
4.6. Rewriting by higher-order matching
Up to now we have not made use of any special feature of Isabelle. All the
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”. In Isabelle this
inference rule can be formalized almost exactly like this, assuming that C : term +
term is declared as a function variable:
?x=?y h"-cong=?C(?x)=?C(?y)
Given a particular equation, for example
val ho_eq= ho_cong RES eq yields the rule
ho_eq = ?C(O+?x)=?C(?x) .
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
142 T. Nipkow
to normalize subterms of arbitrary formulae. With higher-order variables this drops
out for free. In the sequel let P’ be a variable of type term + form and
ho_form_cong = ?x=?y ?P’(?y)
?P’( ?x)
Resolving an equation 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 ho-rules= let fun unchanged rule1 rule2 =premisel (rulel) =premisel (rule2);
fun check_tat rule1 =COND (unchanged rulel) (no_tac, all_tat);
fun ho_rew_tac rule=
(resolve_tat ho-rules 1 THEN check_tat rule) rule in REPEAT ho_rew_tac end;
>val HO_NORM_FORM=fn:rule list+ tactic
The parameter ho-rules should be a list of rules of the form w_ Any list of
equations can be brought into this form by 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 by Isabelle.
Unfortunately, it only works for unquantified formulae. In Isabelle, quantified
formulae are represented as A-expressions. For example ALL x.0+x=x would be
converted 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 p-conversion would force the x in t=x and 0+x to
be different.
This problem can be solved by going to a higher type level. In the case above,
the correct rule would be
?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 in Isabelle 143
of the higher-order inference rule depends on the number of free variables in the
equation. A higher-order version of for example ?x +?y=?y +?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. We have tacitly assumed
th,at the given set of rewrite rules induces a well-founded relation on terms. There
are a large number of techniques for proving termination of term rewriting systems
(see [l]), all of which share the property that they require considerable implementa-
tion effort. Hence we have refrained from implementing any of them for this work.
However, there is no principle problem; all it requires is some knowledge about
the internal representation of terms in Isabelle.
5. Inductive theorem 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) (8)
where P’ is a variable of type term + form and A a list of assumptions. Isabelle also
provides the means for expressing that x should not occur free in ?P’ or ?A.
144 T. Nipkow
We will now discuss the induction tactic, starting from the top-level IND_TAC:
fun IND_TAC sch eq-tat simp_tac var=
ind_sch_tac sch var THEN simp_qnt_eqns eq_tac simp_tat;
> val IND_TAC =fn : rule + tactic + tactic + string + tactic
The parameters sch 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 var THEN resolve_tac [sch] 1;
The function qnt_frees_tac universally quantifies 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 ?x with v. To see the need for explicit quantification of all
variables, consider
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 over lists can be written as
list_ind = ?P’([ 1) ?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 reasoning in Isabel/e 145
After application of the induction schema, IND-TAC tries to solve the resulting
subgoals by first simplifying them with simp_tac and then finishing them off with
eq_tac. This is where the real work is done and the success of an inductive proof
depends largely on the quality of the simp_tac and eq_tac. In its simplest form
eq_tac 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 eq-tat simp-tat) ORELSE all_tac)
);
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 [all_intr] 1);
AILintroduction is the following basic predicate calculus rule:
all_intr = P’(x) ALL x. P’(x)
Isabelle ensures that the new variable x does not occur free in P’.
We close this section on induction by presenting a longer example which features
both induction and the various simplification and matching tactics described in
Section 4.
Example 5.2. Let N_ind be as in (8) above, and let
N_rules=[?At O+?y=?y, ?At- s(?x) +?y=s(?x+?y), ?A+O*?y=O, ?A~s(?x)*?y=?y+(?x*?y),
?A k- sum(O)=O, ?A I- sum(s(?x))=s(s(O)) * s(?x) +sum(?x)]
be the list of equations defining addition, multiplication 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 N-rules; val N_ind_tac=IND_TAC N-ind refl_tac simp_tat;
Our first goal is associativity 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
146 T. Nipkow
The function goal is part of Isabelle’s simple user interface to the “subgoal manager”,
the only imperative part of the system: it reads a string and makes the corresponding
formula the first goal to be solved. The output (lines starting with >) should be
read as an inference rule where the first line is the conclusion and the numbered
lines are the premises (=subgoals). Remember that we start with the trivially correct
rule f and work our way backwards. We attack subgoal 1 by induction 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 tactic
val simp_tac=EQ_SIMP_TAC ac-match-tat N-rules; val N_ind_tac= ind-tat N_ind ac-match-tat simp-tat;
which helps us to tackle the next theorem, distributivity 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 in Isabelle 147
of rules. Next on our list is associativity and commutativity of *. Again, only the
relevant output 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 N-ind-tat “x38”);
val tc=top_rule( );
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, ta, tc); val ac_match_tac=match_tac(p_decomp @ t-decomp);
The tactics simp_tac and N-ind-tat are redefined with the new matching tactic.
Finally we can show:
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 +x2 * s(s(x2))))
around which requires 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
148 T. Nipkow
3600). The complexity of AC-rewriting depends very much on the size of the term
and the number of AC-operators in it. It is typically an order of magnitude slower.
In addition to the examples presented in the paper we have also implemented
universal unification as in [ 111 and the basic Knuth-Bendix algorithm [15], orienting
equations by user interaction.
A similar 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 implementa-
tion 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 Computa-
tion, 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’lnfor-
matique, 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 Infor-
matik, 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 and U. Montanari, An efficient unification algorithm, ACM Trans. Programming
Languages Syst. 4 (2) (1982) 258-282.
[19] 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)
119-149.
1211 L.C. Paulson, Natural deduction as higher-order resolution, J. Logic Programming 3 (1986) 237-258.
[22] 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.