27
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)

Equational reasoning in Isabelle

Embed Size (px)

Citation preview

Page 1: Equational reasoning in Isabelle

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)

Page 2: Equational reasoning in Isabelle

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

Page 3: Equational reasoning in Isabelle

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 ! .

Page 4: Equational reasoning in Isabelle

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

Page 5: Equational reasoning in Isabelle

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.

Page 6: Equational reasoning in Isabelle

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

Page 7: Equational reasoning in Isabelle

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.

Page 8: Equational reasoning in Isabelle

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)]

Page 9: Equational reasoning in Isabelle

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

Page 10: Equational reasoning in Isabelle

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

Page 11: Equational reasoning in Isabelle

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

Page 12: Equational reasoning in Isabelle

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

Page 13: Equational reasoning in Isabelle

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

Page 14: Equational reasoning in Isabelle

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)

Page 15: Equational reasoning in Isabelle

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

Page 16: Equational reasoning in Isabelle

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))

Page 17: Equational reasoning in Isabelle

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

Page 18: Equational reasoning in Isabelle

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

Page 19: Equational reasoning in Isabelle

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

Page 20: Equational reasoning in Isabelle

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

Page 21: Equational reasoning in Isabelle

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.

Page 22: Equational reasoning in Isabelle

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.

Page 23: Equational reasoning in Isabelle

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

Page 24: Equational reasoning in Isabelle

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

Page 25: Equational reasoning in Isabelle

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

Page 26: Equational reasoning in Isabelle

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).

Page 27: Equational reasoning in Isabelle

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.