UsingZ: Specification, Refinement, and Proof
UsingZ: Specification, Refinement, and Proof
Jim Woodcock
University of Oxford
Jim Davies
University of Oxford
Foreword xi
Acknowledgments xv
1 Introduction 1
1.1 Formal methods 1
1.2 The CICS experience 2
1.3 The Z notation 3
1.4 The importance of proof 4
1.5 Abstraction 5
2 Propositional Logic 9
2.1 Propositional logic 9
2.2 Conjunction 10
2.3 Disjunction 13
2.4 Implication 14
2.5 Equivalence 17
2.6 Negation 20
2.7 Tautologies and contradictions 23
3 Predicate Logic 27
3.1 Predicate calculus 28
3.2 Quantifiers and declarations 30
3.3 Substitution 34
vi
5 Sets 57
5.1 Membership and extension 57
5.2 Set comprehension 61
5.3 Power sets 64
5.4 Cartesian products 65
5.5 Union, intersection, and difference 67
5.6 Types 69
5.7 Proofs 70
6 Definitions 73
6.1 Declarations 73
6.2 Abbreviations 74
6.3 Generic abbreviations 75
6.4 Axiomatic definitions 77
6.5 Generic definitions 79
6.6 Sets and predicates 81
7 Relations 83
7.1 Binary relations 83
7.2 Domain and range 85
7.3 Relational inverse 88
7.4 Relational composition 91
7.5 Closures 94
8 Functions 99
8.1 Partial functions 99
8.2 Lambda notation 101
8.3 Functions on relations 104
8.4 Overriding 106
8.5 Properties of functions 108
8.6 Finite sets 112
vii
9 Sequences 115
9.1 Sequence notation 115
9.2 A model for sequences 119
9.3 Functions on sequences 122
9.4 Structural induction 124
9.5 Bags 128
11 Schemas 147
11.1 The schema 147
11.2 Schemas as types 152
11.3 Schemas as declarations 154
11.4 Schemas as predicates 158
11.5 Renaming 160
11.6 Generic schemas 162
13 Promotion 185
13.1 Factoring operations 185
13.2 Promotion 193
13.3 Free and constrained promotion 196
14 Preconditions 201
14.1 The initialisation theorem 201
14.2 Precondition investigation 203
14.3 Calculation and simplification 206
14.4 Structure and preconditions 210
viii
Index 379
Notation 385
Resources 387
Thanks 391
Foreword
Formal methods require a soundly based specification language. Until now the
emphasis in the Z literature has been on the use of Z as a specification language.
In this arena, use of Z is extensive and has been fostered by its many positive
aspects, including the importance that has been placed on the careful merging
of text and formal material.
The construction of a clear specification is the cornerstone of any formal
development and—as the authors of the current book make clear—sometimes
there is little incentive to go further with formalism than to provide such a
specification.
But formal methods should also include a precise notion of correctness: a
formal method should pin down exactly what it means for a design to satisfy a
specification and provide tractable proof obligations for use in a development
which requires formal justification. This book addresses notions of develop-
ment based on Z specifications.
The authors’ emphasis on proof should be applauded. Although some-
times seen as a difficult undertaking, formal proofs are justified for systems
on which major reliance is to be placed. There are also strong reasons for
understanding notions of proof even where their use in full formality is not
anticipated.
Pedagogically, experience with proof is the best route to a thorough under-
standing of many of the logical and data operators in a specification language.
Furthermore, attempting even outline proofs about specifications and designs
will inculcate good taste in the construction of specifications.
For practical purposes, perhaps the most important reason for studying
proof notions is that proof obligations can be used as mental checks during
reviews or inspections of stages of design even where formal proofs are not
xii
Cliff B Jones
Using this Book
This book contains enough material for three courses of study: a course on
mathematics for software engineering, a course on formal specification, and a
course on refinement. This material can be adapted in a number of ways, to
support other courses or as part of a programme of self-paced learning. To
make the book easier to use, we have divided it into six parts:
This book is based upon our experience in teaching and using formal methods
at the Programming Research Group, Oxford, and elsewhere. The courses that
we teach draw their inspiration from the work of others: Jean-Raymond Abrial,
Paul Gardiner, Ian Hayes, He Jifeng, Tony Hoare, Cliff Jones, Carroll Morgan,
Jeff Sanders, Ib Holm Sørensen, Mike Spivey, and Bernard Sufrin.
We are grateful to many others: John Axford, Rosalind Barden, Stephen
Brien, Neil Brock, Tim Clement, David Cooper, Will Harwood, Jonathan Hoare,
Fleur Howles, Michael Jackson, Roger Jones, Steve King, Peter Lupton, Andrew
Martin, Colin O’Halloran, Steve Schneider, Roger Shaw, Jane Sinclair, Susan
Stepney, Pete Verey, Geoff Winn, John Wordsworth, and Maureen York.
We are grateful also to the students who have attended our lectures over
the years: at the Programming Research Group; at Royal Holloway, University
of London; at the University of Reading; at the University of Klagenfurt; on
industrial courses and summer schools in Europe, North America, and Africa.
Their comments and suggestions are much appreciated.
The file system and save area case studies are based upon work carried out
by Ian Hayes and Ib Sørensen; the better examples of simulation were suggested
by Carroll Morgan and Paul Gardiner; the better examples of refinement owe
much to the work of Cliff Jones. The diagrams in Chapter 1 were supplied by
the London Transport Museum.
Jackie Harbor, Helen Martin, and Derek Moseley of Prentice Hall were
professional but sympathetic at every turn. The last two years would have
been less enjoyable if it had not been for their good humour, gentle tolerance,
and quiet efficiency.
We would also like to thank our families, without whose love and support
this book could not have been completed. This book is dedicated to them.
xvi
• Nortel;
• British Telecom;
• QinetiQ;
• Department of Computer Science, Reading;
• Department of Continuing Education, Oxford;
• Engineering and Physical Sciences Research Council;
• Formal Systems (Europe) Ltd;
• IBM UK Laboratories;
• Kellogg College, Oxford;
• Logica;
• Oxford University Computing Laboratory;
• Rutherford–Appleton Laboratory.
Thank you.
Chapter 1
Introduction
One way to improve the quality of software is to change the way in which soft-
ware is documented: at the design stage, during development, and after release.
Existing methods of documentation offer large amounts of text, pictures, and
diagrams, but these are often imprecise and ambiguous. Important informa-
tion is hidden amongst irrelevant detail, and design flaws are discovered too
late, making them expensive or impossible to correct.
2 1 / Introduction
CICS is one of the most successful pieces of software in the world: there are
over 30 000 licences, and most of the world’s top companies use it. CICS stands
for Customer Information Control System, a family of transaction processing
products produced by IBM UK Laboratories at Hursley Park. CICS provides
data access, communications, integrity, and security services. Put simply, CICS
manages information.
When we use an automated teller machine in San Francisco, an account at
our local branch in Oxford is debited, even though the machine is thousands
of miles away. During the busiest times, there may be many thousands of
customers of the bank using the service all over the world, and we all expect to
be served within a reasonable time. CICS offers a way of achieving this.
There have been regular releases of CICS since the mid-1970s. Each release
has introduced additional features and extended the structure of the existing
code. In the early 1980s, the complexity of the system started to become a
serious problem for the company. A decision was made to re-design some of
the CICS modules with the aim of making extensions easier. An important
part of the proposed solution involved finding a more precise way to specify
functionality.
1.3 / The Z notation 3
The Z notation is based upon set theory and mathematical logic. The set theory
used includes standard set operators, set comprehensions, Cartesian products,
and power sets. The mathematical logic is a first-order predicate calculus. To-
gether, they make up a mathematical language that is easy to learn and to apply.
However, this language is only one aspect of Z.
Another aspect is the way in which the mathematics can be structured.
Mathematical objects and their properties can be collected together in schemas:
patterns of declaration and constraint. The schema language can be used to
describe the state of a system, and the ways in which that state may change. It
can also be used to describe system properties, and to reason about possible
refinements of a design.
A characteristic feature of Z is the use of types. Every object in the math-
ematical language has a unique type, represented as a maximal set in the current
specification. As well as providing a useful link to programming practice, this
notion of types means that an algorithm can be written to check the type of
every object in a specification; several type-checking tools exist to support the
practical use of Z.
4 1 / Introduction
At the design stage, a proof can show us not only that a design is correct,
but also why it is correct. The additional insight that this affords can be in-
valuable: as requirements evolve and the design is modified, the consequences
are easier to investigate. At the implementation stage, a proof can help us to
ensure that a piece of code behaves according to the specification. Again, a
significant contribution to quality can be made.
The construction of proofs is an essential part of writing a specification,
just as proof-reading is an essential part of writing a book. A specification
without proofs is untested: it may be inconsistent; it may describe properties
that were not intended, or omit those that were; it may make inappropriate
assumptions. The practice of proof makes for better specifications.
It seems to be part of software engineering folklore that proof is im-
possible on an industrial scale; however, our experience has been different. We
have been involved in many large-scale applications of formal methods; some
involved proof, others did not. We have seen that techniques involving proof
are successful where formal methods are used with a light touch, and where
proofs are conducted at an appropriate level of formality.
In many situations, a rigorous argument, or a semi-formal justification,
will be sufficient to bring about the desired improvement in quality. In other,
more critical situations, it may be necessary to increase the level of formal-
ity until the correctness of the design is beyond doubt. In some situations, a
completely formal proof may be required. The trick of using formal methods
effectively is to know when proofs are worth doing and when they are not.
1.5 Abstraction
The Diagram was, and still is, a good specification of the London Under-
ground network. It is
• Abstract. Since it records only the logical layout, not the physical reality
in all its detail.
• Concise. Since it is printed on a single A5 sheet of card that is folded twice
so that it fits into the pocket.
• Complete. Since every station on the London Underground network is
represented.
• Unambiguous. Since the meaning of the symbols used is explained, and
the Diagram is expressed in simple geometrical terms. It is a precise and
accurate description of the Underground network.
• Cost-effective. Since it cost only five guineas to commission the specifica-
tion from the engineering draughtsman Harry Beck.
• Maintainable. Since it has been successfully maintained over the last 60
1.5 / Abstraction 7
years, reflecting the changes in the network as stations have opened and
closed, and new lines have been added.
• Comprehensible. Since it has been regarded fondly by its users from its
first issue in January 1933, the Diagram must be readily understood by
the general public.
The Diagram gives its users a good conceptual model; it is how Londoners and
visitors see the Underground network. It embodies a specification structure
that enables users to make sense out of a rather complex implementation. To
do this, it uses abstract shapes, colours, and compression. All lines have been
reduced to 90◦ or 45◦ angles. The various lines are coloured differently. The
central area, where there are more stations, is shown in greater detail than the
outlying parts, as if the Diagram were being viewed through a convex lens.
Furthermore, the Diagram may be used to predict the result of travelling
on the Underground network. We might observe that if we start at Oxford
Circus, travel eastbound on the Central Line and change trains at Tottenham
8 1 / Introduction
Court Road, then take the Northern Line, we may arrive at Mornington Crescent.
In mathematical terms, this property is a theorem of the system; in practical
terms, it describes a possible route.
The Diagram has served its purpose well; if only every specification were
as good as this. Interestingly, the first sketch of the Diagram was rejected by
the Publicity Department of the Underground. They thought that the idea of a
90◦ and 45◦ schematic treatment was too ‘revolutionary’. The abstract notation
was thought to be too strange and incomprehensible for the ordinary user of
the Underground network.
Chapter 2
Propositional Logic
Propositional logic deals with the statement of alleged facts which must be
either true or false, but not both.
• A tomato is a fruit.
• An orange is a fruit.
¬ negation not
∧ conjunction and
∨ disjunction or
⇒ implication implies
a equivalence if and only if
The table gives the connective’s symbol, its name, and how it is pronounced.
Using the notion of precedence, we can see that the proposition
¬p ∧ q ∨ r a q ⇒ p ∧ r
(((¬p) ∧ q) ∨ r ) a (q ⇒ (p ∧ r ))
Example 2.2
2.2 Conjunction
p q p∧q
t t t
t f f
f t f
f f f
In the first column we describe all the situations that we are interested in: all
the combinations of the possible truth values of p and q (abbreviating truth
and falsity to t and f ). In the second we have written down the truth value of
p ∧ q in each of these situations. Thus, p ∧ q is true just in the case that p is
true and q is true.
Now, suppose that we wanted to prove that p ∧ q is true: the truth table
tells us when that is so. If we follow the row that has the entry t for p ∧ q, we
see that we must prove that both p and q are true (have the entry t ). Thus, to
prove p ∧ q, we must prove both p and also q. Now, suppose that we know
that p ∧ q is true, then we certainly know that p must be true; we also know
that q must be true. We can summarise these observations with the following
rules of inference:
p q p∧q p∧q
[∧−intro] [∧−elim1] [∧−elim2]
p∧q p q
These inference rules form part of the natural deduction system that we use to
conduct our proofs.
Each inference rule is written in the following form:
premiss1 . . . premissn
[name]
conclusion side condition
The list of premisses is sometimes empty; the role of the side condition will
become clear later. The meaning of such a rule is that the truth of the conclusion
follows from the truth of the premisses: whenever the premisses are true, then
so is the conclusion.
The rules come in two flavours. For an operator op, the op-elimination
rule describes what may be deduced from p op q; and the op-introduction
rule describes under what conditions p op q can be concluded. Using these
rules to introduce and eliminate different operators, we can start from a set
of propositions, or hypotheses, and derive another proposition. If the set of
hypotheses is empty, then we call the derived proposition a theorem.
12 2 / Propositional Logic
p q p∧q q p q∧p
t t t t t t
t f f f t f
f t f t f f
f f f f f f
p q p∧q q∧p
t t t t
t f f f
f t f f
f f f f
Notice that the columns for p ∧ q and q ∧ p are identical: in every situation
they take the same truth value; thus, we can conclude that they are the same
proposition, and so conjunction is commutative.
p∧q
q∧p
We prove this derived rule by exhibiting a proof tree, built from rules fitted
together jigsaw-wise. The leaves (at the top of the tree) are instances of the
premisses, and the root (at the bottom of the tree) is the conclusion.
p∧q p∧q
[∧−elim2] [∧−elim1]
q p
[∧−intro]
q∧p
2.3 / Disjunction 13
In this tree, there are three rules which have been used. Where the conclu-
sion of one rule forms the premiss of the next, they match exactly. The tree
matches the rule that we were trying to prove, since, if we take all the leaves
and remove duplicates, we end up with the premiss of the rule; the root of the
tree corresponds to the conclusion.
One more piece of terminology and its notation. Some premisses are in-
troduced during a proof: they are called assumptions. An assumption must be
discharged during a proof, and there are certain rules (discharge rules) which
do this. The assumption p is denoted by dpe[] . In the next section we see
examples of assumptions and discharge rules.
2.3 Disjunction
p q p∨q
t t t
t f t
f t t
f f f
This is inclusive or: the disjunction is true in any situation in which one of the
disjuncts is true, including the situation in which both are true. The disjunction
p ∨ q is true if and only if either p is true or q is true. Our three rules are:
dpe[i] dqe[i]
p q p∨q r r
[∨−intro1] [∨−intro2] [∨−elim[i] ]
p∨q p∨q r
dpe[i]
r
by a step labelled i in the proof tree. It is discharged from the proof of r when
the rule is applied: given a proof of p ∨ q, a proof of r from the assumption p,
and a proof of r from the assumption q, the rule concludes r .
p q p∨q q∨p
t t t t
t f t t
f t t t
f f f f
p∨q
q∨p
dpe[1] dqe[1]
[∨−intro2] [∨−intro1]
p∨q q∨p q∨p [1]
[∨−elim ]
q∨p
2.4 Implication
p q p⇒q
t t t
t f f
f t t
f f t
Thus, the implication is true unless the antecedent is true and the consequent
is false.
2.4 / Implication 15
dpe[i]
q p⇒q p
[⇒−intro[i] ] [⇒−elim]
p⇒q q
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
p q r (p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
t t t t t t t t
t t f t f t f f
t f t f t t t t
t f f f t t t t
f t t f t t t t
f t f f t t t f
f f t f t t t t
f f f f t t t t
Every entry in the column underneath the major connective in the proposition
is a t : thus the proposition is true in every situation.
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
To see how this might be established, consider the incomplete proof tree:
..
.
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
16 2 / Propositional Logic
The major connective is an implication; we could consider how it got there, and
try to introduce it:
dp ∧ q ⇒ r e[1]
..
.
(p ⇒ (q ⇒ r ))
[⇒−intro[1] ]
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
dp ∧ q ⇒ r e[1]
dpe[2]
..
.
q⇒r
[⇒−intro[2] ]
(p ⇒ (q ⇒ r ))
[⇒−intro[1] ]
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
Again the goal is an implication. Using the introduction rule a third time leaves
us in the following situation:
dp ∧ q ⇒ r e[1]
dpe[2]
dqe[3]
..
.
r
[⇒−intro[3] ]
q⇒r
[⇒−intro[2] ]
(p ⇒ (q ⇒ r ))
[⇒−intro[1] ]
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
At this stage, the structure of the goal suggests nothing: there is no struc-
ture. Now is the time to start working forwards from the assumptions: one of
them has an implication, so we should try to eliminate that:
2.5 / Equivalence 17
dp ∧ q ⇒ r e[1]
dpe[2]
dqe[3]
..
.
dp ∧ q ⇒ r e[1] p∧q
[⇒−elim]
r
[⇒−intro[3] ]
q⇒r
[⇒−intro[2] ]
(p ⇒ (q ⇒ r ))
[⇒−intro[1] ]
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
It is clear now how to finish this proof: the assumptions p and q can be con-
joined to discharge the conjunction p ∧ q:
dpe[2] dqe[3]
[∧−intro]
dp ∧ q ⇒ r e[1] p∧q
[⇒−elim]
r [3]
[⇒−intro ]
q⇒r
[⇒−intro[2] ]
(p ⇒ (q ⇒ r ))
[⇒−intro[1] ]
(p ∧ q ⇒ r ) ⇒ (p ⇒ (q ⇒ r ))
2.5 Equivalence
The equivalence p a q means that p and q are of the same strength; thus it
might also be called bi-implication: p a q means that both p ⇒ q and q ⇒ p.
Since p and q have the same strength, they must therefore have the same entries
in the truth table:
p q paq
t t t
t f f
f t f
f f t
The rules for introducing and eliminating the equivalence connective follow
from the observation that p a q is equivalent to p ⇒ q and q ⇒ p.
Example 2.9 If p is stronger than q, then p ∧ q and p have the same strength:
p⇒q
p∧qap
To show that this is a derived rule of our system, consider the goal
..
.
p∧qap
.. ..
. .
p∧q⇒p p⇒p∧q
[a−intro]
p∧qap
In the left-hand subtree, the major connective is now an implication, so let’s try
to introduce that:
dp ∧ qe[1]
..
.
..
p .
[⇒−intro[1] ] [⇒−intro]
p∧q⇒p p⇒p∧q
[a−intro]
p∧qap
dpe[2]
..
dp ∧ qe[1] .
[∧−elim1]
p p∧q
[⇒−intro[1] ] [⇒−intro[2] ]
p∧q⇒p p⇒p∧q
[a−intro]
p∧qap
2.5 / Equivalence 19
dpe[2] dpe[2]
.. ..
. .
dp ∧ qe[1] p q
[∧−elim1] [∧−intro]
p p∧q
[⇒−intro[1] ] [⇒−intro[2] ]
p∧q⇒p p⇒p∧q
[a−intro]
p∧qap
The left-most unfinished subtree can be closed easily, since we have to prove
p from the assumption p: that is immediate. The right-most one cannot be
pushed further backwards, since there is no structure to exploit; instead, we
work from our premiss:
dpe[2]
..
.
p⇒q p
[1] [2] [⇒−elim]
dp ∧ qe dpe q
[∧−elim1] [∧−intro]
p p∧q
[⇒−intro[1] ] [⇒−intro[2] ]
p∧q⇒p p⇒p∧q
[a−intro]
p∧qap
Again, the closing of this subtree is trivial, thus completing the proof:
p⇒q dpe[2]
[1] [2] [⇒−elim]
dp ∧ qe dpe q
[∧−elim1] [∧−intro]
p [1]
p∧q
[⇒−intro ] [⇒−intro[2] ]
p∧q⇒p p⇒p∧q
[a−intro]
p∧qap
A derived rule may be used in the same way as any other inference rule;
the above example gives us
p⇒q
[subsume]
p∧qap
This is just one of several similar inference rules involving conjunction, dis-
junction, and implication.
20 2 / Propositional Logic
2.6 Negation
The negation ¬p is true if and only if p is false. The truth table is simple:
p ¬p
t f
f t
Our rules for negation make use of a special proposition called false, which
stands for a contradiction: it is false in every situation. If ¬p is true, then p is
false; and if p is true, then ¬p is false. Notice that it is not possible for ¬p and
p both to be true. This gives us three rules:
dpe[i] d¬pe[j]
false p ¬p false
[¬−intro[i] ] [¬−elim] [false −elim[j] ]
¬p false p
Our system requires three rules to deal with negation. At first sight, it might
seem that the two rules that we have called ¬−intro and false −elim would be
sufficient, but they would give us no way of concluding false.
Example 2.10 One of de Morgan’s Laws states that the negation of a disjunc-
tion is the conjunction of negations:
¬(p ∨ q)
[de Morgan1]
¬p ∧ ¬q
..
.
¬p ∧ ¬q
.. ..
. .
¬p ¬q
[∧−intro]
¬p ∧ ¬q
2.6 / Negation 21
Let’s focus on the left subtree. In order to prove the negation ¬p, we
should assume p and then force a contradiction:
dpe[1]
..
.
[¬−elim] ..
false .
[¬−intro[1] ]
¬p ¬q
[∧−intro]
¬p ∧ ¬q
Now, what should the contradiction be? We have the premiss ¬(p ∨ q), we
could try to contradict that:
dpe[1]
..
.
p∨q ¬(p ∨ q)
[¬−elim] ..
false .
[¬−intro[1] ]
¬p ¬q
[∧−intro]
¬p ∧ ¬q
dpe[1]
[∨−intro1]
p∨q ¬(p ∨ q)
[¬−elim] ..
false .
[¬−intro[1] ]
¬p ¬q
[∧−intro]
¬p ∧ ¬q
dpe[1] dqe[2]
[∨−intro1] [∨−intro2]
p∨q ¬(p ∨ q) p∨q ¬(p ∨ q)
[¬−elim] [¬−elim]
false [1]
false [2]
[¬−intro ] [¬−intro ]
¬p ¬q
[∧−intro]
¬p ∧ ¬q
Example 2.11 The Law of the Excluded Middle states that either a proposition
is true or it is false. That is,
p ∨ ¬p
..
.
p
[∨−intro1]
p ∨ ¬p
Now there is no structure to analyse, so we are lost, since there are no as-
sumptions or premisses either. We must admit defeat and backtrack to the last
choice that we made. Perhaps we should have thrown away p instead:
..
.
¬p
[∨−intro2]
p ∨ ¬p
Now we are in the same position: we still cannot complete the proof. Again, we
must backtrack to the last decision point. This time, we go right back to the
start of the proof. We have tried both varieties of disjunction introduction, and
there are neither assumptions nor premisses to work from. What now? One
possible way forward is to try to contradict our goal: assume ¬(p ∨ ¬p) and
force a contradiction:
d¬(p ∨ ¬p)e[1]
..
.
false
[false −elim[1] ]
p ∨ ¬p
d¬(p ∨ ¬p)e[1]
[de Morgan1]
¬p ∧ ¬¬p
..
.
[¬−elim]
false
[false −elim[1] ]
p ∨ ¬p
2.7 / Tautologies and contradictions 23
The proof tree isn’t quite closed, because of the technicality that we need two
separate propositions: ¬p and ¬¬p. We can get each from the conjunction, so
we need to duplicate our work from the assumption, and use both varieties of
conjunction elimination:
The last example shows an important part of the proof process: the explora-
tion of possibilities. When we look at a proof, we see only a completed chain of
reasoning; we do not see the other attempts that may have been made. Further-
more, rules like negation elimination give us a problem in a backwards proof,
since p appears in the premiss, but not in the conclusion. Thus, when we match
the consequent to our current goal, we still have to find an instantiation of p.
p ∨ ¬p
p⇒p
p ⇒ (q ⇒ p)
p ∧ ¬p
p a ¬p
¬(p ⇒ (q ⇒ p))
24 2 / Propositional Logic
p q ¬p ∨ q a p ⇒ q
t t f t t t
t f f f t f
f t t t t t
f f t t t t
b a
[a a b] [a a b]
a b
..
.
a
[a a b] ..
b .
..
.
A logical equivalence may be used to justify rewriting even when the proposition
involved is only part of the goal or assumption:
..
.
(¬ p ∨ q) ⇒ r
[(¬a ∨ b) a (a ⇒ b)] ..
(p ⇒ q) ⇒ r .
..
.
2.7 / Tautologies and contradictions 25
a
b
¬(p ∧ q) ⇒ ¬p ∨ ¬q
¬(p ∧ q)
[de Morgan2]
¬p ∨ ¬q
(p ∧ q) ⇒ (r ∨ s)
[(a ∧ b) ⇒ a]
p ⇒ (r ∨ s)
Predicate Logic
In this chapter we introduce another part of our logical language. The language
of propositions introduced in the previous chapter allows us to make state-
ments about specific objects, but it does not allow us to make statements such
as ‘Every cloud has a silver lining’. These are known as universal statements,
since they describe properties that must be satisfied by every object in some
universe of discourse.
• Jim doesn’t know anybody who can sign his bail application.
Sometimes we wish to state that at least one thing has a particular property,
without necessarily knowing which thing it is. This leads to an existential state-
ment.
28 3 / Predicate Logic
A predicate is a statement with a place for an object. There may be many such
places within a single predicate; this is often the case when the objects con-
cerned are mathematical. When these places are filled, our predicates become
statements about the objects that fill them. We could say that a predicate is a
proposition with a gap for an object of some kind.
For example, the statement ‘ > 5’ is a predicate. As it stands, it is not a
proposition; we cannot say whether it is true or false until we have filled the
empty place. We could turn it into a proposition by putting 0 in this place; the
result would be ‘0 > 5’, a proposition that happens to be false. This is not the
only way to fill a gap, however. We could also choose to put an object variable
in the empty place above.
The predicate ‘x > 5’ is still not a proposition; we cannot say whether
it is true or false without knowing what x is. The use of object variables is a
powerful technique, and holds the key to expressing the universal and exist-
ential properties described above. We can make a proposition out of ‘x > 5’
by adding a quantifier to the front of the expression. For example, we could
state that ‘there is an x, which is a natural number, such that x > 5’. Here,
the quantifier is ‘there is an…’, and we have quantified the predicate ‘x > 5’ to
produce a true proposition.
In mathematics, the symbol ‘∃’ is used to denote the expression ‘there is
an …’; in Z, the natural numbers are denoted by the symbol ‘N’. Thus, we can
write down our quantified predicate in Z as:
∃ x : N • x > 5.
To see that the quantified predicate is true, consider the number 6: it is a natural
number, and it is greater than 5.
Existential quantification may be thought of as a generalised form of dis-
junction: for example,
∃x : N • x > 5
a
0 > 5 ∨ 1 > 5 ∨ 2 > 5 ∨ 3 > 5 ∨ ...
3.1 / Predicate calculus 29
• Let Friends stand for the set of all your friends, and let x told y mean that
x has told y.
∃ f : Friends • f told me
• Let MadDog stand for the set of all mad dogs, and let x bit y mean that x
has bitten y.
• Let Person stand for the set of all people, and let PL(x) mean that x prefers
logic.
∃ p : Person • PL(p)
∀ x : N • x > 5.
∀ y : N • y > 5.
This predicate is false because not every natural number x is greater than 5:
consider 3.
The universal quantifier may be thought of as a generalised conjunction:
for example,
∀x : N • x > 5
a
0 > 5 ∧ 1 > 5 ∧ 2 > 5 ∧ 3 > 5 ∧ ...
30 3 / Predicate Logic
The predicate > 5 would have to be true of every natural number; of 0 and
of 1 and of 2 and of 3, etcetera. It is not true of 0, for example, and thus the
whole quantified expression is false.
• Let Student stand for the set of all students, and let Submit (x) mean that
x must hand in course work.
• Let Person be the set of all people, and let knows trouble(x) mean that x
knows the trouble I seen.
• Again, let Person be the set of all people. Let x Knows y means that x
knows y, and let x CanBail y mean that x can sign y’s application for bail.
In the Z notation, the two kinds of quantified expressions have a similar syntax:
Qx : a | p • q
where
• Q is the quantifier ;
• a is the range of x;
• q is the predicate.
The optional constraint p restricts the set of objects under consideration; only
those objects in a that satisfy p are to be considered. The constraint takes
3.2 / Quantifiers and declarations 31
(∃ x : a | p • q) a (∃ x : a • p ∧ q)
(∀ x : a | p • q) a (∀ x : a • p ⇒ q)
∃x : a | p • q
∀x : a | p • q
(∀ x : a | p • q ∧ r ) ∨ s ⇒ t
| {z }
scope of x
If a statement contains more than one quantifier, the scopes may overlap. This
poses no problems unless the same name is chosen for two variables bound by
different quantifiers; in this case, there would be a hole in one of the scopes.
Example 3.6 In the following expression, the scope of the first bound variable
has a hole corresponding to the scope of the second:
scope of second y
z }| {
∀y : a | p • q ∧ ( ∀y : b | r • s ⇒ t ) ∧ u ∨ v
| {z } | {z }
scope of first y scope of first y
32 3 / Predicate Logic
As the above example shows, there is scope for confusion whenever two differ-
ent variables have the same name.
Whenever such confusion can arise, we will choose another name for one
of the variables. We can change the name of a bound variable without changing
the meaning of the quantified expression, as long as we avoid the names of any
other variables that appear.
Example 3.7 Consider the following quantified expression, which states that
every natural number x is greater than or equal to zero:
∀ num : N • num ≥ 0
The choice of ‘num’ as the variable name is not important; the following ex-
pression has the same meaning:
∀ nat : N • nat ≥ 0
We must take care that the new name chosen for a bound variable has not
already been used for a different variable in the same expression.
Example 3.8 Consider the following quantified expression, which states that
there is some natural number max such that every natural number num must
be less than or equal to max:
∃x : a • ∃y : b • p
∃ x : a; y : b • p
There is a circumstance in which this is not possible: when the first quantified
variable appears in the range of the second.
Example 3.10 In the expression below, the first bound variable is used as the
range of the second:
∃a : b • ∃c : a • p
Example 3.11 In the expression below, there is a single free occurrence of vari-
able z:
∀x : N • z ≤ x
This predicate states that every natural number x is greater than z, whatever z
may be.
If we use the same name for two different variables, then we may find that a
variable appears both free and bound in the same expression.
Example 3.12 There are both free and bound occurrences of variable x in the
expression below:
x = 3 ∧ ∀ x : N • 0 ≤ |{z}
|{z} x
free occurrence bound occurrence
3.3 Substitution
Example 3.13
1. (x ≤ y + 2)[0 / x] a (0 ≤ y + 2)
2. (∃ x : N • x ≤ y + 2)[0 / x] a (∃ x : N • x ≤ y + 2)
3. (∃ x : N • x ≤ y + 2)[5 / y] a (∃ x : N • x ≤ 5 + 2)
We write p[t / x][u / y] to denote the predicate p[t / x] with the expression u
systematically substituted for free occurrences of the variable y.
Example 3.14
Example 3.15
1. (x ≤ y + 2)[y, 5 / x, y] a (y ≤ 5 + 2)
3.3 / Substitution 35
Example 3.16 Let Person denote the set of all people, and let m LooksLike n
mean that person m looks like person n. The following predicate is a statement
about a person o; it states that there is some person who does not look like o:
(∀ x : a | p • q)[t / x] a (∀ x : a[t / x] | p • q)
(∃ x : a | p • q)[t / x] a (∃ x : a[t / x] | p • q)
In this case, the only part of the expression that may change is the range of the
quantified variable. In general, this substitution will have no effect; it is poor
practice to include a free variable in the declaration of a bound variable of the
same name.
If the quantifier is binding some variable other than x, then the substitu-
tion will have more of an effect. If y is not free in t , then
(∀ y : a | p • q)[t / x]
a (∀ z : a[t / x] | p[z / y][t / x] • q[z / y][t / x])
(∃ y : a | p • q)[t / x]
a (∃ z : a[t / x] | p[z / y][t / x] • q[z / y][t / x])
By using z instead of y for the name of the quantified variable, we have avoided
any possibility of unintentional variable capture.
If the major operator in an expression is not a quantifier, then the effect
of substitution is easy to explain:
(¬p)[t / x] a ¬p[t / x]
(p ∧ q)[t / x] a p[t / x] ∧ q[t / x]
(p ∨ q)[t / x] a p[t / x] ∨ q[t / x]
(p ⇒ q)[t / x] a p[t / x] ⇒ q[t / x]
(p a q)[t / x] a p[t / x] a q[t / x]
dx ∈ ae[i]
q provided that x is not free
[[i] ]
∀x : a • q in the assumptions of q
dx ∈ ae[1]
dpe[1]
q provided that x is not free
[∀−intro[1] ]
∀x : a | p • q in the assumptions of q
dx ∈ ae[1]
dpe[2]
..
.
q
[⇒−intro[2] ]
p⇒q
[∀−intro[1] ]
∀x : a • p ⇒ q
[defn]
∀x : a | p • q
t ∈a ∀x : a • p
p[t / x]
t ∈a ∀x : a | p • q p[t / x]
[∀−elim]
q[t / x]
x∈a ∀x : a | p • q p
[∀−elim]
q
(∀ x : a • p ∧ q) ⇒ ((∀ x : a • p) ∧ (∀ x : a • q))
..
.
(∀ x : a • p ∧ q) ⇒ ((∀ x : a • p) ∧ (∀ x : a • q))
The major connective here is the implication, so we assume the antecedent and
try to prove the consequent:
d∀ x : a • p ∧ qe[1]
..
.
(∀ x : a • p) ∧ (∀ x : a • q)
[⇒−intro[1] ]
(∀ x : a • p ∧ q) ⇒ ((∀ x : a • p) ∧ (∀ x : a • q))
d∀ x : a • p ∧ qe[1] d∀ x : a • p ∧ qe[1]
.. ..
. .
∀x : a • p ∀x : a • q
[∧−intro]
(∀ x : a • p) ∧ (∀ x : a • q)
[⇒−intro[1] ]
(∀ x : a • p ∧ q) ⇒ ((∀ x : a • p) ∧ (∀ x : a • q))
We shall deal with the left-hand subtree. The major connective is the universal
3.4 / Universal introduction and elimination 39
d∀ x : a • p ∧ qe[1]
dx ∈ ae[2]
.. d∀ x : a • p ∧ qe[1]
.
..
p .
[∀−intro[2] ]
∀x : a • p ∀x : a • q
[∧−intro]
(∀ x : a • p) ∧ (∀ x : a • q)
[⇒−intro[1] ]
(∀ x : a • p ∧ q) ⇒ ((∀ x : a • p) ∧ (∀ x : a • q))
dx ∈ ae[2] d∀ x : a • p ∧ qe[1]
[∀−elim]
p∧q
.. d∀ x : a • p ∧ qe[1]
.
..
p .
[∀−intro[2] ]
∀x : a • p ∀x : a • q
[∧−intro]
(∀ x : a • p) ∧ (∀ x : a • q)
[⇒−intro[1] ]
(∀ x : a • p ∧ q) ⇒ ((∀ x : a • p) ∧ (∀ x : a • q))
Now this subtree is finished, since we can use conjunction elimination to con-
nect top and bottom. The right-hand subtree is symmetric with the left.
Example 3.18 Provided that x does not occur free in the antecedent, then we
can move the universal quantifier through an implication
dp ⇒ ∀ x : a • qe[4] dpe[6]
[5] [⇒−elim]
dx ∈ ae ∀x : a • q
[∀−elim]
q
[⇒−intro[6] ]
p⇒q
[∀−intro[5] ]
∀x : a • p ⇒ q
[⇒−intro[4] ]
(p ⇒ ∀ x : a • q) ⇒ (∀ x : a • p ⇒ q)
dx ∈ ae[3] d∀ x : a • p ⇒ qe[1]
[∀−elim]
p⇒q dpe[2]
[⇒−elim]
q [3]
[∀−intro ]
∀x : a • q
[⇒−intro[2] ]
p ⇒ ∀x : a • q
[⇒−intro[1] ]
(∀ x : a • p ⇒ q) ⇒ (p ⇒ ∀ x : a • q)
[a−intro]
(∀ x : a • p ⇒ q) a (p ⇒ ∀ x : a • q)
p[t / x] ∧ q[t / x]
That is, given that we are talking about t not x, both the constraint and the
quantified predicate should hold.
To introduce an existential quantifier, we must show that a suitable ex-
pression t exists: we must provide an example.
t ∈a p[t / x] q[t / x]
[∃−intro]
∃x : a | p • q
Example 3.19 With suitable assumptions about N, +, and >, we can prove that
for any natural number x, there is some natural number y such that y is greater
3.5 / Existential introduction and elimination 41
dx ∈ Ne[1]
[arithmetic]
dx ∈ Ne[1] x <x +1
[arithmetic] [subst]
x +1∈N (x < y)[x + 1 / y]
[∃−intro]
∃y : N • x < y [1]
[∀−intro ]
∀x : N • ∃y : N • x < y
Two of the steps in this proof cannot be made using the rules of our natural
deduction system. The validity of these steps depends upon our understanding
of the natural numbers N, and a conventional interpretation of > and +.
x∈a p q
[∃−intro]
∃x : a | p • q
Example 3.20 If, in the course of a proof, we have established that x ∈ N and
x ≥ 0, then we may apply the special case of existential-introduction and con-
clude that
∃x : N • x ≥ 0
dx ∈ a ∧ pe[i]
∃x : a • p r provided x is not free in the
[∃−elim[i] ]
r assumptions, and x is not free in r
42 3 / Predicate Logic
dx ∈ a ∧ pe[i]
dx ∈ a ∧ p ∧ qe[i]
provided x is not free
∃x : a | p • q r in the assumptions, and x is
[∃−elim[i] ]
r not free in r
These rules are generalisations of the case analysis rule given in Chapter 2 for
the elimination of the ∨ operator. For each value of x, we must show that r
follows from p and q.
Example 3.21 Existential quantifiers commute. We will prove this in one dir-
ection only:
(∃ x : a • ∃ y : b • p) ⇒ (∃ y : b • ∃ x : a • p)
d∃ x : a • ∃ y : b • pe[1]
dx ∈ ae[2] dpe[3]
[3] [∃−intro]
dy ∈ be ∃x : a • p
[∃−intro]
d∃ y : b • pe[2] ∃y : b • ∃x : a • p
[∃−elim[3] ]
∃y : b • ∃x : a • p
[∃−elim[2] ]
∃y : b • ∃x : a • p
[⇒−intro[1] ]
(∃ x : a • ∃ y : b • p) ⇒ (∃ y : b • ∃ x : a • p)
The two quantifiers are related in the same way as the propositional op-
erators they generalise. The statement ‘for some x in a, predicate p is true’ is
the negation of ‘for every x in a, predicate p is false’. In terms of equivalences:
∃ x : a • p a ¬ ∀ x : a • ¬p
∀ y : b • q a ¬ ∃ y : b • ¬q
These two equivalences are generalisations of the de Morgan laws for the ∧ and
∨ operators given in Chapter 2.
3.6 / Satisfaction and validity 43
A predicate with free variables or ‘spaces’ is neither true nor false; it cannot be
assigned a truth value until values are chosen for these variables or the spaces
are filled. Some predicates will become true whatever values are chosen: these
are said to be valid predicates.
n≥0
A predicate that is true for some, but not necessarily all, choices of values is
said to be satisfiable.
n≥5
A predicate that is false for all choices is said to be unsatisfiable. Valid, satis-
fiable, and unsatisfiable predicates are the analogues of tautologies, contingen-
cies, and contradictions in the language of propositions.
Chapter 4
4.1 Equality
Example 4.1 In an identity parade, a witness may state that ‘the man on the
right is the man who stole my idea’, making the following identification:
That is, the man on the right is identical to the man who stole the idea.
The ‘=’ symbol is due to Robert Recorde, whose textbook on algebra The Whet-
stone of Witte, published in 1557, used the symbol for the first time. Recorde
argued that a pair of parallel lines of the same length were suitable as the sym-
bol for equality ‘bicause noe 2 thynges can be moare equalle’.
We do not use equality to state that two predicates are identical: the pro-
positional connective of equivalence is reserved for that purpose. Rather, we
use equality to state that two values (such as numbers) are identical. Thus,
we may write 5 + 3 = 3 + 5, since both sides of the equation are expressions
which denote values. These denoted values are the same, so the equality is true.
Equalities form the atomic propositions in our logical language; the only other
way of obtaining an atomic proposition is through set membership, described
in Chapter 5.
Everything is identical to itself: thus, if t is any expression, then t is equal
to t . This principle is known as the law of reflection:
[eq-ref]
t =t
It should be remarked that there are logics in which this principle does not
hold. It is, however, an axiom of standard Z.
1+1 = 1+1
s = t p[t / x]
[eq-sub]
p[s / x]
Example 4.3 If we know that Christmas Day = 25th December , and that
If two expressions e and f are not identical, then we write e ≠ f . This is simply
an abbreviation for ¬(e = f ).
p[s / x] ¬p[t / x]
s≠t
¬p[t / x]
[1] [subst]
ds = t e (¬p)[t / x]
[eq-sub]
(¬p)[s / x]
[subst]
p[s / x] ¬p[s / x]
[¬−elim]
false
[¬−intro[1] ]
¬(s = t )
[abbreviation]
s≠t
Using the rules [eq-ref] and [eq-sub], we are able to establish that equality
is symmetric: for any expressions s and t , if s = t , then t = s. If we let x be a
fresh variable, so that x does not appear in either s or t , then we may construct
the following derivation:
[eq-ref]
t =t
[subst]
s = t (t = x)[t / x]
[eq-sub]
(t = x)[s / x]
[subst]
t =s
Having derived this property, we may use it as a rule of inference in our natural
deduction system; we will refer to it as [eq-symm].
48 4 / Equality and Definite Description
Example 4.5 From the identification made in Example 4.1, we may apply the
[eq-symm] rule and conclude that:
We are also able to establish that equality is transitive: for any expressions s, t
and u, if s = t and t = u then s = u. Again, let x be a fresh variable:
t =u
[subst]
s = t (x = u)[t / x]
[eq-sub]
(x = u)[s / x]
[subst]
s=u
Example 4.6 After the identity parade, it is revealed that the man on the right
is Professor Plum, the prominent plagiarist:
We may add this to the information in Example 4.5 and conclude that
∃x : a • p ∧ x = t
(∃ x : a • p ∧ x = t ) a t ∈ a ∧ p[t / x]
4.2 / The one-point rule 49
For this to work, x must not be free in t . If it were, then x would be bound on
the left-hand side of the equivalence but free on the right. In this case, if we
were to replace the left-hand side of the equivalence by the right-hand side, x
would suddenly become a free variable.
The right-to-left implication is rather obvious: it relies on existential in-
troduction, and the equality suggests what the term should be:
dt ∈ a ∧ p[t / x]e[1]
[∧−elim2] [eq-ref]
p[t / x] t =t
[∧−intro]
dt ∈ a ∧ p[t / x]e[1] p[t / x] ∧ t = t
[∧−elim1] [subst]
t ∈a (p ∧ x = t )[t / x]
[∃−intro]
∃x : a • p ∧ x = t
[⇒−intro[1] ]
t ∈ a ∧ p[t / x] ⇒ (∃ x : a • p ∧ x = t )
The left-to-right direction is more interesting, since it relies on the use of exist-
ential elimination. Notice that the use of this rule is sound, due to the proviso
that x is not free in t .
d∃ x : a • p ∧ x = t e[1]
[∃−elim[2] ]
t ∈ a ∧ p[t / x]
[⇒−intro[1] ]
(∃ x : a • p ∧ x = t ) ⇒ t ∈ a ∧ p[t / x]
∃x : a • p ∧ x = t
[one-point]
t ∈ a ∧ p[t / x] provided that x is not free in t
∃n : N • 4 + n = 6 ∧ n = 2
50 4 / Equality and Definite Description
is equivalent, by the one-point rule, and since n does not appear free in the
expression ‘2’, to the proposition
2∈N∧4+2=6
∃ n : N • 6 + n = 4 ∧ n = −2
is equivalent, by the one-point rule, and since n does not appear free in the
expression ‘−2’, to the proposition
−2 ∈ N ∧ 6 − 2 = 4
∃ n : N • (∀ m : N • n > m) ∧ n = n + 1
cannot be simplified using the one-point rule, since n is free in the expression
‘n + 1’.
Equality can be used to make our predicate calculus more expressive, by allow-
ing us to identify and distinguish objects.
Example 4.8 Let x loves y mean that x is in love with y, and let Person be the
set of all people. We may symbolise the proposition ‘only Romeo loves Juliet’
using a conjunction:
Example 4.9 The statement ‘there is at most one person with whom Romeo is
in love’ may be formalised as
That is, if p and q are two people that Romeo loves, then they must be the same
person.
4.3 / Uniqueness and quantity 51
Example 4.10 The statement ‘no more than two visitors are permitted’ can be
formalised as
∀ p, q, r : Visitors • p = q ∨ q = r ∨ r = p
The notion of ‘at least one’ can be formalised using the existential quantifier.
Example 4.11 The statement ‘at least one person has applied’ could be form-
alised as
∃ p : Person • p ∈ Applicants
∃ p, q : Applicants • p ≠ q
With the notions of ‘at least’ and ‘at most’, we have a method for formalising
definite numerical propositions. This will be made easier later in the book when
we have introduced finite sets. Then we shall be able to say, for example, that
there are 29 distinct things with property p.
Example 4.13 The statement ‘there is exactly one book on my desk’ may be
formalised as
where ‘Book’ denotes the set of all books, and ‘x ∈ Desk’ means that ‘x is on my
desk’.
Specifying that there is exactly one object with a given property occurs so often
that there is a special notation for it: the unique quantifier. We write
∃1 x : a • p
when there exists exactly one element x of set a such that p holds. This new
quantifier can be defined in terms of the two forms of quantifier introduced in
52 4 / Equality and Definite Description
Chapter 3:
∃1 x : a | p • q a ∃ x : a | p •
q ∧ ∀ y : a • p[y / x] ∧ q[y / x] ⇒ y = x
Example 4.14 Each of the following phrases indicates that there is a unique
object with a certain property:
In our mathematical language, there is a special notation for this definite de-
scription of objects: the µ-notation. We write
(µx : a | p )
4.4 / Definite description 53
Example 4.16 The statement that Marie Curie is the person who discovered
radium could be formalised as
If it can be shown that there is exactly one element of Person with this property,
we may infer that
Such a statement makes sense only if there exists a unique object with the
specified property. This requirement is reflected in the proof rules for the µ
operator:
and
If there is a unique x from a such that p holds, and t is such an object, then we
may infer that t is equal to the µ-expression ( µ x : a | p ). Conversely, if t is
equal to this expression and uniqueness is guaranteed, then we may infer that
t is an element of a such that p holds.
Example 4.17 The following proposition states that 2 is the natural number
that yields a result of 6 when added to 4:
2 = (µn : N | 4 + n = 6)
It may be proved by
[arithmetic] [arithmetic]
∃1 n : N • 4 + n = 6 2∈N∧4+2=6
[defdesc]
2 = (µn : N | 4 + n = 6)
In both left and right subtrees, the required result follows from the properties
of N and +.
54 4 / Equality and Definite Description
To prove that an object is not equal to a given µ-expression we must show that
the µ-expression denotes a unique object, and that this isn’t it.
Example 4.18 The following proposition states that 3 is not the natural number
that yields a result of 6 when added to 4:
3 ≠ (µn : N | 4 + n = 6)
It may be proved by
[arithmetic]
∃1 n : N • 4 + n = 6 d3 = ( µ n : N | 4 + n = 6 )e[1]
[µ-elim]
∃n : N | 4 + n = 6 • 3 = n
[one-point]
3∈N∧4+3=6
[∧−elim2]
4+3=6
[arithmetic]
false
[¬−intro[1] ]
3 ≠ (µn : N | 4 + n = 6)
If there is no unique object with the specified property, then our attempts at
proving equality may fail.
Example 4.19 The following proposition states that 1 is the unique natural
number that satisfies n = n + 0:
1 = (µn : N | n = n + 0)
∃1 n : N • n = n + 0 1 ∈ N ∧ 1 = 1 + 0
[µ-intro]
1 = (µn : N | n = n + 0)
The right-hand subtree seems quite easy to prove, but the left-hand one is im-
possible. At this point, we might decide that our proposition is false, and at-
tempt to prove its negation.
∃1 n : N • n = n + 0d1 = ( µ n : N | n = n + 0 )e[1]
[µ-elim]
..
.
false
[¬−intro[1] ]
1 ≠ (µn : N | n = n + 0)
4.4 / Definite description 55
This proof is also problematic: we must derive a contradiction, and the only
assumption that we have is that
1 = (µn : N | n = n + 0)
∃1 n : N • n = n + 0
which is not true, and even if we could, it would be no use, since it does not
lead to a contradiction. In conclusion, we cannot prove that
1 = (µn : N | n = n + 0)
nor can we prove its negation. In this respect, our proof system is incomplete,
and deliberately so.
There is no unique number n which has the property that n = n + 0, there
are many; we refer to the descriptive phrase
(µn : N | n = n + 0)
as being improper. We do not know what value it has, and we can prove very
little about it.
Example 4.20 The following proposition states that 1 is the unique natural
number which is equal to its own successor:
1 = (µn : N | n = n + 1)
No natural number has this property, and our attempts to prove this proposi-
tion or its negation will fail.
Sometimes we wish to refer not to the unique object with some property,
but to some object or expression associated with it. A more general form of
µ-expression allows this: we write
(µx : a | p • e)
Example 4.21 The date upon which University College, Oxford, was founded
might be given by the expression:
56 4 / Equality and Definite Description
The proof rules for this form of definite description are simple generalisations
of those given above:
and
Notice that since the object x is unique, so too is the value of the expression e.
Chapter 5
Sets
s == {a, b, c}
Example 5.2 The first two sets in Example 5.1 can be defined by extension:
x 6∈ s a ¬(x ∈ s)
5.1 / Membership and extension 59
Example 5.3 If Primes denotes the set of all prime numbers, then the following
propositions are true:
3 ∈ Primes
5 ∈ Primes
8 6∈ Primes
(∀ x : t • x ∈ u) ∧ (∀ x : u • x ∈ t ) provided that x is
[ext]
t =u free in neither u nor t
This rule expresses an equivalence known as the axiom of extension, one of the
axioms of Zermelo–Fraenkel set theory—the variety of set theory upon which
the Z notation is based.
An expression belongs to a set described in extension if and only if it is
equal to one of the set’s elements:
t = u1 ∨ . . . ∨ t = un
[ext−mem]
t ∈ {u1 , . . . , un }
s == {2, 2, 5, 5, 3}
t == {2, 3, 5}
then
s=t
That is, the list expressions used to define s and t denote the same set.
60 5 / Sets
This may be proved using the two inference rules given above:
dx ∈ se[1] dx ∈ t e[2]
[eq−sub] [eq−sub]
x ∈ {2, 2, 5, 5, 3} x ∈ {2, 3, 5}
[ext−mem] [ext−mem]
x=2∨x=3∨x=5 x=2∨x=3∨x=5
[ext−mem] [ext−mem]
x ∈ {2, 3, 5} x ∈ {2, 2, 5, 5, 3}
[eq−sub] [eq−sub]
x∈t x∈s
[∀−intro[1] ] [∀−intro[2] ]
∀x : s • x ∈ t ∀x : t • x ∈ s
[∧−intro]
(∀ x : s • x ∈ t ) ∧ (∀ x : t • x ∈ s)
[ext]
s=t
Notice that, having defined s and t through syntactic abbreviations, we are able
to assume that they are equal and substitute accordingly.
Some sets are so useful that they are given special names, and regarded
as permanent features of our formal language: one such set is N, the set of all
natural numbers:
N = {0, 1, 2, 3, 4, 5, . . .}
This is not a formal definition of the set N; such a definition is provided later
in the book: see Example 6.8.
Another useful set is the set with no elements: the empty set. We write ∅
to denote such a set.
Example 5.5 If we let Rockallers be the set of people who live and work on
Rockall, a small uninhabited island in the Atlantic Ocean, then
Rockallers = ∅
Another axiom of Zermelo–Fraenkel set theory states that the empty set
exists, and has no elements:
[empty]
∀ x : a • x 6∈ ∅
Whatever set a that we consider, none of the values x in a will appear in the
empty set.
5.2 / Set comprehension 61
Example 5.6 Any universal quantification over the empty set is valid: that is,
∀ x : ∅ • p. This follows from the empty set axiom, with a as ∅:
[empty]
dx ∈ ∅e[1] ∀ x : ∅ • x 6∈ ∅
[∀−elim]
dx ∈ ∅e[1] ¬ (x ∈ ∅)
[¬−elim]
false
[false −elim]
p
[∀−intro[1] ]
∀x : ∅ • p
Example 5.7 Let Benelux denote the set of countries in the Benelux economic
union, and let Europe denote the set of all countries in the European Union.
Since the formation of the EU, it has been true that Benelux ⊆ Europe. There
were other partners when the EU (then the EEC) was formed in 1957, so it is
also true that ¬(Europe ⊆ Benelux).
∀x : s • x ∈ t provided that x
[subset]
s⊆t is not free in t
s⊆t ∧t ⊆s a s=t
Given any non-empty set s, we can define a new set by considering only those
elements of s that satisfy some property p. This method of definition is called
comprehension. We write
{x : s | p }
Example 5.8 Suppose that a red car is seen driving away from the scene of a
crime. In this case, the authorities might wish to talk to anyone who owns such
a vehicle. If Person denotes the set of all people, then the set to consider is
given by
{x : s | p • e}
to denote the set of all expressions e such that x is drawn from s and satisfies
p. The expression e will usually involve one or more free occurrences of x.
Example 5.9 In order to pursue their investigation of the crime, the authorities
require a set of addresses to visit. This set is given by
If we have no restrictions upon the choice of values, we can still use a set
comprehension to generate a set of expressions: we write
{x : s • e}
Example 5.10 Without the information that a red car was involved, the author-
ities would be left with the following set of addresses:
{ x : Person • address(x) }
{x : s | p } = {x : s | p • x }
{ x : s • e } = { x : s | true • e }
{ x : a; y : b | p • e }
to denote the set of expressions e formed as x and y range over a and b, re-
spectively, and satisfy predicate p.
Example 5.11 An eyewitness account has established that the driver of the red
car had an accomplice, and that this accomplice left a copy of the Daily Mail
at the scene. The authorities are now interested in tracing the following set of
potential criminals:
The variables declared in a set comprehension are bound in the same way
as variables declared in a quantified expression. We may change their names
provided that we choose names that are not already used in the comprehension.
Example 5.12 There is nothing special about the names chosen for the driver
and his or her accomplice. The set in Example 5.11 could equally well have
been written as
64 5 / Sets
∃x : s | p • e = f provided that x
[compre]
f ∈ {x : s | p • e} is not free in f
and
f ∈ {x : s | p } provided that x
[compre−s]
f ∈ s ∧ p[f / x] is not free in f
∀x : a • p ⇒ q
{x : a | p } ⊆ {x : a | q}
If a is a set, then the set of all subsets of a is called the power set of a, and
written P a. For example, if a is the set {x, y} then
This new set has four elements: the empty set, the set a itself, and the two
other subsets of a. In general, if a set a has n elements, then the power set P a
has 2n .
Example 5.14 Four friends have been invited to dinner: Alice, Bill, Claire, and
David. If their names are abbreviated to A, B, C, and D, then the set of people
5.4 / Cartesian products 65
P{A, B, C, D} = { ∅, {A}, {B}, {C}, {D}, {A, B}, {A, C}, {A, D},
{B, C}, {B, D}, {C, D}, {A, B, C}, {A, B, D},
{A, C, D}, {B, C, D}, {A, B, C, D} }
s⊆a
[power]
s ∈ Pa
This inference rule corresponds to the power set axiom of Zermelo-Fraenkel set
theory, which states that a power set exists for any set a.
∅ ∈ Pa
This follows from the result of Example 5.6, that any universal quantification
over the empty set is valid:
[Example 5.6]
∀x : ∅ • x ∈ a
[subset]
∅⊆a
[power]
∅ ∈ Pa
The Z notation has a second power set symbol; we write F a to denote the set
of finite subsets of a; this symbol is defined in Chapter 8.
tuple with exactly two elements is called an ordered pair ; a tuple with exactly
n elements, where n is greater than 2, is called an n-tuple.
Example 5.16 In the game of CluedoTM , it is assumed that a murder has been
committed. The players are then invited to guess the identity of the person
responsible, the room in which the crime was committed, and the weapon used.
If we define the set of guests, the set of locations, and the set of potential
weapons,
x1 ∈ a1 ∧ . . . ∧ xn ∈ an
[cart − mem]
(x1 , . . . , xn ) ∈ a1 × . . . × an
(x, y) ∈ a × b a x ∈ a ∧ y ∈ b
The ordered pair (x, y) is an element of the product set a × b if and only if x is
in a and y is in b.
The order of components in a Cartesian product is important: if a and b
are different sets, then a × b ≠ b × a. A similar consideration applies to the
elements of a product set: two tuples are the same if and only if they agree in
5.5 / Union, intersection, and difference 67
every component:
x1 = y1 ∧ . . . ∧ xn = yn
[cart−eq]
(x1 , . . . , xn ) = (y1 , . . . , yn )
t .1 = x1 ∧ . . . ∧ t .n = xn
[cart−proj]
t = (x1 , . . . , xn )
If guess is the tuple (Colonel Mustard, Library, Revolver ) then these would be
Colonel Mustard, Library, and Revolver , respectively.
The product set is so called because the size of the set a × b is the product of
the size of a and the size of b.
Example 5.18 There are 6 guests, 9 rooms, and 6 weapons in CluedoTM . There
are 6 × 9 × 6 = 324 elements in the set
If a and b are sets, then we write a ∪ b to denote the union of a and b; this is
the smallest set that contains all of the elements of a and b.
x ∈ (a ∪ b)
[union]
x∈a∨x∈b
Example 5.19 Edward, Fleur, and Gareth have each been given an assignment
consisting of 7 questions. Edward has attempted the questions numbered 1,
2, and 4; Fleur has attempted all but questions 5 and 6; Gareth has attempted
only those questions with even numbers. We may record this information as
follows:
E == {1, 2, 4}
F == {1, 2, 3, 4, 7}
G == {2, 4, 6}
We may discover which questions have been attempted by examining the union
of these three sets:
S
{E , F , G} = {1, 2, 3, 4, 6, 7}
This is the set of numbers n such that question n was attempted by at least one
of the three.
We write a ∩ b to denote the intersection of two sets a and b; this is the set that
contains only those elements that are common to a and b.
x ∈ (a ∩ b)
[inter]
x∈a∧x∈b
If s is the empty set of sets, then the universal quantification above will be true
T
for any x; the set ∅ contains all of the elements of the appropriate type: see
Section 5.6.
5.6 / Types 69
Example 5.20 Using the information of Example 5.19, we may discover which
questions were attempted by all three students by examining the intersection
T
{E , F , G} = {2, 4}
This is the set of numbers n such that question n was attempted by every one
of the three.
If a and b are sets, then we write a \ b to denote the set difference a minus b;
this is the set containing only those elements that appear in a but not in b.
x ∈ (a \ b)
[diff]
x ∈ a ∧ x 6∈ b
Example 5.21 The set of questions which have been attempted by both Edward
and Fleur, but have not been attempted by Gareth, is given by
(E ∩ F ) \ G = {1}
F \ (E ∪ G) = {3, 7}
5.6 Types
When people use set theory to specify software systems, they often include
some notion of types. In Z, this notion is a simple one: a type is a maximal set,
at least within the confines of the current specification. This has the effect of
ensuring that each value x in a specification is associated with exactly one type:
the largest set s present for which x ∈ s.
The Z notation has a single built-in type: the set of all integers Z. Any
other types will be constructed from Z, or from basic types of values. A basic
type is a set whose internal structure is invisible. We may introduce elements
of such a set, and associate properties with them, but we can assume nothing
about the set itself.
Example 5.22 A computer system used by the United States Immigration Ser-
vice might store information about foreign nationals presently in the United
States. In a specification of this system, the set of all people would be a good
70 5 / Sets
choice for a basic type. The set of all UK nationals would be a poor choice, as
we are likely to consider supersets of this set.
Additional types can be created using the power set constructor P and the
Cartesian product ×. If T is a type, then the power set P T is the type of all
subsets of T . If T and U are types, then T × U is the type of all pairs formed
from elements of T and elements of U .
Example 5.23 The power set P Z is the type of all sets of integers,
{1, 2, 3} ∈ P Z
(1, 2) ∈ Z × Z
The fact that each value in a specification is associated with exactly one
type is most useful. We can apply type-checking algorithms to the mathematical
text of a Z document to reveal any inconsistencies in the use of variable names
and expressions. Such algorithms can verify neither the interpretation of these
names nor the inferences made using them, but they are a powerful means of
increasing confidence in a formal specification.
Our use of types imposes restrictions upon the ways in which we may
define and use sets. For example, the statement x ∈ s is valid only if the type
of s is the power set of the type of x; if this is not the case, then the statement
is meaningless. Such restrictions are welcome.
Example 5.24 Suppose that types were not important in the use of ∈. In this
case, we could define R, the set of sets of some type T that are not members of
themselves:
R == {s : T | ¬ s ∈ s}
5.7 Proofs
We will often wish to prove that two sets are equal, or that one set is a subset
of another. Such proofs are often presented as a series of equivalences or
implications, each step justified by one of the inference rules given above, or a
tautology of propositional logic: see Section 2.7.
5.7 / Proofs 71
x ∈ S ∩ (T ∪ U )
a x ∈ S ∧ x ∈ (T ∪ U ) [definition of ∩]
a x ∈ S ∧ (x ∈ T ∨ x ∈ U ) [definition of ∪]
a (x ∈ S ∧ x ∈ T ) ∨ (x ∈ S ∧ x ∈ U )
[P ∧ (Q ∨ R) a P ∧ Q ∨ P ∧ R)]
a (x ∈ S ∩ T ) ∨ (x ∈ S ∩ U ) [definition of ∩, twice]
a x ∈ (S ∩ T ) ∪ (S ∩ U ) [definition of ∪]
∀ x : X • x ∈ S ∩ (T ∪ U ) a x ∈ (S ∩ T ) ∪ (S ∩ U )
S ⊆T
⇒ ∀x : X • x ∈ S ⇒ x ∈ T [definition of ⊆]
⇒ ∀x : X • ¬ x ∈ T ⇒ ¬ x ∈ S [P ⇒ Q a ¬ Q ⇒ ¬ P ]
x ∈U \T
⇒ x ∈ U ∧ ¬ (x ∈ T ) [definition of \]
⇒ x ∈ U ∧ ¬ (x ∈ S ) [from the above observation]
⇒x ∈U \S [definition of \]
There are no assumptions in which x appears free, so we may apply the univer-
sal introduction rule to obtain
∀x : X • x ∈ U \ T ⇒ x ∈ U \ S
72 5 / Sets
S ⊆T ⇒U \T ⊆U \S
The inference rules presented in this chapter are quite terse: they capture the
essence, but do not mention the underlying type of the sets involved. Later, in
Chapter 6, we will see how to add scaffolding, presenting generic definitions
for symbols such as ∩ and ∪.
Chapter 6
Definitions
6.1 Declarations
The simplest way to define an object is to declare it. If the object is a given
set, or basic type, then we do this by writing its name between brackets: for
example, the declaration
[Type]
introduces a new basic type called Type. If the object is a variable, then we give
the name of a set that it comes from. The declaration
x:A
introduces a new variable x, drawn from the set A. If this set is not Z, the type
of integers, then it must be defined elsewhere in the specification.
74 6 / Definitions
[Guest , Room]
introducing two basic types to represent the set of all guests and the set of all
rooms. A variable of the type Guest is introduced by the following declaration:
x : Guest
x:t |x∈s
6.2 Abbreviations
Another way to define an object is to exhibit an existing object and state that
the two are the same. The abbreviation definition
symbol == term
introduces a new name for term, a mathematical object that must be defined
elsewhere in the specification. The new name symbol is a global constant of the
specification, with the same type and value as the expression term.
introduces a set Additive, as another name for the set described in enumeration
above. The names red, green, and blue must be defined elsewhere, they are
not introduced by the abbreviation. If they are declared as elements of a type
Colours, then Additive is a constant of type P Colours.
6.3 / Generic abbreviations 75
Example 6.3 The recursive acronym gnu could not be defined using the abbre-
viation notation:
Example 6.4 Given the basic type Person, representing the set of all people, we
may introduce abbreviations for the set of all people who take sugar in tea:
and the set of all people who put salt on their porridge:
Provided that the constraining predicates are properly introduced, the two sets
above are bound to be well defined.
Example 6.5 The simplest example is the definition of the empty set symbol
∅. In a Z specification, there may be many empty sets, one for each type; we
distinguish between empty sets of different types. To define the empty set of
objects from a set S , we write
∅[S ] == { x : S | false }
76 6 / Definitions
Example 6.6 For any set T , we may define the set of all non-empty subsets of
T as follows:
P1 T == { a : P T | a ≠ ∅ }
We are happy to omit the brackets from the parameter list in the definition and
in instantiations:
A second generic symbol appears in the definition above: the ∅ symbol. From
the context, it is clear that this denotes the empty set of elements from T .
For the convenience of the reader, we allow the definition of infix generic
symbols. The abbreviation
defines a global constant symbol which may appear inside a list of parameters.
Example 6.7 We may define a generic symbol rel such that, for any sets s and
t , the set s rel t is the power set of s × t :
s rel t == P(s × t )
Each element of s rel t is a set of pairs; the first component of each pair is
an element of s; the second is an element of t . (In fact, this is exactly how we
define the ‘set of all relations’ symbol ↔: see Chapter 7).
Once an abbreviation definition has been made, we may conclude that the
symbol on the left is equal to the term on the right. Each abbreviation adds an
inference rule to our specification:
[abbrev]
s=e given the abbreviation s == e
A third form of definition includes a constraint upon the object being intro-
duced. Such definitions are said to be axiomatic, as the constraint is assumed
to hold whenever the symbol is used: it is an axiom for the object. In the Z
notation, an axiomatic definition takes the form
declaration
predicate
where the predicate expresses the constraints upon the object or objects intro-
duced in the declaration.
The definition
x:s
p
Example 6.8 We may use an axiomatic definition to define the set of natural
numbers:
N : PZ
∀z : Z • z ∈ N a z ≥ 0
If this is not the case, if there are several values of the underlying type that
meet the constraints, then we say that the definition is loose.
maxsize : N
maxsize > 0
That is, maxsize is a natural number, and it is strictly greater than zero.
78 6 / Definitions
[axdef]
x∈s∧p
Example 6.10 The definition of maxsize can be used to establish the truth of
the following predicate:
∃ n : N • n = maxsize − 1
We must take care that our axiomatic definitions do not introduce incon-
sistencies into a specification. Such a definition asserts that some object exists
with the stated property; this may contradict other parts of the specification,
or even well-known mathematical results.
Example 6.11 If Primes has been defined as the set of all prime numbers, then
we may define maxprime, the largest prime number, as follows:
maxprime : N
∀ p : Primes • maxprime ≥ p
x:s
[axdef]
x∈s
[X ]
x:X
p
Example 6.12 The generic non-empty power set constructor defined using an
abbreviation in Example 6.6 may also be defined using a generic definition:
[X ]
P1 : P(P X )
P1 = { s : P X | s ≠ ∅ }
In applications, the brackets around the generic parameter are optional: the
forms P1 [s] and P1 s are equally acceptable.
80 6 / Definitions
Example 6.13 We can use a generic definition to define the subset symbol:
[X ]
⊆ : PX ↔ PX
∀ s, t : P X •
s ⊆ t a ∀x : X • x ∈ s ⇒ x ∈ t
The ⊆ symbol denotes a relation between two sets of the same type P X (re-
lations are mathematical objects discussed in Chapter 7). In applications, we
omit the parameter list altogether:
{2, 3} ⊆ {1, 2, 3, 4}
The rule for introducing facts about a generic definition is similar to that for
axiomatic ones, with the obvious addition of a mechanism for instantiating
parameters. In the general case, if S is an expression including X —P X , for
example—and the specification contains the declaration
[X ]
x:S
p
[gendef]
(x ∈ S ∧ p)[t / X ]
where t is the value being given to the formal generic parameter X . Again, this
is a family of inference rules, for each definition in the specification, and for
each possible instantiation of X .
Example 6.14 The definition of P1 in Example 6.12 gives us the constraint in-
formation of its declaration and its axiom; we can use the latter to prove that
∅[N] ∉ P1 [N]
[gendef]
P1 [N] ∈ P(P N) ∧ P1 [N] = { s : P N | s ≠ ∅[N] }
[∧−elim2]
P1 [N] = { s : P N | s ≠ ∅[N] }
[eq-symm]
{ s : P N | s ≠ ∅[N] } = P1 [N] d∅[N] ∈ P1 [N]e[1]
[eq-sub]
∅[N] ∈ { s : P N | s ≠ ∅[N] }
[compre-s]
∅[N] ∈ P N ∧ ∅[N] ≠ ∅[N]
[eq-ref] [∧−elim2]
∅[N] = ∅[N] ∅[N] ≠ ∅[N]
[¬−elim]
false
[¬−intro[1] ]
∅[N] ∉ P1 [N]
All of the objects that we define in Z are sets of one kind or another. A reader
with some experience of mathematical logic might be excused for asking: how
do we define a predicate symbol? How do we introduce a symbol such as good,
so that good x is true for some values of x and false for others? The answer is
simple: we define a predicate in terms of the set of objects that satisfy it.
If p is a predicate with a free variable x of type t , and
c = {x : t | p }
then we say that c is the characteristic set of p: it is the set of values of x for
which p is true.
If we wished to define a predicate good, then we could define it as a set of
values:
good : P t
...
Example 6.15 We wish to formalise the predicate ‘is a crowd’ upon sets of
people. To do this, we introduce a set of sets:
The expression ‘#s’ denotes the number of elements in set s—it is defined form-
ally in Section 8.6. With this definition of crowds, we may make statements such
82 6 / Definitions
as
and
Example 6.16 For a number of reasons, it is not a good idea to have Alice and
Bill in the room at the same time. Thus, a set of people is safe if it contains
Alice, or Bill, or neither, but not both. We may define safe as a property of sets
of people:
and
Chapter 7
Relations
Although we may define relations that express links between any finite number
of objects, it is enough to employ binary relations: relations that express links
between pairs of objects. In our mathematical language, a relation is a set of
ordered pairs, a subset of a Cartesian product.
If X and Y are sets, then X ↔ Y denotes the set of all relations between X
and Y . The relation symbol may be defined by generic abbreviation:
X ↔ Y == P(X × Y )
Any element of X ↔Y is a set of ordered pairs in which the first element is drawn
from X , and the second from Y : that is, a subset of the Cartesian product set
X × Y.
84 7 / Relations
Example 7.1 The set of relations {a, b} ↔ {0, 1} is the set of sets of pairs
{∅, {(a, 0)}, {(a, 1)}, {(b, 0)}, {(b, 1)}, {(a, 0), (a, 1)}, {(a, 0), (b, 0)},
{(a, 0), (b, 1)}, {(a, 1), (b, 0)}, {(a, 1), (b, 1)}, {(b, 0), (b, 1)},
{(a, 0), (a, 1), (b, 0)}, {(a, 0), (a, 1), (b, 1)}, {(a, 0), (b, 0), (b, 1)},
{(a, 1), (b, 0), (b, 1)}, {(a, 0), (a, 1), (b, 0), (b, 1)}}
A typical element of this set is {(a, 0), (a, 1), (b, 0)}: the relation that associates
a with 0, a with 1, and b with 0.
Where ordered pairs are being used as elements of relations, we will often
write them using a maplet notation. The expression x , y is another way of
writing (x, y).
Example 7.2 The relation drives is used to record which makes of car are driven
by the members of a small group of people. If the group of people is defined
by
then drives is an element of Drivers ↔ Cars, and the statement ‘Kate drives a
cortina’ could be formalised as kate , cortina ∈ drives.
That is, Helen and Jim drive Beetles, Indra drives an Alfa, Kate drives a Cortina,
and nobody drives a DeLorean.
Simple relations can be illustrated using diagrams with arrows, or graphs. The
graph of drives is shown in Figure 7.1.
7.2 / Domain and range 85
Drivers Cars
helen alfa
indra beetle
jim cortina
kate delorean
dom R = { x : X ; y : Y | x , y ∈ R • x }
ran R = { x : X ; y : Y | x , y ∈ R • y }
Example 7.4 The set of people that drive is the domain of drives:
86 7 / Relations
We may focus upon part of the domain, or part of the range, by considering
a subset of the relation. If R is a relation of type X ↔ Y , and A is any subset of
X , then A / R denotes the domain restriction of R to A; this is the set of pairs
{x : X; y : Y | x , y ∈ R ∧ x ∈ A • x , y }
{x : X; y : Y | x , y ∈ R ∧ y ∈ B • x , y }
Example 7.6 If we are interested only in sports cars, then it is enough to con-
sider the relation drives . {alfa, delorean} which contains the single maplet
indra , alfa.
The position of the arguments is different for the two forms of restriction. In
domain restriction, the set argument appears to the left of the operator; in
range restriction, it appears to the right. This corresponds to the position of
domain and range in the Cartesian product underlying the relation.
To exclude the set A from the domain of a relation, we could consider
the domain restriction (X \ A) / R. However, this occurs so frequently that an
abbreviated form is provided. We write A R to denote the domain subtraction
of A from R, where
A R = { x : X ; y : Y | x , y ∈ R ∧ x 6∈ A • x , y }
This includes only those maplets whose first element does not lie in A.
Similarly, we may exclude the set B from the range of a relation. We write
R B to denote the range subtraction of B from R, where
R B = { x : X ; y : Y | x , y ∈ R ∧ y 6∈ B • x , y }
This includes every maplet whose second element does not lie in B.
7.2 / Domain and range 87
Drivers Cars
helen alfa
indra beetle
jim cortina
kate delorean
Example 7.7 If we are concerned only with people who are not called ‘Jim’, then
the relation {jim} drives tells us all that we want to know. It is a relation with
three elements:
R(| A |) = ran(A / R)
Example 7.8 The set of all cars that are driven by either Indra or Jim is given
by the relational image of the set {indra, jim} under drives. That is,
Drivers Cars
helen alfa
indra beetle
jim cortina
kate delorean
If R is an element of the set X ↔ Y , then we say that X and Y are the source
and target sets of R. The choice of terminology reminds us that relations are
directional: they relate objects of one set to objects of another. It is always
possible to reverse this direction, and thus present the same information in a
different way.
The relational inverse operator ∼ does exactly this. Source and target
are exchanged, and so are the elements of each ordered pair; the result is an
element of Y ↔ X such that
∀ x : X ; y : Y • x , y ∈ R∼ ⇒ y , x ∈ R
Example 7.9 The inverse of the relation drives, defined in Example 7.2, relates
cars to their drivers:
If the source and target of a relation have the same type, then we say that
the relation is homogeneous; if they are different, then we say that the relation
is heterogeneous.
7.3 / Relational inverse 89
Example 7.10 The relation < on natural numbers is homogeneous: the source
and the target sets are the natural numbers N. The relation drives is hetero-
geneous: the source is Drivers, the target is Cars.
id X == { x : X • x , x }
That is, it associates each element of X with itself, and makes no other associ-
ations. The identity relation is useful in reasoning about other relations and in
classifying them. If a homogeneous relation contains the identity relation, we
say that it is reflexive. The set of all reflexive relations on X is given by
Reflexive[X ] == {R : X ↔ X | id X ⊆ R}
Example 7.11 The relation ≤ upon N is reflexive; the relation < is not.
Symmetric[X ] == {R : X ↔ X | ∀ x, y : X • x , y ∈ R a y , x ∈ R}
x ,y ∈S
ay ,x ∈S [S is symmetric]
a x , y ∈ S∼ [definition of ∼ ]
∀ x : X ; y : Y • x , y ∈ S a x , y ∈ S∼
Antisymmetric[X ] ==
{ R : X ↔ X | (∀ x, y : X • x , y ∈ R ∧ y , x ∈ R ⇒ x = y) }
House House
louise louise
martin martin
natalie natalie
Example 7.12 The subset relation ⊆ is antisymmetric. For any two sets s and
t , if s ⊆ t and t ⊆ s, then s = t . This fact is often used to show that two sets
are equal.
Asymmetric[X ] ==
{ R : X ↔ X | ∀ x, y : X • (x , y ∈ R) ⇒ ¬(y , x ∈ R) }
This relation tells us that Louise likes Martin, that Martin likes Louise, and
Martin likes Natalie: see Figure 7.4.
7.4 / Relational composition 91
If the target type of one relation matches the source type of another, then they
may be combined to form a single relation. If R is an element of X ↔ Y , and S
is an element of Y ↔ Z, then we write R o9 S to denote the relational composition
of R and S . This is the element of X ↔ Z such that
x , z ∈ R o9 S a ∃ y : Y • x , y ∈ R ∧ y , z ∈ S
That is, two elements x and z are related by the composition R o9 S if there is an
intermediate element y such that x is related to y and y is related to z.
Example 7.15 The relation uses of type Cars ↔ Fuels tells us which fuel is used
by each of the cars in Example 7.2:
An Alfa can use either leaded or unleaded petrol, the older cars—Beetles and
Cortina—require leaded petrol, and the DeLorean in question runs on large
amounts of electricity.
We may compose the relations drives and uses to find out which fuels a
driver may purchase. If buys = drives o9 uses then buys is a relation of type
Drivers ↔ Fuels such that
This composition of relations is illustrated in Figure 7.5. Note that the maplet
delorean , electricity makes no contribution to the new relation.
drives uses
the identity relation, and symmetric if it includes its own inverse. The inclusion
of a relational composition is associated with a third property: transitivity.
A homogeneous relation R is transitive if every pair of connecting maplets
x , y and y , z in R has a corresponding maplet x , z in R.
Transitive[X ] ==
{ R : X ↔ X | ∀ x, y, z : X • x , y ∈ R ∧ y , z ∈ R ⇒ x , z ∈ R }
Example 7.16 The likes relation is not transitive: it contains louise , martin
and martin , natalie but not louise , natalie. Louise likes Martin, and Martin
likes Natalie, but Louise does not like Natalie.
x ,z ∈T o
9 T
⇒ ∃y : X • x , y ∈ T ∧ y , z ∈ T [definition of o9]
⇒ ∃y : X • x , z ∈ T [T is transitive]
⇒x ,z ∈T [property of ∃]
7.4 / Relational composition 93
∀ x, z : X • x , z ∈ T o
9 T ⇒x ,z ∈T
x ,t ∈T ∧t ,z ∈T
⇒ ∃y : X • x , y ∈ T ∧ y , z ∈ T [property of ∃]
⇒x ,z ∈T o
9 T [definition of o9]
⇒x ,z ∈T [T o
9 T ⊆ T]
∀ x, t , z : X • x , t ∈ T ∧ t , z ∈ T ⇒ x , z ∈ T
Example 7.17 The relation same sign holds between two people if and only if
they have the same birth sign. Assuming that each person has exactly one birth
sign, this is an equivalence relation:
• any person a has the same sign as themselves, so same sign is reflexive;
• if a has the same sign as b, then b has the same sign as a, so same sign
is symmetric;
• if a has the same sign as b, and b has the same sign as c, then a has the
same sign as c, so same sign is transitive.
{x : X | x , a ∈ E }
House House
louise louise
martin martin
natalie natalie
Example 7.18 The relation same sign divides Person into twelve equivalence
classes, corresponding to the twelve signs of the zodiac. If Marina was born
on 28th January, then the equivalence class of Marina will be the set of all
Aquarians (20th January – 18th February).
7.5 Closures
An idea that is often useful in specification is that of closure: given some in-
formation, we consider what may be obtained by using it to its fullest extent, or
by adding to it in a well-defined way. Applied to relations, this means adding
maplets to a relation until some useful property is achieved.
The simplest form of closure is obtained by adding the identity relation.
If R is a homogeneous relation, we write Rr to denote its reflexive closure, where
Rr = R ∪ id X
Example 7.19 The likes relation of Example 7.14 is its own reflexive closure:
the maplets louise , louise, martin , martin, and natalie , natalie are already
present.
Rs = R ∪ R∼
7.5 / Closures 95
Any symmetric relation containing R must also contain R∼ ; the smallest such
relation is obtained by adding the maplets in R∼ .
Example 7.20 The likes relation is not symmetric: Martin likes Natalie, but Nat-
alie does not like Martin. To obtain the symmetric closure likes s , we must add
the maplet natalie , martin: the result is shown in Figure 7.6.
R1 = R
R2 = R o9 R
R3 = R o9 R o9 R
..
.
R+ = { n : N | n ≥ 1 • Rn }
S
96 7 / Relations
London
San Francisco
Singapore
Perth
Example 7.22 We may use a relation direct to record the availability of a direct
flight between two airports. For the four airports shown in Figure 7.7, this
relation is given by
The composition direct o9 direct comprises all of the possibly indirect flights
that involve at most one stop en route:
It is now possible to reach Perth from London. With a second stop, we may
reach Perth from San Francisco using only the flights shown in direct :
London
San Francisco
Singapore
Perth
Example 7.23 The transitive closure of direct relates two airports exactly when
there is a route between them consisting of some number of direct flights: see
Figure 7.8.
R∗ = R+ ∪ id X
Example 7.24 In the direct + relation of Example 7.23, there is no way to travel
from Perth to Perth: in our small collection of routes, there is not one flight that
98 7 / Relations
Chapter 8
Functions
X→
7 Y ==
{f : X ↔ Y | ∀ x : X ; y1 , y2 : Y • x , y1 ∈ f ∧ x , y2 ∈ f ⇒ y1 = y2 }
Example 8.1 An organisation has a system for keeping track of its employees
while they are on the premises. Each employee is issued with an active badge
which reports their current position to a central database. If the set of all people
is Person, and the set of all locations is Location, then the information provided
by the system may be described by a relation where is of type Person ↔Location.
100 8 / Functions
where is ∈ Person →
7 Location
These relations are called partial functions because there may be elements
of X that are not related to any element of Y . If each element of X is related
to some element of Y , then the function is said to be total; we write X → Y to
denote the set of all total functions from X to Y , where
X → Y == {f : X →
7 Y | dom f = X }
The domain of a total function must be the whole of the source set.
Example 8.2 We may define a relation double on the set of natural numbers N
as follows:
double : N ↔ N
∀ m, n : N • m , n ∈ double a m + m = n
This relation is a total function: for every natural number m there is a unique
number n such that m , n ∈ double.
The second states that if b = f (a) and there is a unique pair whose first element
is a, then a , b ∈ f .
quentin
rachel
peter office
otto
kitchen
f = {x : X | p • x , e}
102 8 / Functions
to denote the function that maps each object introduced by the declaration
that satisfies the constraint to the expression result . Using lambda notation,
our function f could be described as
f = (λx : X | p • e)
double : N ↔ N
double = ( λ m : N • m + m )
As the domain of this function is the whole of the source, the constraint part
of the lambda expression is omitted.
Example 8.5 The function min maps a set of natural numbers to the least num-
ber present in that set: the minimum value.
min : P N →
7 N
min =
(λs : PN | s ≠ ∅ •
(µ x : s | ∀ y : s | y ≠ x • y > x) )
The minimum value is the unique element x of s such that for any element y of
s, if y is not equal to x then y must be greater than x.
The constraint s ≠ ∅ restricts the domain of the function to non-empty
sets of natural numbers. If we wished to define a corresponding function max
to return the maximum value in a set, then we would need to add a further
constraint upon its domain, restricting to finite sets of natural numbers (finite
sets are explained in the next chapter).
( λ a : A; b : B; c : C • . . . )
Example 8.6 The function pair takes two functions f and g as arguments, each
of which must be a homogeneous relation on N. The result is a function that
takes a natural number n and returns the pair formed by applying each of f
and g to n:
pair : ((N →
7 N) × (N →
7 N)) → (N →
7 (N × N))
pair = ( λ f , g : N →
7 N • (λ n : N | n ∈ dom f ∩ dom g • (f n, g n)) )
Because f and g may be any partial function on the natural numbers, pair is a
total function.
If, on the other hand, our intention is to apply two functions, one after
the other, then we may use the relational composition operator. If f and g are
functions, then so too is f o9 g. If we suppose that f ∈ X →
7 Y and g ∈ Y → 7 Z,
that x is an element of X , and that z1 and z2 are elements of Z , we may argue
as follows:
x , z1 ∈ f o
9 g ∧ x , z2 ∈ f o
9 g
⇒ (∃ y1 : Y • x , y1 ∈ f ∧ y1 , z1 ∈ g) ∧ [definition of o9, twice]
(∃ y2 : Y • x , y2 ∈ f ∧ y2 , z2 ∈ g)
⇒ ∃ y1 , y2 : Y • x , y 1 ∈ f ∧ x , y2 ∈ f ∧ [predicate logic]
y1 , z1 ∈ g ∧∧ y2 , z2 ∈ g
⇒ ∃ y1 , y2 : Y • y 1 = y2 ∧ y1 , z 1 ∈ g ∧ y2 , z 2 ∈ g [f is a function]
⇒ ∃ y1 : Y • y1 , z 1 ∈ g ∧ y1 , z 2 ∈ g [one-point rule]
104 8 / Functions
⇒ ∃ y1 : Y • z1 = z2 [g is a function]
⇒ z1 = z2 [predicate logic]
This result confirms that, rather than writing g (f x), it is perfectly acceptable
to write (f o9 g) x.
incr : N → N
decr : N →
7 N
∀n : N •
incr n = n + 1 ∧
decr n = n − 1
(incr o
9 decr ) = id N
In the previous chapter, we introduced the operators that form the basis of a
calculus of relations: domain, range, inverse, composition, and closure. These
may all be seen as examples of functions upon relations.
[X , Y ]
dom : (X ↔ Y ) → P X
ran : (X ↔ Y ) → P Y
∀R : X ↔ Y •
dom R = { x : X | ∃ y : Y • x , y ∈ R } ∧
ran R = { y : Y | ∃ x : X • x , y ∈ R }
8.3 / Functions on relations 105
[X , Y ]
/ : P X × (X ↔ Y ) → (X ↔ Y )
. : (X ↔ Y ) × P Y → (X ↔ Y )
∀R : X ↔ Y; A : PX; B : PY •
A / R = {x : X; y : Y | x ∈ A ∧ x , y ∈ R • x , y } ∧
R . B = {x : X; y : Y | y ∈ B ∧ x , y ∈ R • x , y }
[X , Y , Z]
o
9 : (X ↔ Y ) × (Y ↔ Z) → (X ↔ Z)
∀R : X ↔ Y; S : Y ↔ Z •
R o9 S = { x : X ; y : Y ; z : Z | x , y ∈ R ∧ y , z ∈ S • x , z }
[X , Y ]
∼ : (X ↔ Y ) → (Y ↔ X )
∀R : X ↔ Y •
R∼ = { x : X ; y : Y | x , y ∈ R • y , x }
Given any two sets X and Y , the inverse operator is a total function on the set
X ↔ Y which yields relations in the set Y ↔ X .
106 8 / Functions
[X ]
+
: (X ↔ X ) → (X ↔ X )
∗
: (X ↔ X ) → (X ↔ X )
∀R : X ↔ X •
R+ = {T : X ↔ X | R ⊆ T ∧ (T o9 T ) ⊆ T } ∧
T
R∗ = {T : X ↔ X | (R ∪ id X ) ⊆ T ∧ (T o9 T ) ⊆ T }
T
These definitions rely upon the fact that a relation T is transitive if and only if
it contains the composition T o9 T .
8.4 Overriding
[X , Y ]
⊕ : (X ↔ Y ) × (X ↔ Y ) → (X ↔ Y )
∀f,g : X ↔ Y •
f ⊕ g = (dom g f ) ∪ g
Example 8.14 Suppose that a partial update arrives from the staff location sys-
tem, informing us that Rachel and Sally are in the lobby and that Tim is in the
office. This update may be represented by a partial function from Person to
Location:
The union of this function with our original information would not be func-
tional. The expression (where is ∪ update) rachel is not defined, as there are
two locations associated with rachel in this relation.
8.4 / Overriding 107
quentin
peter office
sally
otto
rachel
tim
kitchen
as our new location relation. The result is shown in Figure 8.2. The information
that Quentin and Peter are in the meeting room comes from where is, the others
are within the domain of update.
dom f ∩ dom g = ∅ ⇒ f ⊕ g = f ∪ g
dom f ∩ dom g = ∅ ⇒ f ⊕ g = g ⊕ f
In practice, we can do better than this: it is enough to know that the two func-
tions agree on the intersection of their domains:
∀ x1 , x2 : dom f • f x1 = f x2 ⇒ x1 = x2
8.5 / Properties of functions 109
Staff
kenny Doors
peanut front
neville back
animal
Example 8.16 The location function of Example 8.3 is not injective. According
to where is, there are at least two different people in the meeting room:
Example 8.17 There are three entrances to the Pink Flamingo: the front door,
the back door, and the fire exit. Kenny and Peanut are paid to stand at the front
door, Neville and Sammy are watching the back, and Animal is leaning on the
fire exit. This situation is represented by a surjective function,
Staff
kenny Doors
peanut front
neville back
animal
Example 8.18 It is Senior Citizens Night at the Pink Flamingo, so Reg Thorpe,
the owner, decides that he needs just one bouncer on each door. Peanut and
Sammy are given the night off, and the relaxed state of security is described by
the following mapping, pictured in Figure 8.4:
{kenny , front ,
neville , back,
animal , fire exit }
8.5 / Properties of functions 111
A B == { f : A →
7 B | ∀ x1 , x2 : dom f • f x1 = f x2 ⇒ x1 = x2 }
A ) B == (A → B) ∩ (A B)
A total injective function is any member of this set which is also a total function
from A to B.
Example 8.19 If s and t are the two sets {1, 2} and {a, b, c} respectively, then
the set of all partial injective functions from s to t is given by:
s t = {∅,
{1 , a}, {1 , b}, {1 , c}, {2 , a}, {2 , b}, {2 , c},
{1 , a, 2 , b}, {1 , a, 2 , c}, {1 , b, 2 , a},
{1 , b, 2 , c}, {1 , c, 2 , a}, {1 , c, 2 , b}}
If A and B are sets, we define the set of all partial surjections from one to the
other as follows:
A→
→
7 B == { f : A →
7 B | ran f = B }
A→
→ B == (A → B) ∩ (A →
→
7 B)
A total surjective function is any element of this set that is also a total function
from A to B.
Example 8.20 If s and t are as defined in Example 8.19, then there are no sur-
jective functions from s to t :
s→
→
7 t = ∅
The source s has fewer elements than the target t , and for a function to be
surjective, there must be at least as many elements in the domain—a subset of
the source—as there are in the target.
112 8 / Functions
Finally, if A and B are sets, we define the set of all partial bijections from A to
B by generic abbreviation:
→
7 B == (A B) ∩ (A →
A) →
7 B)
→ B == (A )
A) →
7 B) ∩ (A → B)
A total bijection is any element of this set that is also a total function.
Example 8.21 As none of the functions from s and t are surjections, there can
be no bijections between these two sets. If we replace t with the set {a, b}, then
two bijections are possible:
→
7 {a, b} = {{1 , a, 2 , b}, {1 , b, 2 , a}}
s)
Both of these are total: for a function to be bijective, the domain and the target
must have the same number of elements.
Our mathematical language can be used to talk about any set, regardless of the
number of elements it contains; indeed, infinite sets are often a more conveni-
ent abstraction. However, it is worth considering the properties of finite sets, if
only because they form the basis of our theory of finite sequences—the subject
of the next chapter.
A finite set is one whose elements are countable up to some natural num-
ber n: that is, a set that may be seen as the range of a total bijection from the
set 1, 2, . . . , n.
Example 8.22 The set {a, b, c} is finite: it may be seen as the range of a bijection
from the set {1, 2, 3},
{1 , a, 2 , b, 3 , c}
.. : N × N → PN
∀ m, n : N • m . . n = {i : N | m ≤ i ≤ n}
8.6 / Finite sets 113
If m and n are natural numbers, then m . . n is the set consisting of all numbers
between m and n, inclusive.
We can now introduce a second power set symbol: if X is a set, then the
set of all finite subsets of X is given by
→ s • true }
F X == { s : P X | ∃ n : N • ∃ f : 1 . . n )
Example 8.23
• There are a finite number of oceans: we can exhibit a total bijection from
the range 1 . . 4 to the set of all Oceans:
• There is only one Jose Feliciano: we can exhibit a total bijection from the
range 1 . . 1 to the set of all Jose Felicianos:
{1 , jose feliciano}
• The set of all inhabitants of Rockall is finite, despite being empty. The
empty function is a total bijection from the empty number range 1 . . 0 to
the empty set:
→ Rockallers
∅ ∈ 1 .. 0 )
• The set of prime numbers is not finite. Given any natural number n, any
total injection
inj ∈ 1 . . n ) Primes
[X ]
# : FX → N
∀s : FX; n : N •
→ s • true
n = #s a ∃ f : (1 . . n) )
114 8 / Functions
For any finite set s, there is exactly one natural number n such that we can
define a bijection from 1 . . n to s.
Example 8.24
• #Oceans = 4
• #Jose Felicianos = 1
• #Rockallers = 0
• #Primes is undefined
If the domain of a function is a finite set, then that function is itself finite;
we write A →7 7 B to denote the set of all finite functions from A to B:
A→
7 7 B == { f : A →
7 B | dom f ∈ F A }
7 7 B == A →
A) 77 B ∩ A B
Example 8.25 The mapping from bouncers to doors described in Example 8.17
is a finite function:
bouncers ∈ Staff →
7 7 Doors
Once Peanut and Sammy are given the night off, as in Example 8.18, the mapping
becomes a finite injection: the entrances are indexed by bouncers, without
repetition.
Chapter 9
Sequences
It is sometimes necessary to record the order in which objects are arranged: for
example, data may be indexed by an ordered collection of keys; messages may
be stored in order of arrival; tasks may be performed in order of importance.
In this chapter, we introduce the notion of a sequence: an ordered collection of
objects. We examine the ways in which sequences may be combined, and how
the information contained within a sequence may be extracted. We show that
the resulting theory of sequences falls within our existing theory of sets, and
provide formal definitions for all of the operators used. The chapter ends with
a proof method for universal statements about sequences.
Example 9.1 The ticket office in a railway station has a choice of two counters
at which tickets may be purchased. There are two queues of people, one at each
116 9 / Sequences
Sally and Vicky are at the head of their respective queues, but—just as Vicky
is about to be served—the ticket machine at Counter b breaks down, and the
people waiting there join the end of other queue. Order is maintained, so the
result is given by queue a _ queue b, the sequence
Sally is interested only in those trains that are going to London; she would be
content with the filtered sequence
that is,
9.1 / Sequence notation 117
time from to
11 15 MANCHESTER POOLE
11 20 OXFORD READING
head ha, b, c, d, ei = a
tail ha, b, c, d, ei = hb, c, d, ei
Notice that the head of a sequence is an element, while the tail is another se-
quence. If s is any non-empty sequence, then
s = hhead si _ tail s
Example 9.3 Sally wants to take the first train to London. From the list of trains
on the destination board, she knows that this is the 10:15,
Tim is still waiting to buy a ticket, and the first train is about to leave. If we
assume that he will not reach the platform in time, then the list of available
trains is given by ‘tail trains’, the sequence
118 9 / Sequences
#ha, b, c, d, e, f i = 6
Recall that the same notation was used in Section 8.6 to denote the size of a
finite set.
Example 9.4 The total number of trains on the destination board is given by
#trains = 6
Example 9.5 The names and addresses of Sally’s friends are stored in 26 files,
address.a, address.b, …, address.z, according to the first letter of the person’s
surname. Within each file, the records are ordered alphabetically: e.g., ad-
dress.h contains the records
Sally would prefer to have just one file containing all of the records. Using the
DOS copy command, she types:
—then we may describe the effect of the copy command using distributed con-
catenation:
The result is exactly what Sally requires: a file address.all containing all of the
names and addresses, arranged in alphabetical order.
The operators introduced above have not been formally defined; we have no
way of proving anything about sequences, nor can we be sure that mathematical
objects exist with the specified properties. For example, we stated that for any
non-empty sequence s,
s = hhead si _ tail s
but how can we be sure that this is the case? Clearly, we must find a formal
basis for our theory of sequences.
Such a basis already exists within our mathematical language. A sequence
may be regarded as a function from the natural numbers to a collection of
objects: the object associated with 1 comes first in the sequence, the object as-
sociated with 2 comes second, and so on. Thus, in our mathematical language,
a finite sequence is a function defined upon some initial segment of the natural
numbers: a number range starting at 1.
If X is a set, then the set of all finite sequences of objects from X is defined
by the following abbreviation:
seq X == {s : N →
7 7 X | ∃ n : N • dom s = 1 . . n}
This definition makes explicit an assumption about sequences: that every ele-
ment of a given sequence must share the same type. The expression
makes no sense to us: the first element is an element of N, while the second is
an element of N × N.
120 9 / Sequences
queue b 3 = wilson
and the second train on the destination board is the 10.38 to Edinburgh:
Furthermore, the practice of using ‘#’ to denote both the cardinality of a set
and the length of a sequence now makes perfect sense: if we regard a sequence
s as a function, then the length of s is equal to the number of maplets in s.
If s and t are sequences and i is a number in the range 1 . . #s, then the i th
element of s_t is the i th element of s:
(s_t ) i = s i
and if j is a number in the range 1 . . #t , then the (j + #s)th element of s_t is the
j th element of t :
(s_t ) (j + #s) = t j
[X ]
_ : seq X × seq X → seq X
∀ s, t : seq X •
#(s_t ) = #s + #t
∀ i : 1 . . #s • (s_t ) i = s i
∀ j : 1 . . #t • (s_t ) (#s + j) = t j
9.2 / A model for sequences 121
The restriction operator is harder to define. Not only must we remove any
maplets which point to objects outside the chosen set, but we must also make
sure that the result is a sequence. The first task is accomplished using range
restriction, while the second requires an auxiliary function:
s u A = squash (s . A)
The auxiliary function squash takes a finite function defined upon the nat-
ural numbers and returns a sequence. It compacts the domain to remove any
spaces created by range restriction, while preserving the order of the remaining
maplets: for example,
squash {1 , a, 3 , c, 6 , f } = {1 , a, 2 , c, 3 , f } = ha, c, f i
[X ]
squash : (N1 →
7 7 X ) → seq X
∀ f : (N1 →
77 X ) •
→ dom f | g ∼ o9 ( + 1) o9 g ⊆ ( < ))
squash f = (µ g : 1 . . #f )
o
9
For any function f whose domain is a finite set of numbers, we consider the
unique function g that enumerates the domain of f in ascending order. The
composition of g with f is then the sequence we require.
Our generic definition of restriction is then:
[X ]
u : seq X × P X → seq X
∀ s : seq X ; A : P X •
s u A = squash (s . A)
[X ]
head : seq X →
7 X
∀ s : seq X | s ≠ hi •
head s = s 1
122 9 / Sequences
[X ]
tail : seq X →
7 seq X
∀ s : seq X | s ≠ hi •
#tail s = #s − 1
∀ i : 1 . . #s − 1 • (tail s) i = s (i + 1)
These definitions make it clear that ‘head’ and ‘tail’ are strictly partial: the
empty sequence has neither a head nor a tail. Because this sequence is excep-
tional, we give a name to the set of all non-empty sequences over X ,
Observe that ‘head’ and ‘tail’ are total when defined upon this set.
Another special set of sequences is the set of all injective sequences: se-
quences in which no element appears more than once. We write iseq X to denote
the set of all injective sequences over set X , where
iseq X == { s : seq X | s ∈ N ) X }
f hi = k
_
f (hxi s) = g (x, f (s))
Example 9.7 The function ‘reverse’ returns a sequence in which the elements
appear in reverse order. The two equations
reverse hi = hi (reverse.1)
are enough to describe the effect of ‘reverse’ upon any finite sequence. In this
case, the constant k is simply hi, and the function g is given by
[X ]
g : X × seq X → seq X
∀ x : X ; s : seq X •
g (x, s) = (reverse s) _ hxi
hi u A = hi (filter.1)
Example 9.8 Using laws ‘reverse.1’ and ‘filter.1’, we may construct a proof that
‘reverse(hi u A) = (reverse hi) u A’ for any set A. We proceed as follows:
reverse(hi u A)
= reverse hi [filter.1]
= hi [reverse.1]
= hi u A [filter.1]
= (reverse hi) u A [reverse.1]
124 9 / Sequences
f (s _ t ) = (f s) _ (f t )
Example 9.9 The function add one is defined on sequences of numbers by the
following pair of equations:
add one hi = hi
add one (hni _ s) = hn + 1i _ (add one s)
The effect of applying add one is to increase each number in the sequence
by precisely one: for example, add one h2, 4, 6i = h3, 5, 7i. This function is
distributive, as add one (s _ t ) = (add one s) _ (add one t ).
(s _ t ) u A = (s u A) _ (t u A)
s = hx1 , x2 , x3 , . . .i
• P 0 is true, and
• if i ∈ N and P i is true, then P (i + 1) is also true
9.4 / Structural induction 125
then P n is true for all natural numbers n. This is an induction principle for the
natural numbers, and it can be extremely useful in proofs of universal proper-
ties.
Example 9.10 The cumulative sum of the first n natural numbers has the value
x = (n 2 + n) div 2. Assuming a suitable definition of the function ‘sum’, and
using the induction principle as a proof rule, we define a predicate
P : PN
∀ n : N • P n a sum{ i : 0 . . n } = (n 2 + n) div 2
.. ..
. .
P0 ∀ m : N • P m ⇒ P (m + 1)
[induction]
∀n : N • P n
[axdef]
∀ n : N • sum{ i : 0 . . n } = (n 2 + n) div 2
The left-hand branch of the proof is called the base case; the right-hand branch
is called the inductive step.
• P hi is true
• if x ∈ X , t ∈ seq X , and P t is true, then P (hxi _ t ) is also true
then P s is true for all sequences s in seq X . This is an induction principle for
finite sequences over X ; it can be written as a proof rule:
P hi ∀ x : X ; t : seq X • P t ⇒ P (hxi _ t )
[induction]
∀ s : seq X • P s
∀ s, t : seq X ; A : P X • (s _ t ) u A = (s u A) _ (t u A)
126 9 / Sequences
There are two sequence variables in the above predicate, but we will need to
consider only one of them. Our inductive hypothesis is described by the fol-
lowing predicate:
P : P seq X
∀ s : seq X •
P s a ∀ t : seq X ; A : P X • (s _ t ) u A = (s u A) _ (t u A)
The base case and inductive step in this proof have been reduced to simpler
inferences: Lemma 1 and Lemma 2. These can be established by equational
reasoning, using the following laws:
hi _ s = s (cat.1)
s _ (t _ u) = (s _ t ) _ u (cat.2)
The first of these confirms that hi is a unit for the concatenation operator; the
second states that concatenation is associative.
The first lemma can be proved using the unit law of concatenation and law
‘filter.1’, which describes the effect of applying the filter operator to the empty
sequence.
(hi _ t ) u A
=t uA [cat.1]
= hi _ (t u A) [cat.1]
= (hi u A) _ (t u A) [filter.1]
((hxi _ r ) _ t ) u A
9.4 / Structural induction 127
= (hxi _ (r _ t )) u A [cat.2]
= hxi _ ((r _ t ) u A) if x ∈ A [filter.2]
(r _ t ) u A otherwise
_ _
= hxi ((r u A) (t u A)) if x ∈ A [P r ]
(r u A) _ (t u A) otherwise
_ _
= (hxi (r u A)) (t u A) if x ∈ A [cat.2]
(r u A) _ (t u A) otherwise
= ((hxi _ r ) u A) _ (t u A) [filter.2]
The step marked P r is justified by our inductive assumption that the result
holds for sequence r .
Example 9.11 For any set A and sequence s, the sequences ‘reverse(s u A)’ and
‘(reverse s) u A’ are equal, provided that the types of s and A are compatible:
The order in which reverse and filter are applied makes no difference.
This result is easily established by structural induction, with the following
predicate as an inductive hypothesis:
P : seq X
∀ s : seq X •
P s a ∀ A : P X • reverse(s u A) = (reverse s) u A
The base case of the induction has already been established; it was the subject
of a proof in Example 9.8.
As in the proof that the filter operator is distributive, the inductive step can be
128 9 / Sequences
reverse((hxi _ r ) u A)
= reverse(hxi _ (r u A)) if x ∈ A [filter.2]
reverse(r u A) otherwise
= reverse(r u A) _ hxi if x ∈ A [reverse.2]
reverse(r u A) otherwise
= ((reverse r ) u A) _ hxi if x ∈ A [P r ]
(reverse r ) u A otherwise
= ((reverse r ) u A) _ (hxi u A) [filter.2]
= ((reverse r ) _ hxi) u A [filter is distributive]
= (reverse (hxi _ r )) u A [reverse.2]
As with ordinary formal proofs, once an equational result has been established,
it may be used as a law in subsequent proofs. Here, we have been able to exploit
the fact that the filter operator is distributive.
9.5 Bags
A sequence stores information about the multiplicity and ordering of its ele-
ments. In the sequence ha, b, c, a, b, ci, we can see that there are exactly two
occurrences of a, and that these occupy the first and fourth positions in the
sequence. Sometimes this is more information than we need.
Suppose that only the number of occurrences of a is important. If this is
the case, then the sequence above contains more detail than is necessary: it is
not a fully abstract representation. The set {a, b, c}, on the other hand, is not
an adequate representation: it records that a is present, but does not record
how many times it occurs.
If we wish to record multiplicities, but not ordering, then we may represent
a collection of objects as a bag. We write a, a, b, b, c, c to denote the bag
containing two copies of a, two copies of b, and two copies of c. The order
in which elements are written is not important: the expression a, b, b, a, c, c
denotes exactly the same bag.
Example 9.12 Four friends—Alice, Bill, Claire, and David—are sitting in a café,
waiting for their drinks to arrive. Alice and Claire have asked for espresso, Bill
has asked for fruit tea, and David has asked for grape juice. Their requests can
be represented by a bag:
The order in which these drinks are to be delivered is left unspecified; the group
will be content with any ordering that includes two espressos, one fruit tea, and
one grape juice.
bag X == X →
7 N \ {0}
where N denotes the set of all natural numbers. A bag is a finite partial function
from X to N; elements of X that do not appear in the bag are left out of the
domain, rather than mapped to zero.
If we wish to know how many instances of an object there are in a given
bag, then we could simply apply the bag as a function. However, if the object
is not present in the bag, the effect of this functional application is undefined.
To avoid this, we employ the total function count :
[X ]
→ (X → N)
count B : bag X )
∀ B : bag X •
count B = ( (λ x : X • 0) ⊕ B )
[X ]
] : bag X × X → N
∀ B : bag X ; x : X •
B ] x = count B x
Example 9.13 If drinks denotes the collection of drinks requested by the four
friends in Example 9.12, then
reflecting the fact that two people have asked for an espresso. It is also possible
to order decaffeinated coffee in the café, but no-one has asked for it. This
information may be expressed using the prefix function count ,
We define bag membership and sub-bag relations, similar to the set mem-
bership and subset relations introduced in Chapter 5:
[X ]
ä : X ↔ bag X
−
v : bag X ↔ bag X
∀ x : X ; B : bag X •
x− ä B a x ∈ dom B
∀ B, C : bag X •
B v C a ∀x : X • B ] x ≤ C ] x
grape juice −
ä drinks
9.5 / Bags 131
We define also bag union and bag difference operators. If B and C are
bags of the same type, then their union B ] C contains as many copies of each
element as B and C put together:
[X ]
] , ∪
− : bag X × bag X → bag X
∀ B, C : bag X ; x : X •
B]C ]x =B]x +C ]x
B∪ − C ] x = max{B ] x − C ] x, 0}
If there are m copies of some element in bag B, and n copies of the same element
in bag C, then the bag difference B ∪− C contains m − n copies, provided that
m ≥ n. If there are more copies in C than in B, then the count of this element
is zero in the difference.
Example 9.15 No sooner have the drinks been asked for than two more friends
arrive—Edward and Fleur—and take their places at the table. Edward asks for
a cappucino; Fleur asks for a mineral water. If we define
remainder == requests ∪
− tray
No-one has asked for a decaffeinated coffee, decaffeinated coffee 6−ä requests,
so its inclusion on the tray has no effect upon the remainder, which is equal to
the bag fruit tea, espresso, cappucino.
[X ]
items : seq X → bag X
∀ s : seq X ; x : X •
(items s) ] x = #(s . {x})
Example 9.16 Another tray is brought over with the remaining drinks; these
are placed on the table in front of our friends. The order in which the drinks
are placed upon the table is recorded in the following sequence,
but only the count of each drink is important. The group at the table will be
content, as items arrive = drinks.
Chapter 10
Free Types
The set of all natural numbers, N, is already part of our mathematical language;
it has been defined as a subset of the built-in type Z. However, the construction
of a set similar to N will prove a useful illustration of the properties of a free
type. Thus we attempt to define such a set, beginning with a basic type nat , a
zero element, and a partial function called succ:
zero : nat
succ : nat →
7 nat
∀ n : nat • n = zero ∨ ∃ m : nat • n = succ m
Every element n is either the constant zero or the result of applying the suc-
cessor function succ to an element m.
134 10 / Free Types
succ
(a)
zero
succ
(b)
zero
succ
(c)
zero
succ
(d)
zero
Figure 10.1 Four sets that are not the natural numbers
zero : nat
succ : nat →
7 nat
∀ n : nat • n = zero ∨ ∃ m : nat • n = succ m
{zero} ∩ ran succ = ∅
This definition fails to exclude the set shown in Figure 10.1(b): it is quite pos-
sible to have elements of nat that have no successor. Having realised that the
function used to construct the natural numbers must be total, we try again:
10.2 / Free type definitions 135
zero : nat
succ : nat → nat
∀ n : nat • n = zero ∨ ∃ m : nat • n = succ m
{zero} ∩ ran succ = ∅
And still this is not enough. We have failed to exclude the possibility that some
element is the successor of two or more others: see Figure 10.1(c). There is
nothing that allows us to conclude that nat is an infinite set.
We must thus add a fourth requirement: that the function used to con-
struct the natural numbers is injective. This leads us to the following definition:
zero : nat
succ : nat ) nat
{zero} ∩ ran succ = ∅
{zero} ∪ ran succ = nat
With this, we are guaranteed an infinite set with the familiar structure of the
natural numbers. There is one more requirement to consider; the above defini-
tion fails to exclude the set shown in Figure 10.1(d). The set nat must contain a
copy of the natural numbers N, or at least a set with exactly the same structure,
but it may contain more besides. The final requirement is that nat should be
the smallest set that meets the conditions laid down above.
Our mathematical language has a special mechanism for introducing sets such
as nat : the free type definition. To begin with, consider the special case in
which the set to be introduced has a small, finite number of elements. An
example might be the set of colours of the rainbow: red, orange, yellow, green,
blue, indigo, and violet. In the programming language Pascal, this set may be
introduced as an enumerated type:
However, this abbreviation does not define the constants in the set. It not only
fails to introduce the names, it also fails to make them distinct: there is no
guarantee that red is different from green.
136 10 / Free Types
The following free type definition has a different effect; it introduces a set
Colours, and seven distinct constants:
Once this definition has been made, we may infer that Colours is the smallest set
containing the seven distinct elements red, orange, yellow, green, blue, indigo,
and violet . The order in which these elements are introduced is unimportant:
the definition
Example 10.1 The people in charge of Oxford colleges are given a variety of
titles. We may represent this variety as a free type:
From this definition we can conclude that ‘dean’ and ‘warden’ are elements of
the set Titles and that dean ≠ warden. A dean and a warden are quite different
animals.
We may include copies of other sets as part of a free type, using constructor
functions. The notation
introduces a collection of constants, one for each element of the set source.
constructor is an injective function whose target is the set FreeType.
ba = status 0
msc = status 1
dphil = status 2
ma = status 3
We are then free to define the University’s ordering of degrees in terms of the
≤ ordering on 0 . . 3:
Because status is an injection, we can be sure that its inverse is a function, and
hence that status ∼ d is well-defined.
What is more, the source type of a constructor function may refer to the free
type being defined. The result is a recursive type definition: FreeType is defined
in terms of itself.
Example 10.3 The set nat discussed in the previous section could be intro-
duced by the following free type definition:
Every element of nat is either zero or the successor of a natural number, zero
is not a successor, and every element of nat has a unique successor. The set
nat is the smallest set containing the following collection of distinct elements:
zero, succ zero, succ(succ zero), succ(succ(succ zero)), and so on.
Example 10.4 We may define a free type of binary trees, in which every element
is either a leaf or a branching point.
leaf 3 leaf 5
Each leaf contains a number; each branching point joins a pair of sub-trees. For
example, one element of Tree is given by
in which three different leaves are joined together to form the structure pictured
in Figure 10.2.
Example 10.5 The following definition introduces a more complex free type, in
which every element is a tree: a pair whose first component is a natural number
and whose second component is a sequence of trees.
10.2 / Free type definitions 139
c1 : T
..
.
cm : T
d1 : E1 ) T
..
.
dn : En ) T
disjoint h{c1 }, . . . , {cm }, ran d1 , . . . , ran dn i
∀S : PT •
({c1 , . . . , cm } ∪ d1 (| E1 [S / T ] |) ∪ . . . ∪ dn (| En [S / T ] |)) ⊆ S
⇒ S =T
Such a definition adds two inference rules to a specification. The first states
that the constants are distinct and that the ranges are disjoint:
Example 10.6 In the case of nat , we may infer that the constant zero is not the
successor of any natural number,
Example 10.7 From the definition of Tree, we may conclude that leaves and
branches are different objects:
S ⊆T {c1 , . . . , cm } ∪ d1 (| E1 [S / T ] |) ∪ . . . ∪ dn (| En [S / T ] |) ⊆ S
S =T
Any subset of T that contains all of the constants and is closed under the con-
structors must be the whole of T . A set S is closed under d and E if the image
of E [S / T ] under d is within S itself.
Example 10.8 The free type definition of nat can be used to justify the follow-
ing assertion:
Any subset of nat which contains zero and is closed under succ must be equal
to nat itself.
The second inference rule above can be rewritten to match the induction prin-
ciples given in the previous chapter. Suppose that P is a predicate upon ele-
ments of a free type T , and define the characteristic set
S == {t : T | P t }
that is, S is the set of elements of T that satisfy P . Since S is a subset of T , the
inference rule gives us that
({c1 , . . . , cm } ∪ d1 (| E1 [S / T ] |) ∪ . . . dn (| En [S / T ] |)) ⊆ S
S =T
We may use properties of the union and subset operators to separate the ante-
cedent part of the rule into a list of inequalities. Furthermore, Each of the
expressions involving ⊆ may be rewritten:
di (| Ei [S / T ] |) ⊆ S
a Ei [S / T ] ⊆ di∼ (| S |)
a ∀ e : Ei [S / T ] • e ∈ di∼ (| S |)
a ∀ e : Ei [S / T ] • di e ∈ S
P c1 ... P cm
∀ e : E1 [S / T ] • P (d1 e)
... ∀ e : En [S / T ] • P (dn e)
[induction principle]
∀t : T • P t
Example 10.9 The definition of nat involved a single constant zero and a single
constructor function succ:
This type has a single constant and a single constructor function; the definition
yields an inference rule of the form
In this instance: the constant c is zero; the constructor function d is succ; and
the expression E is nat itself.
If P is a predicate on nat , we may take S to be the characteristic set
S == {n : nat | P n}
P zero
∀ m : nat • P m ⇒ P (succ m)
∀ n : nat • P n
To establish that P holds of every natural number, we must show that it holds
for zero and that it is preserved by the successor function: if it is true of m,
then it is also true of succ m.
Example 10.10 The definition of Tree in Example 10.4 leads to the following
142 10 / Free Types
induction principle:
∀ n : N • P (leaf n)
∀ t1 , t2 : Tree •
P t1 ∧ P t2 ⇒ P (branch (t1 , t2 ))
∀ t : Tree • P t
To show that a property P is true of all trees, we must show that it holds for
any leaf, whatever value is stored there. We must show also that the property is
preserved when trees are joined using branch: if it is true of both components,
then it is true of their combination.
A function defined upon the elements of a free type may have a number of
different parts: one for each clause in the type definition. For example, suppose
that f is a function upon elements of a free type T , introduced by
T ::= c | dhhE ii
There may be two parts to the definition of f : one explaining the effect of f
upon constant c, the other explaining the effect of f upon an element of the set
d(| E |).
If the expression E contains a copy of the free type T , the function defin-
ition will be recursive. It will describe the result of applying f to an element of
d(| E |) in terms of the result of applying f to one or more components.
Example 10.11 We may define a function fact upon nat by giving a case for
zero and a case for successors:
fact 0 = 1
fact (succ n) = (n + 1) ∗ (fact n)
The fact that such functions are well defined follows from a recursion
principle for the type in question. In the case of the natural numbers, this
principle may be stated as follows: for every value k and operator g, there is a
unique total function f from the natural numbers such that
f 0=k
f (n + 1) = g(n + 1)(f n)
10.4 / Primitive recursion 143
This may be used to justify the use of recursively defined functions upon N. A
similar principle applies to arbitrary free types, provided that their definitions
are consistent, a condition discussed in the next section.
Example 10.12 We may define a function flatten that takes a binary tree and
produces a sequence by traversing the tree from left to right.
In both cases, the fact that the given equations define a unique function is a
consequence of the recursion principle for binary trees.
Example 10.13 Using the induction principle for binary trees, we may prove
that flipping and flattening is the same as flattening and then reversing. To be
more precise, if we apply flip to a binary tree and then flatten the result, we
obtain the same sequence that appears when we flatten the tree and apply the
reverse function. Formally,
and then check that the base case is valid. For any natural number n,
dn ∈ Ne[1]
[equational reasoning]
(flip o9 flatten) leaf n =
(flatten o9 reverse) leaf n
[definition of P ]
P (leaf n)
[∀−intro[1] ]
∀ n : N • P (leaf n)
The equality that we were trying to prove now follows immediately, with a single
application of the induction principle for Tree.
10.5 Consistency
T ::= dhhP T ii
E1 , . . . , En
involves only Cartesian products, finite power sets, finite functions, and finite
sequences.
Schemas
In the Z notation there are two languages: the mathematical language and the
schema language. The mathematical language is used to describe various as-
pects of a design: objects, and the relationships between them. The schema
language is used to structure and compose descriptions: collating pieces of
information, encapsulating them, and naming them for re-use.
Re-usability is vital to the successful application of a formal technique. By
identifying and sharing common components, we keep our descriptions both
flexible and manageable. In the schema language, we see specifications shar-
ing parts, proofs sharing arguments, theories sharing abstractions, problems
sharing common aspects.
We believe that the use of schemas helps to promote a good specification
style. However, as with any notation, the language of schemas requires careful
and judicious application if it is not to be abused. We should take care to
develop simple theories and to use schemas to present them in an elegant and
comprehensible fashion.
This chapter is an informal introduction to schemas: their appearance,
and the information they contain. We see how they may be used as types, as
declarations, and as predicates. In subsequent chapters, we present a language
of schema operators, and show how schemas may be used in reasoning about
formal descriptions.
Example 11.1 The set comprehension term, lambda expression, and quantified
predicates below each exhibit this pattern of introduction and constraint:
{m, n : N | n = 2 × m • m , n}
∀ x, y : N | x ≠ y • x > y ∨ y > x
∃z : N | z ≠ 1 • z < 2
Example 11.2 A concert hall uses a software system to keep track of bookings
for performances. Inside the hall is a certain amount of seating, some or all
of which may be made available to customers for a given performance. At this
level of abstraction, we have no need to consider the representation of seats
and customers, so we introduce them as given sets:
[Seat , Customer ]
The box office maintains a record of which seats have been sold, and to whom.
This relationship should be functional: that is, no seat can be sold to two dif-
ferent customers:
sold ∈ Seat →
7 Customer
To allow for the possibility that seats may be added to or removed from the hall,
we introduce a set seating, a subset of Seat , to represent the seating allocated
for the performance.
11.1 / The schema 149
It should not be possible to book seating that has not been allocated; the
following predicate should be true at all times
That is, the domain of sold should be a subset of seating. This property, to-
gether with the declarations of sold and seating, forms a schema which we shall
call BoxOffice.
[declaration | predicate]
or vertically
declaration
predicate
In the horizontal form, the declaration and predicate are separated by a vertical
bar, and the schema text is delimited by brackets. In the vertical form, the
declaration and predicate are separated by a horizontal bar, and the schema
text is delimited by a broken box.
Example 11.3 We can write the box office schema in horizontal form, as
or in vertical form, as
In the declaration part of a schema, the order in which variables are in-
troduced is unimportant. In the above example, it would make no difference if
sold were to be declared before seating.
150 11 / Schemas
b [declaration | predicate]
Name =
Name
declaration
predicate
Example 11.4 We can name the box office schema text by writing
or by writing
BoxOffice
seating : P Seat ; sold : Seat →
7 Customer
dom sold ⊆ seating
Two schemas are equivalent if they introduce the same variables, and place
the same constraints upon them. When considering equivalence, remember
that some constraints may be hidden in the declaration part.
Example 11.5 The declaration part of the box office schema includes the con-
straint that the relation sold between Seat and Customer must be functional.
The following schema, in which this constraint appears as part of the predicate,
is entirely equivalent:
11.1 / The schema 151
a⇒b
c∨d
∃y : T •
x<y ∨
y <x
Example 11.6 The schema of Example 11.5 could be written in the following
form:
seating : P Seat
sold : Seat ↔ Customer
dom sold ⊆ seating
sold ∈ Seat →
7 Customer
Example 11.7 The following schema text has a single component, a set of seats
called stalls, with no constraints:
stalls : P Seat
stalls : P Seat
true
152 11 / Schemas
SchemaOne
a:Z
c : PZ
We may introduce elements of this type in the usual way: the declaration s :
SchemaOne introduces an object s of schema type SchemaOne.
To write an object of schema type in extension, we list the component
names and the values to which they are bound. This requires a new piece of
notation:
is a binding in which a is bound to 2 and c is bound to the set {1, 2, 3}. The
schema type SchemaOne is the set of all bindings in which a and c are bound
to an integer and a set of integers, respectively.
The schema BoxOffice appears as a subrange type; it describes only those bind-
ings in which sold is a partial function, and the domain of sold is a subset of
seating.
Month ::= jan | feb | mar | apr | may | jun | jul | aug | sep |
oct | nov | dec
11.2 / Schemas as types 153
Date
month : Month
day : 1 . . 31
month ∈ {sep, apr , jun, nov} ⇒ day ≤ 30
month = feb ⇒ day ≤ 29
A binding h|month m, day d|i is a valid date provided that there are at
least d days in month m.
Frame
source, destination : Address
data : Data
where Address is the set of all addresses in the network, and Data is the set
of all possible data components. If f is an object of type Frame, then we write
f .source to denote its source, f .destination to denote its destination, and f .data
to denote its data component.
Following such a declaration, we may refer to name of the month in which Fleur
was born as fleur’s birthday.month, and to the day on which she was born as
fleur’s birthday.day.
154 11 / Schemas
SchemaTwo
a:Z
c : PZ
a∈c∧c≠∅
{ SchemaTwo | a = 0 • c }
The same effect could be achieved by replacing SchemaTwo with a list of de-
clarations and a constraint:
{ a : Z; c : P Z | a ∈ c ∧ c ≠ ∅ ∧ a = 0 • c }
Example 11.12 If Date is the schema named in Example 11.9, then the set com-
prehension
11.3 / Schemas as declarations 155
If a set comprehension has no term part, then the type of objects in the
set depends upon the characteristic tuple of the declaration. For example, in
the set
{ a : Z; c : P Z | a ∈ c ∧ a = 0 }
the characteristic tuple is the pair (a, c), and the type of objects in the set is
Z × P Z. The set consists of every pair (a, c) that meets the stated constraint.
In a schema, the order in which components are declared is unimportant,
so the characteristic tuple of
{ SchemaTwo | a = 0 }
is quite different from (a, c). A typical element of this set is a binding associat-
ing a with 0 and c with some set containing 0; the characteristic tuple has one
component: the binding h|a a, c c|i.
This is a binding in which component a is bound to the value of variable
a, and component c is bound to the value of variable c:
variable value
z}|{
h| a a , |{z}
c c |i
component name
Whenever this expression is used, variables a and c must already have been
declared. Furthermore, the types of these variables must match those given in
the declaration part of SchemaTwo, although any predicate information in the
schema is ignored.
Example 11.13 If Date is the schema named in Example 11.9, then θDate de-
notes the characteristic binding
Whenever this expression is used, variables month and day must be in scope,
and their types must match those given in the declaration part of Date.
156 11 / Schemas
There is a close relationship between our use of schemas as types and our
use of the θ notation. As an illustration of this, consider the schema
SchemaThree
a:Z
c : PZ
c≠∅∧a∈c
c ⊆ {0, 1}
This introduces the same variables as SchemaOne and SchemaTwo, but under
a more restrictive set of constraints.
If we use SchemaThree to describe a set of bindings, then this set will have
exactly four elements:
a : { S • θS }
Example 11.14 When used in a variable declaration, the schema name Date
represents the set of all valid dates: bindings in which there are at least day
days in month month. This set of bindings could also be written as a set com-
prehension:
{ Date • θDate }
Notice that, although θDate is the default term of this set comprehension, we
include it here to avoid confusion. It is not immediately obvious that the ex-
pression {Date} denotes a set comprehension.
11.3 / Schemas as declarations 157
FunctionOne == ( λ SchemaOne • a 2 )
introduces a function defined upon objects of schema type, mapping any bind-
ing of a and c to the square of the value of a. For example,
As in Section 8.2, the source type of a lambda expression is given by the char-
acteristic tuple of the declaration. In this case, the characteristic tuple has a
single component: θSchemaOne.
Example 11.15 An object of schema type BoxOffice has two components: a set
of seats allocated for the performance, and a record of which seats have been
sold, and to whom. Given such an object, we may be interested in the set of
seats that have yet to be sold. The function
∃ SchemaTwo • a = 0 a ∃ a : Z; c : P Z | c ≠ ∅ ∧ a ∈ c • a = 0
In such expressions, the order in which the components are declared is unim-
portant: there are no characteristic tuples to consider.
These expressions may be used to make statements about objects of the
corresponding schema type. For example, the following predicate states that,
for any object of type SchemaOne, the a component must be an element of the
c component:
∀ SchemaOne • a ∈ c
This predicate is false, as the binding h|a 1, c {2, 3}|i is an object of type
SchemaOne. On the other hand, the predicate
∀ SchemaTwo • a ∈ c
158 11 / Schemas
is true, as any binding of subrange type SchemaTwo must satisfy precisely this
constraint.
Example 11.16 The following predicate states that there is an object of schema
type Date such that the value of month is feb and the value of day is 29:
Example 11.17 The following predicate states that, for any object of schema
type Date, the value of day must be less than or equal to 30:
∀ Date • day ≤ 30
This is false, as the binding h|month mar , day 31|i satisfies the predicate
part of Date. March is a month with 31 days.
∀ SchemaTwo • a ∈ c
is logically equivalent to
Both predicates insist that there is a binding of type SchemaTwo such that a is
an element of c.
∀ a : Z; c : P Z | SchemaThree • SchemaTwo
11.4 / Schemas as predicates 159
∀ a : Z; c : P Z | c 6∈ ∅ ∧ a ∈ c ∧ c ⊆ {0, 1} • c 6∈ ∅ ∧ a ∈ c
FromA
source, destination : Address
data : Data
source = A
SchemaFour
a:N
c : PN
a∈c∧c≠∅
but this is not the case: an additional constraint upon a and c has been imposed
by the declarations a : N and c : P N.
To avoid confusion, we may choose to rewrite a schema so that all of the
constraint information appears in the predicate part. This process is called
normalisation; the declaration part has been reduced to a unique, canonical
form. For example, the above schema may be rewritten as
SchemaFourNormalised
a:Z
c : PZ
a∈N
c ∈ PN
a∈c∧c≠∅
160 11 / Schemas
It is now obvious that this schema contains strictly more information than
SchemaTwo. When used as a predicate, it will insist that a, and every element
of c, is greater than or equal to 0.
Example 11.19 At first glance, it might seem that any set seating of seats, and
any relation sold between seats and customers, such that
However, this is not the case. The declaration part of BoxOffice includes the
additional requirement that sold is a partial function.
Example 11.20 The schema Date introduces two variables, month and day, in
such a way that they correspond to a date in the Gregorian calendar. Using this
schema as a predicate, we can show that
The necessary constraint upon the range of day is included in the declaration
part of the schema. If we consider the normalised form of Date,
DateNormalised
month : Month
day : Z
day ∈ 1 . . 31
month ∈ {sep, apr , jun, nov} ⇒ day ≤ 30
month = feb ⇒ day ≤ 29
11.5 Renaming
Schema[new/old]
11.5 / Renaming 161
to denote the schema obtained from Schema by replacing component name old
with new. For example, we might wish to introduce variables q and s under the
constraint of SchemaTwo: the schema
SchemaTwo[q/a, s/c]
q:Z
s : PZ
s≠∅∧q∈s
Example 11.21 The variables start month and start day represent the month
and the day on which a contract of employment is due to start. The requirement
that this should be a valid date can be encapsulated by an appropriate renaming
of the schema Date:
StartDate
start month : Month
start day : 1 . . 31
start month ∈ {sep, apr , jun, nov} ⇒ start day ≤ 30
start month = feb ⇒ start day ≤ 29
The types of the variables, and the constraints upon them, are unaffected by
the renaming operation.
SchemaOne[q/a, s/c]
Example 11.22 We may use another renaming to describe the set of all valid
finish dates for our contract:
FinishDate =
b Date[finish month/month, finish day/day]
A start date and a finish date are quite different objects, although each has a
component of type Month and another of type Z. If s ∈ StartDate and f ∈
FinishDate, then the value of s = f is undefined: these are variables of different
types. However, it still makes sense to state that
SchemaFive[X ]
a:X
c : PX
a∈c
a:Z
c : PZ
a∈c
System[Items, Client ]
seating : P Items
sold : Items →7 Client
dom sold ⊆ seating
We may obtain the familiar BoxOffice system by instantiating Items with Seat
and Client with Customer .
Chapter 12
Schema Operators
In this chapter we see how the information contained in schemas may be com-
bined in a variety of different ways. We introduce a language of logical schema
operators: conjunction, disjunction, negation, quantification, and composition.
To illustrate the use of this language, we explain how schemas may be used to
describe the behaviour of a computing system.
This application of the schema language revolves around the concept of
an abstract data type: a collection of variables, and a list of operations that may
change their values. We encapsulate these variables within a schema, so that
an object of the corresponding schema type represents a state of the system.
An operation that affects the state can be seen as a relation upon objects
of schema type: bindings of the state variables. The schema notation provides
a convenient way of describing such a relation: we may use a schema to express
the relationship between the state before and the state after an operation.
12.1 Conjunction
S T
a:A b:B
b:B c:C
P Q
a:A
b:B
c:C
P ∧Q
If the same variable is declared in both schemas, as with b above, then the types
must match, or the schema S ∧ T will be undefined.
The result of a schema conjunction is a schema that introduces both sets
of variables and imposes both constraints. Schema conjunction allows us to
specify different aspects of a specification separately, and then combine them
to form a complete description. This makes for simple, well-structured descrip-
tions, in which each individual component can be easily understood.
Friends
friends : P Customer
status : Status
sold : Seat →7 Customer
status = premiere ⇒ ran sold ⊆ friends
If the current performance is a premiere, then seats may be sold only to friends
of the theatre.
To describe the enhanced box office, we have only to conjoin this schema
12.1 / Conjunction 167
FriendlyBoxOffice =
b BoxOffice ∧ Friends
FriendlyBoxOffice
status : Status
friends : P Customer
sold : Seat →7 Customer
seating : P Seat
dom sold ⊆ seating
status = premiere ⇒ ran sold ⊆ friends
In such a small example, there is little to be gained from the separate description
of different features. In larger, more realistic examples, such a separation of
concerns is essential if we are not to be overwhelmed by complexity.
Example 12.2 The friendly box office could have been introduced by including
BoxOffice in the declaration part of a schema:
FriendlyBoxOffice
BoxOffice
status : Status
friends : P Customer
status = premiere ⇒ ran sold ⊆ friends
FriendlyBoxOffice
BoxOffice
Friends
168 12 / Schema Operators
EnhancedBoxOffice
BoxOffice
available : N
available = free θBoxOffice
To obtain the number of seats available, we have only to apply free to the bind-
ing θBoxOffice, representing the values of seating and sold.
12.2 Decoration
Suppose that the state of a system is modelled by a schema State with two
components a and b, and that these are introduced under a constraint P .
State
a:A
b:B
P
Each object of schema type State represents a valid state: a binding of a and b
in which predicate P is satisfied. We say that P forms part of the state invariant
for the system: a constraint that must always be satisfied.
Example 12.4 The set of all valid states of our box office system is described
by the schema type BoxOffice, where
BoxOffice
seating : P Seat
sold : Seat →
7 Customer
dom sold ⊆ seating
Each state is a binding of variables seating and sold; the state invariant insists
that only allocated seats are sold, and that the relationship between seats and
customers remains functional.
12.2 / Decoration 169
To describe an operation upon the state, we use two copies of State: one
representing the state before the operation; the other representing the state
afterwards. To distinguish between the two, we decorate the components of
the second schema, adding a single prime to each name: that is,
State0
a0 : A
b0 : B
P [a 0 /a, b 0 /b]
The predicate part of the schema is modified to reflect the new names of the
state variables.
Example 12.5 To describe the state of the box office system after some opera-
tion, we could use the following schema:
BoxOffice0
seating 0 : P Seat
sold 0 : Seat →
7 Customer
dom sold 0 ⊆ seating 0
This introduces two variables, seating 0 and sold 0 , corresponding to the seat
seating and the sales record after the operation has been performed.
Operation
State
State0
...
This is a schema with four components, two of them primed. The inclusion of
State and State0 indicates that a and b constitute a valid state of the system,
and that the same is true of a 0 and b 0 .
The predicate part of such a schema characterises the operation: it de-
scribes its effect upon the values of the state variables; it states what must
be true of the state if the effect of the operation is to be fully defined. In the
above example, we would expect the predicate part of Operation to include free
occurrences of a, a 0 , b and b 0 .
170 12 / Schema Operators
Example 12.6 One operation upon the state of the box office system is the
purchasing of a single seat for the current performance. Suppose that this seat
is denoted by s?, and that the customer buying it is c?. If the operation is to be
a success, then s? must be available for sale beforehand:
Afterwards, the sold relation should be modified to indicate that s? has been
sold to customer c?:
Finally, the collection of seats allocated for this performance should be un-
changed by the operation.
We may encapsulate all of this information in a single schema Purchase0 ,
representing the successful purchasing of seat s? by customer c?:
Purchase0
BoxOffice
BoxOffice0
...
s? ∈ seating \ dom sold
sold 0 = sold ∪ {s? , c?}
seating 0 = seating
Some operations, such as the one described in the example above, involve
either input to the system or output from it. To model such operations, we
include additional components in the declaration part of the operation schema.
The predicate part can then relate the values of these components to the states
before and after the operation. For example, the schema
Operation
State
State0
i? : I
o! : O
...
Example 12.7 The operation of purchasing a seat requires two inputs: the
name of the seat, and the name of the customer. We model these as two input
components s? and c?, of types Seat and Customer , respectively. The operation
of successfully purchasing a seat is described by
Purchase0
BoxOffice
BoxOffice0
s? : Seat
c? : Customer
s? ∈ seating \ dom sold
sold 0 = sold ∪ {s? , c?}
seating 0 = seating
The effect of this operation is defined only when input s?, the seat requested,
is available for sale.
Example 12.8 We may add an output to our description of the purchasing op-
eration, corresponding to the response offered to the customer. This response
will be drawn from a free type of responses:
Success
r ! : Response
r ! = okay
Purchase0 ∧ Success
This produces a schema with two inputs and a single output r !, whose value
will be okay.
172 12 / Schema Operators
There is a convention for including two copies of the same schema, one of
them decorated with a prime. If Schema describes the state of a system, then
∆Schema is a schema including both Schema and Schema 0 : that is,
∆Schema
Schema
Schema 0
Purchase0
∆BoxOffice
...
...
Two bindings are equal if they bind their component names to equivalent
values: the two bindings above will be equal if and only if a = a 0 and c = c 0 .
Equating decorated and undecorated bindings of State is thus a way of insisting
that the before state is equal to the after state: each component has been left
with the same value.
Again, there is a convention: we write ΞSchema to denote the schema that
includes Schema and Schema 0 and equates their bindings:
ΞSchema
∆Schema
θSchema = θSchema 0
Example 12.10 We may interrogate the box office system to determine the
number of seats that are still available for the current performance. This oper-
ation may be described by
QueryAvailability
ΞBoxOffice
available! : N
available! = free θBoxOffice
StateInit
State0
...
The predicate part of this schema describes the initial constraints upon the
components of the state.
174 12 / Schema Operators
Example 12.11 If we assume that the initial allocation of seats for the current
performance has been declared as a global variable,
then the initial state of the box office system is described by the following
decorated schema:
BoxOfficeInit
BoxOffice0
seating 0 = initial allocation
sold 0 = ∅
12.3 Disjunction
S T
a:A b:B
b:B c:C
P Q
a:A
b:B
c:C
P ∨Q
As with conjunction, the declaration parts are merged. This time, however, the
predicate parts are disjoined.
If the same variable is declared in both schemas, as with b above, then the
types must match. Any constraint information that is present in just one of the
declarations should be moved into the predicate part before combination: for
12.3 / Disjunction 175
example, if set A in the above example is a proper subset of type T , then the
constraint a ∈ A should be conjoined with P , and the declaration a : A should
be replaced with a : T .
Example 12.12 If a customer attempts to purchase a seat from the box office,
but the seat requested is not available, then the schema Purchase0 does not
apply. To specify what happens in this case, we introduce another schema:
NotAvailable
ΞBoxOffice
s? : Seat
s? 6∈ seating \ dom sold
This schema applies whenever the seat requested has not been allocated, or
has already been sold. The inclusion of ΞBoxOffice confirms that the state of
the system is unchanged.
To indicate that the seat could not be sold, we specify a different response
for the customer:
Failure
r ! : Response
r ! = sorry
b (Purchase0 ∧ Success)
Purchase =
∨
(NotAvailable ∧ Failure)
If the seat requested is available, then the effect of the operation is described
by the first disjunct; if it is not, then the second disjunct applies.
Example 12.13 Having purchased a seat, a customer may decide not to attend
the performance. In this case, they may return the seat to the box office. The
operation of successfully returning a seat is described by
Return0
∆BoxOffice
s? : Seat
c? : Customer
s? , c? ∈ sold
sold 0 = sold \ {s? , c?}
seating 0 = seating
This is a partial operation upon the state of the box office. Its effect is defined
only if the seat has been sold to the customer in question: that is, if
s? , c? ∈ sold
If this seat has not been sold, or if it has been sold to another customer, then the
schema tells us nothing about the consequences of this customer attempting
to return it to the box office. The state after this attempt could be any state of
the system.
To model the effect of an unsuccessful attempt, we introduce another
partial operation schema. This will apply only if the seat has not been sold to
the customer in question:
NotPossible
ΞBoxOffice
s? : Seat
c? : Customer
s? , c? 6∈ sold
The inclusion of ΞBoxOffice tells us that the state of the box office system is not
changed by this operation.
We may combine these partial operations using schema disjunction. The
result is a total operation upon the box office state:
This describes the effect of attempting to return a seat whatever the current
state of the box office.
12.4 / Negation 177
12.4 Negation
The negation of a schema introduces the same set of components under a neg-
ated constraint. If S is a normalised schema, then its negation ¬S may be
obtained by negating the predicate part. For example, if A and B are types and
S is introduced by
S
a:A
b:B
P
a:A
b:B
¬P
SchemaFour
a:N
c : PN
a∈c∧c≠∅
This schema has not been normalised: the declaration part includes the con-
straint that a ∈ N and c ∈ P N. The negation ¬SchemaFour has a different
declaration part:
a:Z
c : PZ
¬(a ∈ N ∧ c ∈ P N ∧ a ∈ c ∧ c ≠ ∅)
SchemaFourNormalised
a:Z
c : PZ
a ∈ N ∧ c ∈ PN ∧ a ∈ c ∧ c ≠ ∅
Example 12.14 The schema Date was used to characterise the set of all valid
dates in the Gregorian calendar. Its negation, ¬Date, describes the set of all
bindings of day and month that do not correspond to a valid date:
month : Month
day : Z
day 6∈ 1 . . 31 ∨
(month ∈ {sep, apr , jun, nov} ∧ day > 30) ∨
(month = feb ∧ day > 29)
Example 12.15 If the box office is no longer in the initial state, then its state
may be characterised by the conjunction
BoxOffice0 ∧ (¬BoxOfficeInit )
Notice that it is not enough to simply negate the initialisation schema. The
schema ¬BoxOfficeInit describes the set of all bindings of seating and sold that
do not match the initial state: this includes bindings that are not valid states
of the system.
We may quantify over some of the components of a schema while retaining the
declarations of the others. If Q is a quantifier and dec is a declaration, then the
quantified schema
Q dec • Schema
may be obtained from Schema by removing those components that are also
declared in dec and quantifying them with Q in the predicate part. For this
12.5 / Quantification and hiding 179
S
a:A
b:B
P
a:A
∀b : B • P
a:A
∃b : B • P
Example 12.16 The friendly box office records the status of the current per-
formance: if the show is a premiere, then seats are sold only to customers who
are registered friends of the theatre:
FriendlyBoxOffice
status : Status
friends : P Customer
sold : Seat →7 Customer
seating : P Seat
dom sold ⊆ seating
status = premiere ⇒ ran sold ⊆ friends
∀ status : Status •
dom sold ⊆ seating
status = premiere ⇒ ran sold ⊆ friends
CautiousBoxOffice
friends : P Customer
sold : Seat →7 Customer
seating : P Seat
dom sold ⊆ seating
ran sold ⊆ friends
Example 12.17 The operation of successfully returning a ticket to the box of-
fice required the name of a customer. We may dispense with this requirement
by existentially quantifying over the input component c?. The result is an an-
onymous version of the return operation:
∃ c? : Customer • Return0
∃ c? : Customer •
s? , c? ∈ sold ∧
sold 0 = sold \ {s? , c?} ∧
seating 0 = seating
12.5 / Quantification and hiding 181
With care, we may rewrite this predicate to make the results of the operation
more obvious:
∆BoxOffice
s? : Seat
∃ c? : Customer •
s? , c? ∈ sold ∧
sold 0 = sold \ {s? , c?} ∧
seating 0 = seating
AnonymousReturn0
∆BoxOffice
s? : Seat
s? ∈ dom sold
sold 0 = {s?} sold
seating 0 = seating
For this operation to be properly defined, it is necessary only that the input s?
is an element of the domain of sold.
S
a:A
b:B
P
b:B
∃a : A • P
Thus hiding is no more than a quick way of writing (and pronouncing) existential
quantification over schema components.
Example 12.18 The enhanced box office system included a component that
recorded the number of seats available for the current performance. We may
abstract away this information by hiding available within the schema:
EnhancedBoxOffice \ (available)
BoxOffice
∃ available : N • available = free θBoxOffice
Since free is a total function, the number of seats available is always uniquely
determined by the values of seating and sold. This schema is equivalent to the
original box office description.
12.6 Composition
The relationship between the before state and the intermediate state—θState
and θState00 —is described by schema OpOne; the relationship between the
intermediate state and the after state—θState00 and θState0 —is described by
schema OpTwo.
For the composition to be defined, both schemas must refer to the same
state. For any primed component in OpOne, there must be an unprimed com-
ponent of the same name in OpTwo. For example, suppose that OpOne and
OpTwo are introduced by
OpOne OpTwo
a, a 0 : A a, a 0 : A
b, b 0 : B b, b 0 : B
P Q
The state components in each operation are the same, so their schema compos-
ition will be well defined.
The composition of OpOne and OpTwo may be calculated using schema
existential quantification, as above, or by renaming the state components cor-
responding to the intermediate state:
OpOne o9 OpTwo =
(OpOne[a 00 /a 0 , b 00 /b 0 ] ∧ OpTwo[a 00 /a, b 00 /b]) \ (a 00 , b 00 )
The components representing the intermediate state, a 00 and b 00 are then hid-
den. If we were to expand this schema, we would see that the composition is
equivalent to:
a, a 0 : A
b, b 0 : B
∃ a 00 , b 00 •
P [a 00 /a 0 , b 00 /b 0 ] ∧ Q [a 00 /a, b 00 /b]
184 12 / Schema Operators
Purchase0 o9 Return
where Purchase0 and Return are as defined above. The result that we might
hope to establish can be expressed as
Purchase0 o9 Return
ΞBoxOffice
Promotion
Large software systems often contain multiple, indexed instances of the same
component. A database may contain a number of records, a computer system
may have several users, a data network may consist of a number of switching
nodes. If this is the case, then there will exist a uniform relationship between
the system state and the state of each indexed component.
This relationship allows us to link certain changes in system state to
changes in the state of indexed components. We may factor a global oper-
ation into a local operation and a mixed operation, the latter expressing the
relationship between local and global state. This is a useful separation of con-
cerns; the two factors may be specified and analysed in isolation. We have used
the structuring information in the design of the system to simplify our formal
description.
186 13 / Promotion
global score
local score
Example 13.1 In the game of Trivial PursuitTM , the players collect tokens of
various colours—red, green, yellow, blue, brown, and pink—the aim being to
collect one token of each colour. There are no teams: each player maintains an
individual score. A player’s score may be modelled using a schema type
LocalScore
s : P Colour
GlobalScore
score : Players →
7 LocalScore
Here, a partial function called score associates each player with an object of
type LocalScore. Figure 13.1 shows a situation in which one of the players has
collected exactly two tokens.
13.1 / Factoring operations 187
Players are awarded tokens if and when they provide correct answers to
questions on various subjects; the colour awarded depends upon the choice of
subject. If a player p? earns a token of colour c?, then the effect upon the state
of play is described by the following operation schema:
AnswerGlobal
∆GlobalScore
p? : Player
c? : Colour
p? ∈ dom score
{p?} score0 = {p?} score
(score0 p?).s = (score p?).s ∪ {c?}
Provided that p? is indeed part of the current game, the function score is up-
dated to reflect the new score associated with p?.
An alternative approach would involve factoring this operation into a local
operation—
AnswerLocal
∆LocalScore
c? : Colour
s 0 = s ∪ {c?}
—and a schema expressing the relationship between global and local states—
Promote
∆GlobalScore
∆LocalScore
p? : Player
p? ∈ dom score
θLocalScore = score p?
score0 = score ⊕ {p? , θLocalScore0 }
—in which a change in GlobalScore and a change in LocalScore are linked by the
identity of the player involved.
If we conjoin the AnswerLocal and Promote schemas, then we obtain a
schema that describes an operation upon the global state:
The local state is uniquely determined by the function score, so there is no need
to record this information at the global level. The existential quantification
hides it, yielding a predicate part
∃ ∆LocalScore •
p? ∈ dom score
θLocalScore = score p?
score0 = score ⊕ {p? , θLocalScore0 }
s 0 = s ∪ {c?}
∃ s, s 0 : P Colour •
p? ∈ dom score
h|s s|i = score p?
score0 = score ⊕ {p? , h|s s 0 |i}
s 0 = s ∪ {c?}
and hence as
∃ s : P Colour •
p? ∈ dom score
(score p?).s = s
score0 = score ⊕ {p? , h|s s ∪ {c?}|i}
∆GlobalScore
p? : Player
c? : Colour
p? ∈ dom score
{p?} score0 = {p?} score
(score0 p?).s = (score p?).s ∪ {c?}
In the above example, there is little to choose between the two approaches,
although the factored description makes it easier to see the effect of the op-
eration upon the local state. However, as we define more operations, and add
more information to the local state, the advantages of the factored approach
become obvious.
13.1 / Factoring operations 189
The relationship between the global state and the collection of local states
need not be functional. There may be several components with the same index,
in which case the association between indices and components can be modelled
as a relation.
MailSystem
address : User ↔ Address
mailbox : Address →
7 MailBox
MailBox
mail : seq Message
new mail, last read : TimeStamp
Of these, new mail records the time of arrival of the latest mail message, and
last read records the last time that mail in the box was read.
A typical object of type MailBox might be
h| mail hm1 , m2 , m3 i , ,
new mail Tue 14 Feb, 11.00 a.m. ,
last read Sun 12 Feb, 12.30 p.m. |i
This tells us that the box holds three messages—m1 , m2 , and m3 —the last of
which arrived at 11.00 a.m. on Tuesday 14th February. It states also that mail
in this box was last read at 12.30 p.m. on Sunday 12th February.
190 13 / Promotion
admin
Carolyn
carolyn
Denise denise
edward
Edward
edwardc
address mailbox
If a message m? arrives at time t ? for user u?, then it will be added to one
of the mailboxes belonging to u?. These components are taken as inputs to the
following operation schema, which describes the effect upon the global state:
ReceiveSystem
∆MailSystem
u? : User
m? : Message
t ? : TimeStamp
a! : Address
u? , a! ∈ address
address 0 = address
a! ∈ dom mailbox
{a!} mailbox 0 = {a!} mailbox
(mailbox 0 a!).mail = (mailbox a!).mail _ hm?i
(mailbox 0 a!).new mail = t ?
(mailbox 0 a!).last read = (mailbox a!).last read
The address used, a!, is provided as an output to the operation. The value of
address and the contents of the other mailboxes—given by {a!} mailbox—are
left unchanged.
13.1 / Factoring operations 191
Promote
∆MailSystem
∆MailBox
u? : User
a! : Address
u? , a! ∈ address
address 0 = address
a! ∈ dom mailbox
θMailBox = mailbox a!
mailbox 0 = mailbox ⊕ {a! , θMailBox 0 }
The link is made by identifying the user u? and the address a! involved in the
operation. The local state is given by mailbox a!, where u? , a! is an element of
the address relation. This is the only part of the global state that will change;
this is the frame in which the operation will take place.
The second part of the factorisation is a schema that describes the effect
of adding mail to a single mailbox:
ReceiveBox
∆MailBox
m? : Message
t ? : TimeStamp
mail 0 = mail _ hm?i
new mail 0 = t ?
last read 0 = last read
The incoming message is added to the end of the sequence mail, and the
new mail is set to t ?. The other time stamp remains unchanged.
If we conjoin these two schemas, and abstract away the components of
the local state, as in
global state
value
value
local state
Data
value : Value
The state of the array is represented by an object of schema type with a single
component, a sequence of Data elements:
Array
array : seq Data
The relationship between the state of the array—the global state—and the state
of a data element—the local state—is illustrated in Figure 13.3.
If an operation upon the array affects but a single element, then we may
express it as the product of two schemas: a local operation schema and a pro-
motion schema. For example, the operation of assigning a new value to a data
element could be described as
AssignData
∆Data
new? : Value
value0 = new?
and the promotion schema, which makes the link between global and local
states using the index of the data, is introduced by
Promote
∆Array
∆Data
index? : N
index? ∈ dom array
{index?} array = {index?} array 0
array index? = θData
array 0 index? = θData 0
Once again, the promotion schema describes the frame, while the local opera-
tion schema describes the effect.
13.2 Promotion
Example 13.4 A global box office system keeps track of bookings for a number
of performances. The record of seats allocated and tickets sold for each per-
formance is represented by an object of schema type BoxOffice. These objects
are indexed by a function on Performance, the set of all possible performances:
GlobalBoxOffice
announced : P Performance
booking : Performance →
7 BoxOffice
dom booking ⊆ announced
As well as booking, the system maintains a set called announced for reference
purposes. The two components are connected: any performance for which we
are booking must have been announced. On the other hand, some performances
may have been announced but have not yet started booking.
A booking operation is an operation upon the global box office system that
involves the sale or return of a seat. These operations may be factored using
promotion. We define a promotion schema:
Promote
∆GlobalBoxOffice
∆BoxOffice
p? : Performance
p? ∈ dom booking
θBoxOffice = booking p?
θBoxOffice0 = booking 0 p?
{p?} booking 0 = {p?} booking
announced 0 = announced
This tells us the relationship between the local state of a box office system and
the global state in such an operation, given that we are talking about perform-
ance p?. Such local operations do not affect the list of performances that have
been announced.
We may promote the local operation of buying a ticket to a global operation
simply by conjoining the schema above with Purchase:
GlobalPurchase0 =
b ∃ ∆BoxOffice • Purchase ∧ Promote
A single box office has changed—the one identified by input p?—and the effect
of this change is described by the operation schema Purchase.
13.2 / Promotion 195
∆GlobalBoxOffice
p? : Performance
s? : Seat
c? : Customer
r ! : Response
p? ∈ dom booking
( ( s? ∈ booking p?.seating \ dom booking p?.sold ∧
booking p?.sold 0 = booking p?.sold ∪ {s? , c?} ∧
r ! = okay)
∨
( s? 6∈ booking p?.seating \ dom booking p?.sold ∧
booking p?.sold 0 = booking p?.sold ∧
r ! = sorry ) )
{p?} booking 0 = {p?} booking
announced 0 = announced
Example 13.5 The GlobalPurchase0 operation defined above is not total: it de-
scribes only those situations in which the performance in question is already
booking. We may wish to add the following alternative:
NotYetBooking
ΞGlobalBoxOffice
p? : Performance
r ! : Response
p? ∈ announced \ dom booking
r ! = not yet booking
Example 13.6 We may instruct the box office system to start accepting book-
ings for a performance p?:
StartBooking
∆GlobalBoxOffice
p? : Performance
p? ∈ announced
p? 6∈ dom booking
announced 0 = announced
∃ BoxOfficeInit •
booking 0 = booking ∪ {p? , θBoxOffice0 }
A performance cannot start booking unless it has been announced; neither can
it start booking more than once.
That is, provided that the update is possible at all, it is possible for all outcomes
of the local state.
In a free promotion, neither the promotion schema nor the global state
invariant should place any additional constraint upon the component variables
of the local state: that is, any constraint above that provided by the local state
schema itself. Then, and only then, can the quantifiers be exchanged.
To decide whether a given promotion is free, we expand ∃ Global 0 • Promote
and simplify the predicate part of the resulting schema. We should be left with
an expression in which θLocal appears unconstrained, other than by the pre-
dicate part of Local iself.
The exchange of quantifiers can then be justified using schema equivalents
of the quantifier rules. The rule required for existential elimination is
ds ∈ S e[i]
..
.
B[s/θS ] provided that s is not free
[∀−intro[i] ]
∀S • B in the other assumptions
∃ Array 0 • Promote
a [Array; ∆Data; index? : N |
∃ Array 0 •
index? ∈ dom array
array index? = θData
array 0 = array ⊕ {index? , θData 0 }]
Simplifying the predicate part using the one-point rule to eliminate the quanti-
fication of array 0 , we obtain
∃ Array 0 • Promote
a [Array; ∆Data; index? : N |
index? ∈ dom array
array index? = θData
array ⊕ {index? , θData 0 } ∈ seq Data]
Given that index? ∈ dom array, the last of these conditions requires only that
θData 0 ∈ Data. Thus
∃ Array 0 • Promote
a [Array; ∆Data; index? : N |
index? ∈ dom array
array index? = θData
θData 0 ∈ Data]
The predicate above places no constraint on θData 0 , other than the requirement
that it meets the constraint of Data. It is then a simple matter to show that
Example 13.8 We wish to model a stack of data objects, each of which contains
a piece of data and a priority value:
PriData
priority : N
data : Data
The objects in the stack are ordered with respect to their priority values. If
object a has a lower index than object b—if it is nearer the top of the stack—
then it must have a higher priority value:
13.3 / Free and constrained promotion 199
Stack
stack : seq PriData
∀ i, j : dom stack | i < j • (stack i).priority ≥ (stack j).priority
At any time, only the data object with the highest priority may be operated upon:
that is, the object at the head of the stack. Our promotion schema includes this
condition:
Promote
∆Stack
∆PriData
stack ≠ hi
θPriData = head stack
stack 0 = hθPriData 0 i _ tail stack
The constraint of the promotion schema states that the stack must be non-
empty, and that any change is made to the object at the head of the sequence.
In this description, the global state invariant refers to a component of the
indexed local state: the priority value. In an arbitrary operation, there may be
local after-states which violate the global state invariant. Consider the case in
which the stack contains two objects:
Preconditions
The construction of an abstract data type presents two important proof op-
portunities. The first involves a demonstration that the various requirements
upon the data type are consistent and not contradictory. The second involves
a demonstration that each operation is never applied outside its domain, in a
situation for which the results of the operation are not defined.
If the language of schemas is used to construct the data type, then these
opportunities present themselves as simple mathematical tasks. To show that
the requirements are consistent, we have only to show that the constraint part
of the state schema is satisfiable. This is usually achieved by proving an initial-
isation theorem: we show that an initial state, at least, exists.
To show that the operations are never applied outside their domain, we
must investigate their preconditions. These may be calculated from the opera-
tion schemas using the one-point rule. In this chapter, we explain the procedure
for calculating preconditions, and show how it may be simplified by the use of
structuring techniques such as promotion.
In the previous chapters we have seen how the behaviour of a system may
be described in terms of an abstract data type. The state of the system was
modelled as an object of schema type, the predicate part of which represented
a state invariant: a list of requirements that should be true in any valid state.
Clearly, if this includes a contradiction, then the data type description is
vacuous: it is impossible to fulfil the requirements, therefore no state exists.
To check that this is not the case, and that our specification is of some use, it
is enough to establish that at least one state exists.
202 14 / Preconditions
∃ State0 • StateInit
then we have shown that an initial state exists, and hence also that the re-
quirements upon the state components are consistent. This result is called the
initialisation theorem for the data type.
Example 14.1 In the case of the box office system, the initial state was charac-
terised by
BoxOfficeInit
BoxOffice0
seating 0 = initial allocation
sold 0 = ∅
∃ BoxOffice0 • BoxOfficeInit
The initialisation theorem is an easy one to prove, unless there are com-
plicated initial conditions. Most often, the initial state is described uniquely
with a number of equations, so the proof strategy is simple: eliminate the
quantified variables. Once this has been done, the truth of the predicate should
follow immediately from the properties of the mathematical objects involved.
Example 14.2 In the case of the box office, we may proceed as follows:
∃ BoxOffice0 • BoxOfficeInit
a ∃ BoxOffice0 • [definition of BoxOfficeInit ]
[BoxOffice0 |
seating 0 = initial allocation ∧
sold 0 = ∅]
14.2 / Precondition investigation 203
The precondition of an operation schema describes the set of states for which
the outcome of the operation is properly defined. If Operation is an operation
schema, then we write
pre Operation
This schema characterises the collection of before states and inputs for which
some after state can be shown to exist.
Example 14.3 The precondition of the operation schema Purchase0 , which de-
scribes the effect of a successful purchase, is given by
pre Purchase0
= ∃ BoxOffice0 • Purchase0 [definition of pre ]
204 14 / Preconditions
The predicate part of Purchase0 identifies an after-state that satisfies the state
invariant only if the chosen seat s? has been allocated and not sold. The effect
of Purchase0 is defined only when
Notice that the actual precondition includes additional declaration and con-
straint information. The effect of the operation is properly defined only if the
initial values of seating and sold satisfy the constraint of BoxOffice and s? is an
element of Seat .
Example 14.4 A simple control system monitors the entry and exit of vehicles
from a car park. It maintains a count of the number of vehicles presently inside;
this count should never exceed capacity, an integer number greater than zero:
CarPark
count : N
count ≤ capacity
Exit0
∆CarPark
count 0 = count − 1
It might seem that this schema would apply to all states of the system, in that
we are not placing any explicit constraint upon count . However,
pre Exit0
= ∃ CarPark 0 • Exit0 [definition of Exit0 ]
0
= [CarPark | ∃ count : N | [definition of CarPark 0 ]
count 0 ≤ capacity • count 0 = count − 1]
= [CarPark | count − 1 ∈ N] [one-point rule]
Because of the state invariant, this operation should be restricted to those states
in which the count variable is strictly greater than 0. The effect upon other
states is undefined. By calculating the precondition, we have identified a pos-
sible source of error.
To see why Exit0 alone might be an unsatisfactory description of the exit
operation, suppose that there is a way for cars to enter the car park unobserved.
In this case, the function that implements the exit operation may be called while
the value of count is 0. The subsequent value of count , according to Exit0 ,
conflicts with our choice of data representation: anything could happen.
Now that the problem has been detected, we may choose to totalise the
operation using a second schema to describe the effect of a car leaving when
the system believes that the car park is empty:
ExtraCar
ΞCarPark
r ! : Report
count = 0
r ! = extra car
Exit =
b Exit0 ∨ ExtraCar
Operation
Declaration
Predicate
Before
∃ After •
Predicate
Example 14.5 To see how this recipe for preconditions may be applied, con-
sider the following state schema definitions:
S T
a:N S
b:N c:N
a≠b b≠c
14.3 / Calculation and simplification 207
and suppose that we wish to calculate the precondition of the following oper-
ation schema:
Increment
∆T
in? : N
out ! : N
a 0 = a + in?
b0 = b
c0 = c
out ! = c
The first step of our recipe requires that we divide the declaration part of the
schema into three parts:
The second step requires us to empty the third part, Mixed, by expanding
schema definitions and separating input, output, before and after components.
The result is
T
in? : N
∃ out ! : N; T 0 •
a 0 = a + in?
b0 = b
c0 = c
out ! = c
208 14 / Preconditions
As we can see from the last example, the precondition schema obtained
after the third stage of the recipe may be quite complicated. It is usually pos-
sible to simplify the predicate part of a precondition schema using the one-point
rule. For a precondition schema
Before
∃ After •
Predicate
7. if After1 and Predicate1 are what remains of After and Predicate, then the
precondition is now
Before
∃ After1 •
Predicate1
∃ out ! : N; T 0 •
a 0 = a + in?
b0 = b
c0 = c
out ! = c
∃ out ! : N; S 0 ; c 0 : N | b 0 ≠ c 0 •
a 0 = a + in?
b0 = b
c0 = c
out ! = c
∃ out ! : N; a 0 : N; b 0 : N; c 0 : N | a 0 ≠ b 0 ∧ b 0 ≠ c 0 •
a 0 = a + in?
b0 = b
c0 = c
out ! = c
a + in? ≠ b
b≠c
a + in? ∈ N
b∈N
c∈N
This is as far as the recipe takes us. However, all but one of these conjuncts
follow immediately from the declarations in the precondition schema:
in? : N
T
Removing the redundant information from our predicate, we obtain the final
simplified form of ‘pre Increment ’:
in? : N
T
a + in? ≠ b
210 14 / Preconditions
Op =
b Op1 ∨ Op2
This result follows immediately from the definition of ‘pre’, given the following
theorem of our predicate calculus:
∃A • P ∨ Q a ∃A • P ∨ ∃A • Q
Example 14.7 The Purchase operation was defined as a disjunction of two par-
tial operations
b (Purchase0 ∧ Success)
Purchase =
∨
(NotAvailable ∧ Failure)
Success
r ! : Response
r ! = okay
This schema imposes no constraint upon the before components of the box of-
fice state, neither does it describe any input. It therefore makes no contribution
to the precondition of an operation, and we may observe that
must hold; this follows from the definition at the start of Section 13.3. Now
consider the precondition of GOp, the promotion of a local operation LOp:
pre GOp
a ∃ Global 0 • GOp [definition of ‘pre’]
0
a ∃ Global • ∃ ∆Local • Promote ∧ LOp [definition of GOp]
0
a ∃ ∆Local • ∃ Global • Promote ∧ LOp [property of ∃]
0
a ∃ ∆Local • (∃ Global • Promote) ∧ LOp
[Global 0 does not appear in LOp]
a ∃ Local • (∃ Local ; Global • Promote) ∧ ∃ Local 0 • LOp
0 0
[free promotion]
a ∃ Local • pre Promote ∧ pre LOp [definition of ‘pre’, twice]
The equivalence justified by the phrase ‘free promotion’ can be derived from
the equivalence labelled ‘∗’ using the proof rules for existential introduction
and universal elimination.
212 14 / Preconditions
Example 14.9 The description of a data array in Example 13.3 included a global
operation AssignIndex defined by
AssignIndex =
b ∃ ∆Data • AssignData ∧ Promote
The local operation AssignData is total: the constraint part of pre AssignData
is simply true precondition. The promotion schema Promote was defined by
Promote
∆Array
∆Data
index? : N
index? ∈ dom array
{index?} array = {index?} array 0
array index? = θData
array 0 index? = θData 0
The precondition of Promote adds the constraint index? ∈ dom array. The
precondition of AssignIndex is therefore
Array
new? : N
index? : N
index? ∈ dom array
Example 14.10 In the prioritised data stack of Example 13.8, we propose a local
operation that sets the priority of the top element to 100:
SetPriority
∆PriData
priority 0 = 100
data 0 = data
SetPriorityStack =
b ∃ ∆PriData • SetPriority ∧ Promote
Promote
∆Stack
∆PriData
stack ≠ hi
θPriData = head stack
stack 0 = hθPriData 0 i _ tail stack
This insists that the stack is non-empty. We may calculate the precondition of
SetPriorityStack as follows:
pre SetPriorityStack
a ∃ Stack 0 • SetPriorityStack [definition of ‘pre’]
0
a ∃ Stack • ∃ ∆PriData • Promote ∧ SetPriority
[definition of SetPriorityStack]
0
a ∃ ∆PriData • ∃ Stack • Promote ∧ SetPriority [property of ∃]
a ∃ ∆PriData • (∃ Stack 0 • Promote) ∧ SetPriority
[Stack 0 does not appear in SetPriority]
Stack
∆PriData
stack ≠ hi
θPriData = head stack
∀ j : dom tail stack • θPriData 0 .priority ≥ (stack j).priority
214 14 / Preconditions
Stack
∆PriData
stack ≠ hi
θPriData = head stack
θPriData 0 .priority = 100
θPriData 0 .data = θPriData.data
∀ j : dom tail stack • θPriData 0 .priority ≥ (stack j).priority
Stack
stack ≠ hi
∀ j : dom tail stack • 100 ≥ (stack j).priority
For the operation to be defined: the stack must be non-empty; every priority
value in the tail of the stack must be less than 100; the stack must be ordered
according to decreasing priority values.
A very different result would be obtained by factoring the precondition
calculation as if it were a free promotion. The constraint part of pre SetPriority
is simply true: there is no restriction at the level of the data objects. The schema
is equivalent to
Stack
stack ≠ hi
This schema that omits an essential part of the precondition: the constraint
that every object in the tail of the stack has a priority lower than 100.
The missing constraint appears when we combine the information from
the local operation—that the new priority value is 100—with the invariant prop-
erty of the global state. If we hide the new priority value before combining the
two schemas, then this information is lost.
Operation Precondition
BoxOfficeInit true
Purchase0 s? ∈ seating \ dom sold
NotAvailable s? 6∈ seating \ dom sold
Success true
Purchase true
Return0 s? , c? ∈ sold
NotPossible s? , c? 6∈ sold
Failure true
Return true
Example 14.11 In our theatre box office system, the constraint part of the ini-
tialisation schema is simply true. No initial input is required: the initial value
of seating is simply that of some global variable initial allocation.
The Purchase and Return operations were both total. Each is the disjunc-
tion of two partial operations, Purchase being defined by
and Return by
Each total operation involves two inputs—s? and c?—and a single output—r !.
Having calculated the preconditions, we may collect the results together in a
single table: see Table 14.1.
Chapter 15
A File System
In this chapter, we present the first of several case studies using Z. We show how
the schema notation can be used to specify a simple file system: representing
concrete data structures and a set of operations upon them. We show also
how the preconditions of the various operations can be calculated, and how
the description of a single file can be promoted to an indexed component of a
file system.
The operations add and write are quite different. The first will extend the file
to accommodate the new data, while the second will overwrite an existing part
of the file.
218 15 / A File System
Of these, the first two may be seen as file management operations, while the
others may be seen as file access operations upon the file system.
We will represent each file using a relation between storage keys and data ele-
ments. For the purposes of this specification, we may suppose that keys and
data are drawn from basic types:
[Key, Data]
In more elaborate descriptions, there may be more to a file than simply its con-
tents. To keep our specification both flexible and extensible, we use a schema
to describe the structure of a file:
File
contents : Key →
7 Data
A file should not associate the same key with two different pieces of data, hence
the requirement that the relation contents should be a partial function.
When a file is initialised, it contains no data, so the value of contents should
be the empty function. The initial state of a file is described by:
FileInit
File0
contents 0 = ∅
The schema File corresponds to a set of bindings, each with a single component
contents. The schema FileInit corresponds to a much smaller set of bindings,
the singleton set
{h|contents 0 ∅|i}
15.2 / Operations upon files 219
∆File
File
File0
If an operation leaves the contents of a file unchanged, then we will add the
condition that the binding remains the same:
ΞFile
∆File
θFile = θFile0
Read0
ΞFile
k? : Key
d! : Data
k? ∈ dom contents
d! = contents k?
Write0
∆File
k? : Key
d? : Data
k? ∈ dom contents
contents 0 = contents ⊕ {k? , d?}
The old value of contents is updated with a maplet associating k? with a second
input d?.
220 15 / A File System
Add0
∆File
k? : Key
d? : Data
k? 6∈ dom contents
contents 0 = contents ∪ {k? , d?}
Delete0
∆File
k? : Key
k? ∈ dom contents
contents 0 = {k?} contents
The effect of removing the key is modelled using domain co-restriction: the
maplet starting at k? is removed from contents.
Thus far, we have described only partial operations upon files. For each op-
eration, there are circumstances in which the effect upon the file state is not
fully defined. For example, we have not explained what happens if an attempt
is made to add data using a key that is already in use. We will now extend our
description to cover every eventuality.
We will add a type of reports to our formal specification, allowing us to
provide some output whether or not the operation is successful:
A failed operation upon the file state will always produce a report as output. It
will prove convenient to include the following schema:
15.3 / A more complete description 221
KeyError
ΞFile
k? : Key
r ! : Report
KeyNotInUse
KeyError
k? 6∈ dom contents
r ! = key not in use
KeyInUse
KeyError
k? ∈ dom contents
r ! = key in use
Success
r ! : Report
r ! = okay
The four operations Read, Write, Add, and Delete have been built up in a struc-
tured fashion from small components. This avoids any duplication of effort,
allowing us to factor out common aspects of the design, and results in a clearer,
more comprehensible specification.
In larger case studies and industrial applications, a structured approach
is essential if the reader is not to be overwhelmed by detail. As an indication
222 15 / A File System
( k? ∈ dom contents ∧
d! = contents k? ∧
contents 0 = contents ∧
r ! = okay )
∨
( k? 6∈ dom contents ∧
contents 0 = contents ∧
r ! = key not in use )
The output d! can take any value if the specified key is not in use.
A file system contains a number of files indexed using a set of names. In this
specification, we will regard the set of names as a basic type:
[Name]
In our description of the system, we will consider two aspects of a file system
state: the collection of named files known to the system, and the set of files
that are currently open:
System
file : Name →
7 File
open : P Name
open ⊆ dom file
It is important that the system should not associate the same name with two
different files: file must always be functional.
When the file system is initialised, there are no files. The partial function
file is empty, as is the set open. As the state invariant insists that every open file
15.4 / A file system 223
is also recorded in file, it is enough to insist that the value of file is ∅. Following
our convention that the initialisation schema uses a decorated copy of the state
schema, it is a decorated version of the component that we constrain:
SystemInit
System 0
file0 = ∅
Again, the following pair of schemas will be useful when we come to describe
file system operations:
b [System; System 0 ]
∆System =
b [∆System | θSystem = θSystem 0 ]
ΞSystem =
Both of these schemas insist that the state invariant is preserved: file must
remain functional, and open must remain within its domain.
Since the state of our file system includes indexed copies of File, we may
choose to promote the operations defined above. The local state is described by
File, the global state is described by System, and the promotion is characterised
by the schema
Promote
∆System
∆File
n? : Name
n? ∈ open
file n? = θFile
file0 n? = θFile0
{n?} file = {n?} file0
open 0 = open
which uses the indexing function file to explain the relationship between local
and global states.
We define four operations using this promotion:
KeyRead0 =
b ∃ ∆File • Read ∧ Promote
KeyWrite0 =
b ∃ ∆File • Write ∧ Promote
KeyAdd0 =
b ∃ ∆File • Add ∧ Promote
KeyDelete0 =
b ∃ ∆File • Delete ∧ Promote
224 15 / A File System
Although each local operation is total, the file in question may not be open. The
resulting global operations are partial.
The operations open and close do not change the name of any file, neither
do they add or remove files from the system. They may be described as file
access operations, in that they may change the availability of a file for reading
and writing. In the formal descriptions of these operations, we will find the
following schema useful:
FileAccess
∆System
n? : Name
n? ∈ dom file
file0 = file
This schema describes an operation upon the file system in which the indexing
function file is left unchanged. It insists also that the input component n?
describes a file that is known to the system.
A successful open operation adds a name to the list of open files.
Open0
FileAccess
n? 6∈ open
open 0 = open ∪ {n?}
This operation is strictly partial. An open operation will fail if the name sup-
plied denotes a file that is already open. This possibility is excluded above.
A successful close operation removes a name from the list of open files:
Close0
FileAccess
n? ∈ open
open 0 = open \ {n?}
Again, this operation is strictly partial. A close operation will fail if the name
supplied does not denote an open file. This possibility is excluded above.
The remaining operations, create and destroy, are file management oper-
ations. They may change the list of files known to the system, but they should
not affect the list of open files. As with FileAccess, we may use a single schema
to describe the information that is common to both operations:
15.4 / A file system 225
FileManage
∆System
n? : Name
open 0 = open
Create0
FileManage
n? 6∈ dom file
∃ FileInit •
file0 = file ∪ {n? , θFile0 }
Immediately after this operation, the state of the file associated with name n?
is described by the binding θFileInit . That is, n? is associated with a binding of
schema type File in which contents is bound to the empty set.
A successful destroy operation removes a name from the list of files, do-
main co-restricting the function file:
Destroy0
FileManage
n? ∈ dom file
file0 = {n?} file
Report ::= key in use | key not in use | okay | file exists |
file does not exist | file is open | file is not open
FileError
ΞSystem
n? : Name
r ! : Report
This information will be common to each of the error cases that we encounter
in specifying operations at the system level.
If we attempt to create a file using a name that is already in use, we will
receive a report complaining that a file with that name exists:
FileExists
FileError
n? ∈ dom file
r ! = file exists
FileDoesNotExist
FileError
n? 6∈ dom file
r ! = file does not exist
FileIsOpen
FileError
n? ∈ open
r ! = file is open
FileIsNotOpen
FileError
n? ∈ dom file
n? 6∈ open
r ! = file is not open
15.5 / Formal analysis 227
We are now ready to describe the interface to the file system. There are
four operations involving the contents of files: KeyRead, KeyWrite, KeyAdd,
and KeyDelete. In each case, if the file exists and is open, then the effect of the
operation is described by a promoted file operation:
KeyRead =
b KeyRead0 ∨ FileIsNotOpen ∨ FileDoesNotExist
KeyWrite =
b KeyWrite0 ∨ FileIsNotOpen ∨ FileDoesNotExist
KeyAdd =
b KeyAdd0 ∨ FileIsNotOpen ∨ FileDoesNotExist
KeyDelete =
b KeyDelete0 ∨ FileIsNotOpen ∨ FileDoesNotExist
∃ SystemInit • true
That is, that there exists a binding of file and open which satisfies the constraint
part of SystemInit .
228 15 / A File System
[2] [3]
∅ ∈ P Name ∅ ⊆ dom ∅
[∃−intro]
∃ open 0 : P Name |
[1]
∅ ∈ Name →
7 File open 0 ⊆ dom ∅ • true
[one-point rule]
∃ file0 : Name →
7 File; open0 : P Name |
open 0 ⊆ dom file0 ∧ file0 = ∅ • true
[definition]
∃ SystemInit • true
The result labelled [1] is also immediate, since we require only that file is a
partial function from Name to File. This is true of the empty function, even if
File is an empty set.
Since we are using File as a type, we should also prove that
∃ File0 • FileInit
This is not required for the initialisation of the system, but it will form part of
the precondition for any operation that requires at least one file to exist. The
proof is easy to construct:
[4]
∅ ∈ Key → 7 Data
[one-point rule]
∃ contents 0 : Key →7 Data | contents 0 = ∅ • true
[definition]
∃ FileInit • true
As Key and Data are basic types, we know that they cannot be empty. Hence
the empty relation is an element of Key →7 Data, and an initial state exists.
The second part of our investigation involves calculating the precondition
of each operation. As an example, consider the operation KeyRead, defined by
KeyRead =
b KeyRead0 ∨ FileDoesNotExist ∨ FileIsNotOpen
pre KeyRead a
pre KeyRead0 ∨ pre FileDoesNotExist ∨ pre FileIsNotOpen
15.5 / Formal analysis 229
System
n? : Name
∃ r ! : Report •
n? 6∈ dom file
r ! = file does not exist
Using the one-point rule, we may rewrite the predicate part of this schema
as n? 6∈ dom file. Similarly, we may establish that pre FileIsNotOpen has the
constraint n? ∈ dom file ∧ n? 6∈ open.
The first disjunct requires a little more work. The operation KeyRead0 was
defined by promoting the local operation Read:
KeyRead0 =
b ∃ ∆File • Read ∧ Promote
The combination of System, File, and Promote makes for a free promotion. We
can prove this by starting with the schema
We can rewrite this expression to obtain equalities for both file0 and open 0 are
both uniquely defined:
∃ File0 • ∃ System 0 •
n? ∈ open ∧
file n? = θFile ∧
file0 = file ⊕ {n? , θFile0 } ∧
open 0 = open ]
230 15 / A File System
∃ File0 •
open ⊆ dom file ⊕ {n? , θFile0 } ∧
n? ∈ open ∧
file n? = θFile ∧
file ⊕ {n? , θFile0 } ∈ Name →
7 File ∧
open ∈ P Name ]
∃ File0 •
open ⊆ dom file ∧
n? ∈ open ∧
file n? = θFile ∧
file ∈ Name →
7 File ∧
θFile0 ∈ File ∧
open ∈ P Name ]
It is now clear that there is no constraint upon θFile0 except that it is an element
of File. Since this is the case, we can replace the existential quantification with
a universal quantification over the same schema name:
∀ File0 • ∃ System 0 •
n? ∈ open
file n? = θFile
file0 n? = θFile0
{n?} file = {n?} file0
open 0 = open
Expanding the precondition schema ‘pre Read’, we find that its predicate part
15.5 / Formal analysis 231
System
n? : Name
n? ∈ open
System
n? : Name
Operation Precondition
KeyRead KeyRead0 n? ∈ open
FileIsNotOpen n? ∈ (dom file) \ open
FileDoesNotExist n? 6∈ dom file
KeyRead true
KeyWrite KeyWrite0 n? ∈ open
FileIsNotOpen n? ∈ (dom file) \ open
FileDoesNotExist n? 6∈ dom file
KeyWrite true
KeyAdd KeyAdd0 n? ∈ open
FileIsNotOpen n? ∈ (dom file) \ open
FileDoesNotExist n? 6∈ dom file
KeyAdd true
KeyDelete KeyDelete0 n? ∈ open
FileIsNotOpen n? ∈ (dom file) \ open
FileDoesNotExist n? 6∈ dom file
KeyDelete true
Open Open0 n? ∈ (dom file) \ open
FileIsOpen n? ∈ open
FileDoesNotExist n? 6∈ dom file
Open true
Close Close0 n? ∈ open
FileIsNotOpen n? ∈ (dom file) \ open
FileDoesNotExist n? 6∈ dom file
Close true
Create Create0 n? 6∈ dom file
FileExists n? ∈ dom file
Create true
Destroy Destroy0 n? ∈ (dom file) \ open
FileIsOpen n? ∈ open
FileDoesNotExist n? 6∈ dom file
Destroy true
Data Refinement
16.1 Refinement
The Concise Oxford Dictionary (8th edition) contains the following definition:
Although the bit about subtle reasoning is amusing, the relevant words here
are ‘instance of improvement upon’. For us, refinement is all about improving
specifications.
The process of improvement involves the removal of nondeterminism, or
uncertainty. An abstract specification may leave design choices unresolved;
a refinement may resolve some of these choices, and eliminate some of the
nondeterminism. Several refinement steps may be performed, each removing
another degree of uncertainty, until the specification approaches executable
program code.
ResourceManager
free : F N
Any resource that is currently free may be allocated. The effect of an allocation
is described by the following operation schema:
Allocate
∆ResourceManager
r! : N
r ! ∈ free ∧ free0 = free \ {r !}
If there is more than one resource free, then this specification is nondetermin-
istic. It is also partial: we have not explained what is to be done if there are no
resources left to be allocated.
This specification may be refined by another in which we decide that,
should there be more than one resource free, the resource with the lowest
number should be allocated first. In this new specification, the effect of an
allocation is described by
Allocate1
∆ResourceManager
r! : N
r ! = min free ∧ free0 = free \ {r !}
ResourceManager2
free2 : array of Bit
Example 16.2 When the Parliament of the European Union passes legislation,
it does so in the form of a European directive. Each of the member countries is
then required to enact the legislation, and in the United Kingdom this is done
by passing an Act of Parliament. Regulatory authorities, such as the Health and
Safety Executive, produce regulations, which they then seek to enforce. These
legal instruments are so arranged that compliance with the regulations implies
compliance with the Act, and compliance with the Act implies compliance with
the directive. It is usually the case that the regulations are rather more strict
than the original directive, because the legislation has been through two stages
of interpretation, each taking into account considerations peculiar to the United
Kingdom. We might say that the Act is a refinement of the directive, and that
the regulations are a refinement of the Act.
Example 16.3 For reasons that we are unable to explain, we would like to raise
a million pounds sterling. One way to do this would be to raise the money in
the United States of America. However, there is a problem with exchange rate
fluctuations. At the time of writing, the exchange rate was £1.00 = $ 1.45, so
we would need to raise $1,450,000 in the United States. If the exchange rate
changes so that there is parity between the currencies, then we would have to
raise only $1,000,000; and if it changes so that there are two US dollars for each
pound sterling, then we would need to raise $2,000,000.
236 16 / Data Refinement
{x : X ⊥ ; y : Y ⊥ | x 6∈ dom ρ • x , y}
where X ⊥ and Y ⊥ denote the augmented versions of the source and target.
For convenience, we will use the expression s to denote the complement
of a set s in its type. For example, if s is a set of type P X , then
s = {x : X | x ∉ s }
•
We will write ρ to denote the totalised form of ρ, where
•
ρ ∈ X⊥ ↔ Y⊥
and
• ⊥
ρ = ρ ∪ (dom ρ × Y ⊥ )
•
The expression ρ can be pronounced ‘ρ-dot’.
16.2 / Relations and nondeterminism 237
a a
b b
c c
d d
L ::= a | b | c | d
and a relation ρ by
ρ == {a , a, a , b, b , b, b , c}
•
ρ == {a , a, a , b, b , b, b , c,
c , ⊥, c , a, c , b, c , c, c , d,
d , ⊥, d , a, d , b, d , c, d , d,
⊥ , ⊥, ⊥ , a, ⊥ , b, ⊥ , c, ⊥ , d}
κ0 == { z : Z • z , 0 }
238 16 / Data Refinement
• •
∅ o9 κ0
σ == {a , a, b , b, b , c, c , c}
dom σ = {a, b, c}
⊇ {a, b}
= dom ρ
and
dom ρ / σ = {a , a, b , b, b , c, }
⊆ρ
• •
Equivalently, we might observe that σ ⊆ ρ, since
•
σ = {a , a, b , b, b , c, c , c, d , ⊥, d , a, d , b, d , c, d , d,
⊥ , ⊥, ⊥ , a, ⊥ , b, ⊥ , c, ⊥ , d}
•
and each of these pairs is present in ρ.
τ == {a , a, c , c}
Example 16.7 We may corrupt a bit—an element of the set {0, 1}—by changing
its value:
∼ : Bit → Bit
∼0 = 1
∼1 = 0
240 16 / Data Refinement
For example,
For example,
In moving from corruptsto to changesto, we have traded the fact that in every
output pair of bits, at least one is correct, for the fact that in every output pair
of bits exactly one is correct.
The second relation is a refinement of the first: both are total relations
on seq Bit , and changesto resolves all of the nondeterminism present in the
definition of corruptsto. If we are content with the behaviour of corruptsto,
then we will be content with that of changesto. Indeed, we may be more so, as
every bit with an even index is guaranteed to be correct.
16.3 / Data types and data refinement 241
• xi ∈ G ↔ X is an initialisation;
• xf ∈ X ↔ G is a finalisation;
• • • •
X = (X ⊥ , xi, xf , { i : I • xoi })
242 16 / Data Refinement
Since xi and xf are total, the process of totalisation does nothing but augment
each of them with the set of pairs {⊥} × X ⊥ .
We are now able to present a suitable definition of refinement for abstract
data types. If data types A and C share the same indexing set, then A is refined
by C if and only if for each program P (A)
• •
P (C) ⊆ P (A)
If A and C are both indexed by the set I , then this definition requires us to
prove that, for sequences hs1 , s2 , . . . sn i in seq I ,
• • • • • • • • • •
ci o9 cos1 o9 cos2 o9 · · · o9 cosn o
9 cf ⊆ ai o9 aos1 o9 aos2 o9 · · · o9 aosn o
9 af
Example 16.8 We may define two data types for handling sequences of bits, A
and C. Each will accept a sequence of bits at initialisation, and deliver another
sequence at finalisation. In each case, the state space is defined as a collection
of tuples:
where Bit is the set containing just 0 and 1, and Action is defined by
The first component of the state tuple represents the unconsumed part of the
input sequence, the second indicates whether or not the next bit must be faith-
fully reproduced, and the third represents the accumulated output.
The initialisation and finalisation operations are the same for both data
types. Initially, the whole of the input sequence waits to be consumed, the
output sequence is empty, and the next bit may be corrupted.
ai : seq Bit ↔ A
ci : seq Bit ↔ C
∀ bs : seq Bit ; a : A; c : C •
bs ai a a a = (bs, no, hi)
bs ci c a c = (bs, no, hi)
16.3 / Data types and data refinement 243
af : A ↔ seq Bit
cf : C ↔ seq Bit
∀ bs : seq Bit ; a : A; c : C •
a af bs a bs = a.3
c cf bs a bs = c.3
The effect of this operation is simply to project out the third component of the
current state tuple.
Each data type has a single operation. That of A is nondeterministic:
it may choose to act faithfully, appending the next input bit b to the output
sequence. However, if the last bit was faithfully reproduced, it may choose to
append the corrupted bit ∼b instead.
ao : A ↔ A
∀ a, a 0 : A •
a ao a 0 a a 0 .1 = tail a.1
a.2 = yes ⇒
a 0 .3 = a.3 _ hhead a.1i ∧ a 0 .2 = no
a.2 = no ⇒
a 0 .3 = a.3 _ h∼head a.1i ∧ a 0 .2 = yes
∨
a 0 .3 = a.3 _ hhead a.1i ∧ a 0 .2 = no
Whenever a bit is corrupted, the action component of the next state is set to
yes, indicating that the next bit must be appended faithfully.
In contrast, the operation of C is completely deterministic. It alternates
between corruption and fidelity, changing the value of the action component
each time it is applied. This has the effect of removing the disjunction from the
above definition, leaving the state after fully determined by the state before.
co : C ↔ C
∀ c, c 0 : C •
c co c 0 a c 0 .1 = tail c.1
c.2 = yes ⇒
c 0 .3 = c.3 _ hhead c.1i ∧ c 0 .2 = no
c.2 = no ⇒
c 0 .3 = c.3 _ h∼head c.1i ∧ c 0 .2 = yes
244 16 / Data Refinement
The relationship between A and C is the same as that between corruptsto and
changesto in Example 16.7. That is, C is a refinement of A. To prove this, we
must show that
ci o9 cf ⊆ ai o9 af
ci o9 co o9 cf ⊆ ai o9 ao o9 af
ci o9 co o9 co o9 cf ⊆ ai o9 ao o9 ao o9 af
..
.
It is easy to show that this reduces to the requirement that co ⊆ ao. The result
then follows from the definitions of the two operations.
16.4 Simulations
If the answer to each question is yes, then we say that ρ is a simulation for the
two data types. The effect of any program step in C can be simulated by a step
in A. Therefore, for any program P ,
P (C) ⊆ P (A)
a a
b b
c c
d d
◦
ρ ∈ X⊥ ↔ Y⊥
◦
ρ = ρ ∪ ({⊥} × Y ⊥ )
◦
We say that ρ is the lifted form of ρ.
L ::= a | b | c | d
and a relation ρ by
ρ == {a , a, a , b, b , b, b , c}
◦
ρ == {a , a, a , b,
b , b, b , c,
⊥ , ⊥, ⊥ , a, ⊥ , b,
⊥ , c, ⊥ , d}
ao
ai af
r r r r
ci cf
co
If data types A and C share the same indexing set, and r is a relation of
type A ↔ C, then r is a forwards simulation if
• • ◦
• ci ⊆ ai o9 r
◦ • •
• r o
9 cf ⊆ af
◦ • • ◦
• r o
9 coi ⊆ aoi o9 r for each index i
These requirements are illustrated in Figure 16.3. The first insists that the effect
of ci can be matched by ai followed by r , a two-step path around the diagram;
the second that the effect of r followed by cf , another two-step path, can be
matched by af ; the third that the effect of moving downwards and then across
can be matched by moving across and then downwards.
The lower path in the diagram corresponds to a program using data type
C. The upper path corresponds to the same program using data type A. Since
the effect of each program step can be simulated, it is easy to see that C is a
refinement of A.
Valid moves in the concrete data type may be simulated by moves in the
abstract data type. The relation r is said to be a forwards simulation because, if
we consider similar concrete and abstract states, then any valid move forwards
to a new concrete state can be matched by a move to a similar abstract state.
Since r relates abstract values down to concrete ones—see Figure 16.3—such a
relation is sometimes called a downwards simulation.
16.4 / Simulations 247
ao
ai af
s s s s
ci cf
co
If data types A and C share the same indexing set, and s is a relation
of type C ↔ A between concrete and abstract states, then s is a backwards
simulation if
• ◦ •
• ci o9 s ⊆ ai
• ◦ •
• cf ⊆ s o9 af
• ◦ ◦ •
• coi o9 s ⊆ s o9 aoi for each index i
The requirements are similar to those for a forwards simulation, except that
the position of the simulation is reversed. The first insists that the effect of
ci followed by s can be matched by ai; the second that the effect of cf can be
matched by s followed by af ; the third that the effect of moving across and
then upwards can be matched by moving upwards and then across.
As before, the lower path in the diagram—Figure 16.4—corresponds to
a program using data type C, and the upper path corresponds to the same
program using data type A. We have that C is a refinement of A.
Again, valid moves in the concrete data type may be simulated by moves
in the abstract data type. The relation s is said to be a backwards simulation
because, if we consider similar concrete and abstract states, then any valid move
to this concrete state from an old concrete state can be matched by a move from
a similar abstract state. Since s relates concrete values up to abstract ones—see
Figure 16.4—such a relation is sometimes called an upwards simulation.
248 16 / Data Refinement
A similar argument shows that the requirement upon the two finalisation op-
erations can be relaxed to
r o
9 cf ⊆ af
In each case, the ‘dot’ of totalisation and the ‘spot’ of lifting can be safely re-
moved from the defining condition.
To obtain a suitable relaxation of the third requirement, we consider the
following result: if ρ, σ , and τ are relations of type X ↔ Z, X ↔ Y , and Y ↔ Z,
respectively, then
• ◦
ρ⊆σ o
9 τ a (dom σ ) / ρ ⊆ σ o
9 τ
⊥ ◦
a ρ ⊆ (σ o
9 τ) ∪ ((dom σ × Y ⊥ ) o9 τ) [⊥ 6∈ ran σ ]
⊥
a ρ ⊆ (σ o
9 τ) ∪ (dom σ × Y ⊥ ) o9 (τ ∪ {⊥} × Z ⊥ ) [lifting]
⊥ ⊥
a ρ ⊆ (σ o
9 τ) ∪ (dom σ ×Z ) [property of o9]
a (dom σ ) / ρ ⊆ σ o
9 τ [property of relations]
We will call this result ‘spot-dot elimination’. Returning to the third requirement
for simulation, we proceed as follows:
◦ • • ◦
r o
9 co ⊆ ao o9 r
◦ •
a dom ao / (r o
9 co) ⊆ ao o9 r [spot-dot elimination]
◦ •
a (dom ao / r ) o9 co ⊆ ao o9 r [property of / and o9]
•
a (dom ao / r ) o9 co ⊆ ao o9 r [⊥ 6∈ dom ao]
⊥
a (dom ao / r ) o9 (co ∪ dom co × C ⊥ ) ⊆ ao o9 r [totalisation]
a (dom ao / r ) co ⊆ ao r o
9
o
9 [property of ⊆]
∧
⊥
(dom ao / r ) o9 (dom co × C ⊥ ) ⊆ ao o9 r
The first conjunct insists that the effect of co is consistent with that of ao,
wherever ao is defined. The second conjunct requires further investigation:
since ⊥ is outside the range of ao o9 r , it is equivalent to the condition that
ran(dom ao / r ) ⊆ dom co
Informally, this requires that the operation co is defined for every value that
can be reached from the domain of ao using relation r .
We may also derive a set of relaxed requirements for proving backwards
simulation. The requirements upon initialisation and finalisation lose their
spots and dots,
ci o9 s ⊆ ai
cf ⊆ s o9 af
The first conjunct insists that the effect of co must be consistent with that of
ao. The second insists—somewhat awkwardly—that the set of values for which
co is not defined must be a subset of those for which ao is not defined.
250 16 / Data Refinement
F-init-rel-seq ci ⊆ ai o9 r
F-fin-rel-seq r o
9 cf ⊆ af
B-init-rel-seq ci o9 s ⊆ ai
B-fin-rel-seq cf ⊆ s o9 af
These results yield a collection of relaxed proof rules for simulations, col-
lected in Table 16.1. The rules are named according to the type of simulation—F
for forwards, B for backwards—and the type of rule: init for initialisation; fin for
finalisation; corr for correctness of operations. We add an additional qualifier
rel to indicate that we are working within a theory of relations.
These rules may be applied to operations involving input and output only
by providing all inputs at initialisation, and delaying all outputs until finalisa-
tion. The initial state of a program would include a sequence of inputs—the
input values required during execution—and the final state a sequence of out-
puts. Each rule in Table 16.1 is labelled with the suffix seq to indicate that input
and output must be represented in this way.
It is possible to derive an equivalent set of rules in which inputs and out-
puts may occur at each program step. Suppose that op is an operation that
involves input and output: a relation of type
State × (seq Input × seq Output ) ↔ State × (seq Input × seq Output )
16.5 / Relaxing and unwinding 251
that behaves as follows: the effect of ops upon the state is that of op, given the
head of the first sequence as input. Any output from op is added to the end of
the second sequence. That is,
In our use of ops , we may regard the input and output sequences as part of the
state information.
To obtain ops from op, we must extract the next value from the input
sequence. We define a function that takes a state and a pair of sequences and
returns a state, an input, and a new pair of sequences:
When we apply split , the first input is selected, and the results are assembled in
a useful combination. The state and next input are presented as a pair, ready
for consumption by an operation.
To simplify the process of reasoning about split , we derive an equivalent
definition that avoids mentioning the arguments of the function. This will re-
quire three new operators for manipulating pairs and sequences. The first is a
form of parallel composition:
[W , X , Y , Z ]
k : (W ↔ Y ) × (X ↔ Z ) → W × X ↔ Y × Z
∀ ρ : W ↔ Y ; σ : X ↔ Z; w : W ; x : X ; y : Y ; z : Z •
(w, x) , (y, z) ∈ ρ k σ a w , y ∈ ρ ∧ x , z ∈ σ
[X ]
cp : X ) X × X
∀ x : X • cp x = (x, x)
252 16 / Data Refinement
s s
id
first 9o head
(is, os) head is
is tail is
tail
(is, os)
second
(s, (is, os) ) (tail is, os )
id
os os
The third operator takes a value–sequence pair and appends the value to the
end of the sequence:
[X ]
ap : X × seq X ) seq X
∀ x : X ; xs : seq X • ap(x, xs) = xs _ hxi
id k (first o9 head)
split = cp o9 k
second o9 (tail k id)
cp
((r, o), (is, os)) (r, o) o (is, os
<o>)
second
ap
((r, o), (is, os)) os <o>
second
(is, os) os
We may use split and merge to translate an operation that involves input
and output to one that expects these values to be present as sequences. If ρ is
such an operation, then we may define
where ‘id’ is the identity relation on pairs of input and output sequences.
Furthermore, since r is a relation between states without input and output
sequences, we must construct an equivalent relation that acts on the enhanced
form of the state. If r is a relation of type
AState ↔ CState
AState × (seq Input × seq Output ) ↔ CState × (seq Input × seq Output )
rs = r k id
F-init-rel ci ⊆ ai o9 r
B-init-rel ci o9 s ⊆ ai
The operations cos and aos have the same effect upon the two sequences: they
remove a value from one and append a value to the other. The relation rs has
no effect upon the sequences, so this requirement is equivalent to
The presence of the identity relation reflects the fact that output is no longer
treated as part of the state.
A set of unwound rules for forwards and backwards simulation is presen-
ted in Table 16.2. Finalisation is no longer a special case—any program step
may produce output—so there are only two rules for each form of simulation.
Because these rules may be applied directly, without regarding input and out-
put as special components of the state, we drop the suffix seq from the name
of each rule.
Chapter 17
b [ ∆S | r ≠ 0 ∧ r 0 = 1/r ]
Recip =
{ Recip • θS , θS 0 }
258 17 / Data Refinement and Schemas
If we totalise this, and then simplify the resulting expression, then we get to the
true specification of Recip (‘true’ in the sense that it describes all the behaviour
of Recip, including what happens outside the precondition):
•
z }| {
{ r , r 0 : R | r ≠ 0 ∧ r 0 = 1/r • θS , θS 0 }
{ r , r 0 : R⊥ | r ≠ 0 ∧ r ≠ ⊥ ∧ r 0 = 1/r ∨ r = 0 ∨ r = ⊥ • θS , θS 0 }
This relates any state in which r is non-zero to one in which it has the reciprocal
value. A state in which r is zero, or is undefined, is related to all possible states.
The reader may be forgiven for asking: if this relation is the true specific-
ation of the operation, then why didn’t we write it like this in the first place?
The answer is that totalised relations are more complicated to write, and more
difficult to compose. For example, we can add error handling to Recip by using
schema disjunction (as in Recip ∨ Error ); this would be harder to do with a
totalised version.
An operation schema may also include input and output components. To
represent these, the domain of the corresponding relation will be a Cartesian
product of states and inputs, and the range will be a product of states and
outputs. If Op describes an operation on a state S , then the corresponding
relation will be a lifted, totalised version of
That is,
•
z }| {
split o9 ({ Op • (θS , i?) , (θS 0 , o!) } k id) o9 merge
R
A
C
..
.
17.2 / Forwards simulation 259
The relationship that this schema records is called a retrieve relation: it shows
how the representation of data in A—which may be more abstract—can be
retrieved from the representation of data in C.
To decide whether or not R is a simulation, we will need to compare op-
erations with the same index; let us consider two such operations, AO and CO,
each with a single input i? and a single output o!. We will also need to exam-
ine the initialisations of A and C; let us suppose that these are described by
schemas AI and CI , respectively.
To apply the existing rules for forwards simulation, we consider the relations
that correspond to the retrieve and operation schemas:
r = { R • θA , θC }
ao = { AO • (θA, i?) , (θA0 , o!) }
co = { CO • (θC, i?) , (θC 0 , o!) }
ai = { AI • θA0 }
ci = { CI • θC 0 }
To simplify the process of reasoning about ai and ci, we will regard each as a
trivial form of relation, in which the first component of each pair is ignored.
The unwound rules for forwards simulation, presented towards the end
of the last chapter, insist that the following condition must hold for the two
initialisations:
ci ⊆ ai o9 r
ci ⊆ ai o9 r
a ∀ c : C • c ∈ ci ⇒ c ∈ ai o9 r [by property of ⊆]
a ∀ C • θC ∈ ci ⇒ θC ∈ ai r o
9 [by schema calculus]
a ∀ C • θC ∈ ci ⇒ [by property of o9]
∃ A • θA ∈ ai ∧ θA , θC ∈ r
a ∀ C • θC ∈ { CI • θC 0 } ⇒ [by definition]
0
∃ A • θA ∈ { AI • θA } ∧
θA , θC ∈ { R • θA , θC }
260 17 / Data Refinement and Schemas
F-init ∀ C 0 • CI ⇒ ∃ A0 • AI ∧ R0
∀ A; C; C 0 • pre AO ∧ R ∧ CO ⇒ ∃ A0 • AO ∧ R0
a ∀ C 0 • CI ⇒ ∃ A0 • AI ∧ R0 [by comprehension]
and that
for every pair of operations ao and co. These requirements lead to a pair of
conditions upon the corresponding operation schemas.
The first condition is that the concrete operation CO must be defined in
any state whose abstract equivalent satisfies the precondition of AO.
∀ A; C • pre AO ∧ R ⇒ pre CO
This tells us that the development step has—if anything—weakened the pre-
condition of the operation.
The second condition tells us that the concrete operation produces results
that are consistent with those of the abstract:
∀ A; C; C 0 •
pre AO ∧ R ∧ CO ⇒ ∃ A0 • AO ∧ R0
Suppose that two concrete states C and C 0 are related by the concrete operation
CO. Suppose also that A, the abstract equivalent of C, lies within the precon-
dition of AO. Then for CO to be a correct refinement of AO, there must be an
abstract state A0 , corresponding to C 0 , that can be reached from A by applying
AO. The three conditions are presented together in Table 17.1.
17.2 / Forwards simulation 261
Example 17.1 We require a system that will monitor the access to a building.
The system should keep track of the people who are inside the building, and
should forbid entry by more than a specified number of people at any time. Let
Staff be the set of all members of staff:
[Staff ]
and let maxentry be the maximum number of people that may enter the building
at any time:
maxentry : N
We can model the state of our system by recording the names of those cur-
rently inside the building; the state invariant restricts the number of people
accordingly:
b [ s : P Staff | #s ≤ maxentry ]
ASystem =
Initially, there is no-one in the building; this satisfies the invariant, no matter
what the value of maxentry:
b [ ASystem 0 | s 0 = ∅ ]
ASystemInit =
A person who is not already recorded as being inside the building may enter it,
providing there is enough room:
AEnterBuilding
∆ASystem
p? : Staff
#s < maxentry
p? ∉ s
s 0 = s ∪ {p?}
ALeaveBuilding
∆ASystem
p? : Staff
p? ∈ s
s 0 = s \ {p?}
262 17 / Data Refinement and Schemas
b [ CSystem 0 | l 0 = hi ]
CSystemInit =
A person who is not already inside the building may enter it, providing there is
enough room:
CEnterBuilding
∆CSystem
p? : Staff
#l < maxentry
p? ∉ ran l
l 0 = l _ hp?i
CLeaveBuilding
∆CSystem
p? : Staff
p? ∈ ran l
l 0 = l u (Staff \ {p?})
Although both specifications describe the same system, the first is more ab-
stract: it doesn’t record the order in which people enter the building. The use
of a sequence certainly makes the second specification a bit more awkward: we
have to say that it contains no duplicates. The second specification also makes
certain design decisions: for example, new people are appended to the end of
the sequence.
We regard the first description as an abstract specification, and the second
as a step on the way to producing a design. We intend to implement the set
of names using an array, in which the elements will be ordered. We take a
design decision to record the names in order of arrival. This decision may be
documented using a retrieve relation:
17.2 / Forwards simulation 263
ListRetrieveSet
ASystem
CSystem
s = ran l
This is a formal record of the design step. It will help us to demonstrate that
the second specification is a correct implementation of the first.
In order to prove that this refinement is correct, we must establish that
each of the following statements is a theorem:
∀ CSystem 0 • CSystemInit ⇒
(∃ ASystem 0 • ASystemInit ⇒ ListRetrieveSet 0 )
Example 17.2 We are required to produce a program that finds the average of
some numbers. We decide that the program should find the arithmetic mean of
some natural numbers. Our specification describes a simple interface consist-
ing of two operations: an operation AEnter that adds a number to our data set
and an operation AMean that calculates the arithmetic mean of the numbers
entered thus far.
The state of the program is modelled using a sequence of natural numbers
to represent the data set:
b [ s : seq N ]
AMemory =
Operation Precondition
AMemoryInit true
AEnter true
AMean s ≠ hi
b [ AMemory 0 | s 0 = hi ]
AMemoryInit =
AEnter
∆AMemory
n? : N
s 0 = s _ hn?i
The arithmetic mean of a series is its sum divided by its length. The following
schema makes it clear exactly what is to be calculated:
AMean
ΞAMemory
m! : R
s ≠ hi
P#s
i=1 (s i)
m! =
#s
The result makes sense only if the length of the sequence is strictly positive:
this leads us to the precondition recorded in Table 17.2.
It is not necessary to keep the entire sequence of numbers that has been
input; there is another way to compute the mean. In a specification we are more
concerned with clarity than with efficiency, so the summation over a series is
entirely appropriate. We will now consider a design in which only two numbers
are stored: the running total and the sample size.
17.2 / Forwards simulation 265
Operation Precondition
CMemoryInit true
CEnter true
CMean size ≠ 0
b [ sum : N; size : N ]
CMemory =
When a number is entered, it is added to the running total, and the sample size
is increased by one:
CEnter
∆CMemory
n? : N
sum 0 = sum + n?
size0 = size + 1
If at least one number has been entered, then the mean may be obtained by
dividing the running total by the sample size. In our design, the effect of this
operation is described by
CMean
ΞCMemory
m! : R
size ≠ 0
sum
m! = size
SumSizeRetrieve
AMemory
CMemory
#s
X
sum = (s i)
i=1
size = #s
∀ CMemory 0 • CMemoryInit ⇒
(∃ AMemory 0 • AMemoryInit ⇒ SumSizeRetrieve0 )
We may now translate our design into the refinement calculus—the sub-
ject of the next chapter—using a mixture of program code and specification
statements. Briefly, the specification statement w : [ pre, post ] describes a pro-
gram that must terminate if started in any state satisfying pre, yielding a state
satisfying post , while changing only those variables mentioned in w.
The result of our translation is shown below. The body of the procedure
enter comprises a specification which insists that the global variable sum must
17.2 / Forwards simulation 267
be increased by the value of the n?, and that size must be incremented.
The body of the procedure mean comprises another specification that insists
that m! must have the final value sum/size. In this case, the implementor may
assume that the value of size is not 0.
We may fill in some detail by refining the specification statements into a
target programming language: in this case, Pascal. The result is a program that
correctly implements our original specification:
PROGRAM MeanMachine(input,output);
VAR
n,sum,size: 0..maxint;
m: real;
PROC Enter(n: 0..maxint);
BEGIN
sum := sum + n;
size := size + 1
END;
PROC Mean(VAR m: real);
BEGIN
m := sum / size
END;
BEGIN
sum := 0;
size := 0;
WHILE NOT eof DO
BEGIN
read(n);
Enter(n)
END;
Mean(m);
write(m)
END.
268 17 / Data Refinement and Schemas
b [ ad : P Word ]
ADict =
CDict1
cd1 : iseq Word
∀ i, j : dom cd1 | i ≤ j • (cd1 i) ≤W (cd1 j)
CDict2
cd2 : seq(P Word)
∀ i : dom cd2 • ∀ w : (cd2 i) • #w = i
This design starts by introducing a sequence of sets of words, with each of the
sets containing only words of a particular length: the first set has words of
length 1, the second of length 2, and so on.
As a third alternative, suppose that we are more interested in space ef-
ficiency. In this case, we might choose to exploit the common prefixes in the
dictionary. As an example, suppose that our dictionary were rather sparsely
filled with the following words: and, ant , bee, can, and cat . Instead of storing
all 15 letters, we need store only 11 of them. The data structure that we have
in mind is a tree. At its root there are three branches, one for a, one for b, and
one for c. Below each of these branches, there is another prefix tree.
If X →7 1 Y denotes the set of all non-empty functions between X and Y ,
then the free type of prefix trees is given by
a b c
n e a
d t e n t
The use of two injections—tree and treeNode—means that we can capture proper
prefixes.
With this definition, the design of the dictionary can be described by the
following schema:
b [ cd3 : WordTree ]
CDict3 =
As a final example, consider a little dictionary which contains only the words
tin and tiny. This has the representation as a word tree which is linear:
The injection treeNode is used to mark a node that contains the end of a word,
even if it is a proper prefix of another word.
Each of the three designs—CDict1 , CDict2 , and CDict3 —forms the basis for
a correct data refinement of ADict .
270 17 / Data Refinement and Schemas
B-init ∀ A0 ; C 0 • CI ∧ R0 ⇒ AI
∀ A0 ; C 0 • CI ∧ R0 ⇒ AI
Example 17.4 The Phoenix is a cinema whose box office works as follows. A
customer may telephone and ask for a ticket. The box office clerk decides if
there is an unsold ticket so as to accommodate the request. If there is, then a
note is made to reserve a ticket for the caller. When the customer arrives, the
box office clerk allocates an unsold ticket which identifies the seat.
We contrast this procedure with that of the Apollo theatre. At the Apollo,
a customer may telephone and ask for a ticket. The box office clerk decides if
there is an unsold ticket so as to accommodate the request. If there is, then
one is allocated and put to one side for the caller. When the customer arrives,
the clerk presents the allocated ticket which identifies the seat.
The customer cannot tell the difference between the two booking proced-
ures. The point at which the ticket is allocated—and a nondeterministic choice
of seat number is made—cannot be detected by the caller. The transaction ap-
pears the same in each case: the customer telephones the box office, arrives at
the place of entertainment, obtains a ticket, and takes the indicated seat.
The Phoenix maintains a pool of tickets, drawn from a given set
[Ticket ]
Phoenix
ppool : P Ticket
bkd : Booked
The booking operation requires that the customer has not already booked,
and that there is a ticket to be allocated:
PBook
∆Phoenix
bkd = no
ppool ≠ ∅
bkd 0 = yes
ppool 0 = ppool
272 17 / Data Refinement and Schemas
A successful arrival requires that the customer has booked and that a
ticket has been left for them:
PArrive
∆Phoenix
t ! : Ticket
bkd = yes
ppool ≠ ∅
bkd 0 = no
t ! ∈ ppool
ppool 0 = ppool \ {t !}
The state of the Apollo box office contains a pool of ordinary tickets, and a
possibly-null ticket:
Apollo
apool : P Ticket
tkt : ATicket
tkt ≠ null ⇒ ticket ∼ tkt ∉ apool
The booking operation requires that no ticket has already been reserved by the
customer, and that the pool is not empty.
ABook
∆Apollo
tkt = null
apool ≠ ∅
tkt 0 ≠ null
ticket∼ tkt 0 ∈ apool
apool 0 = apool \ {ticket∼ tkt 0 }
Afterwards, a single ticket is removed from the pool and reserved in the state
component tkt .
17.3 / Backwards simulation 273
AArrive
∆Apollo
t ! : Ticket
tkt ≠ null
tkt 0 = null
t ! = ticket∼ tkt
apool 0 = apool
ApolloPhoenixRetr
Phoenix
Apollo
bkd = no ⇒ tkt = null ∧ ppool = apool
bkd = yes ⇒ tkt ≠ null ∧ ppool = apool ∪ {ticket∼ tkt }
The first of these can be proved using forwards simulation; the second cannot.
To see why, consider the following statement:
To prove this, we must show that t ! = ticket∼ tkt : one of the predicates of
AArrive. The most that we can deduce from the antecedents is that t ! ∈ apool ∪
{ticket∼ tkt }. This is not enough. Notice that when we prove the refinement
the other way around, the proof will work as expected.
As the reader will have guessed, we need the backwards simulation rules
in order to be able to prove the second conjecture. Our troublesome predicate
asks the very question that backwards simulation sets out to answer: that is, is
there a state that the abstract operation could have started in that would have
led to this situation?
274 17 / Data Refinement and Schemas
Example 17.5 The game of MastermindTM was popular some twenty years ago.
In this game, one player chooses a code of six coloured pegs, and the other tries
to guess what this code is. The guesser is allowed a number of attempts; the
setter replies to each attempt by stating how many of the guess’s pegs are of
the correct colour, and how many of these are in the correct order.
In the specification of an electronic version of MastermindTM , we might
state that a random sequence is chosen when the system is switched on. An
implementation may do exactly this, or it may postpone the choice until the first
attempt is made. Since there is no way to detect that the choice has not already
been made, this would be a valid refinement of the specification, provable using
backwards simulation.
An implementation might also choose to postpone the choice until the last
possible moment. The system could then maintain a set of codes—consistent
with the answers it has given the user thus far—and leave the decision about
the ‘real’ code until it has no room left to manoeuvre. Of course, delaying the
choice of code is against the spirit of the game, but with this interface there is no
way to detect the ‘fraud’: our cheating implementation is a correct refinement
of the specification.
Example 17.6 Here is another example of a refinement which requires the back-
wards rules; it also has a novel twist. It concerns a greatly simplified model of a
vending machine, which dispenses drinks in response to three-digit codes typed
in by its users. The specification abstracts from the detail of the digits being
input one-by-one, and requires instead that the sequence is entered atomically.
This kind of abstraction, where the level of atomicity in the system is changed,
is useful in describing many similar interfaces, such as that used in telephone
systems.
We begin with a few global definitions. The free type Status is used to
signal the success or failure of the current interaction with the machine, and
to keep track of whether a transaction is in progress; Digit s are those numbers
between 0 and 9; and seq 3 [X ] is the set of sequences of X s whose length is
exactly 3.
Digit == 0 . . 9
seq 3 [X ] == { s : seq X | #s = 3 }
The state of our specification contains two boolean variables; these indicate
whether it is in use, and whether the current transaction will be successful.
Choose
∆VMSpec
i? : seq 3 Digit
busy = no
busy 0 = yes
Note that the value of vend is left undetermined by the operation: its value is
nondeterministically chosen. The end of the transaction simply signals whether
the transaction is successful or not:
VendSpec
∆VMSpec
o! : Status
busy 0 = no
o! = vend
At the design level, digits are entered separately. All we actually need
to record is the number of digits entered. Initially, there is no transaction in
progress:
b [ digits : 0 . . 3 ]
VMDesign =
b [ VMDesign 0 | digits 0 = 0 ]
VMDesignInit =
FirstPunch
∆VMDesign
d? : Digit
digits = 0
digits 0 = 1
276 17 / Data Refinement and Schemas
NextPunch
∆VMDesign
d? : Digit
(0 < digits < 3 ∧ digits 0 = digits + 1) ∨
(digits = 0 ∧ digits 0 = digits)
VendDesign
∆VMDesign
o! : Status
digits 0 = 0
It should be clear that there is a refinement between the abstract and concrete
systems that we have presented. Suppose that we want the drink referred to by
the digit-sequence 238. Abstractly, the transaction proceeds by invoking the
Choose operation with input 238; next, we invoke the VendSpec operation, and
receive the indication o! telling us whether we were successful.
Concretely, the transaction proceeds by invoking the FirstPunch operation
with input 2; then NextPunch with input 3; then NextPunch with input 8; then
we invoke the VendDesign operation, and receive the indication o! telling us
whether we were successful.
There are three differences between the two systems: they have a different
set of operation names; they have different types of inputs; and they make the
nondeterministic choice at different times.
If we are to prove the refinement, then we must explain the correspond-
ence between the two sets of operations. We want the two Vend operations to
correspond, and for the abstract Choose operation to be related to the concrete
FirstPunch operation. There is no operation in the abstract interface that cor-
responds to the concrete NextPunch operation, but instead, we relate it to the
identity on the abstract state. To summarise:
The difference in the types of inputs means that we cannot use the proof
rules that we have derived from the definition of refinement. However, recall
that our treatment of inputs and outputs started by considering them as dis-
tinguished items of state. In fact, it is not necessary for the two operations to
have the same inputs and outputs. To see this, consider the retrieve relation
described by the following schema:
RetrieveVM
VMSpec
VMDesign
busy = no a digits = 0
It is sufficient to prove
Example 17.7 A simple distributed operating system allows users to store files
in a shared file store. A natural specification of this system might contain a
mapping from names to files.
b [ afs : Name →
AFS = 7 File ]
b [ AFS 0 | afs 0 = ∅ ]
AFSInit =
Read
ΞAFS
n? : Name
f ! : File
n? ∈ dom afs
f ! = afs n?
or stored in it:
17.3 / Backwards simulation 279
Store
∆AFS
f ? : File
n? : Name
n? ∉ dom afs
afs 0 = afs ∪ {n? , f ?}
CFS
cfs : Name →7 seq Byte
tfs : Name →
7 seq Byte
dom cfs ∩ dom tfs = ∅
The act of storing a file in the distributed file store is performed not by a single
operation, but by a transaction. First, the user must Start the transaction; Next
the user must transfer the file byte by byte; and finally the user must Stop the
transaction.
The transaction is started by making a reservation in the temporary file
store:
Start
∆CFS
n? : Name
n? ∉ dom cfs ∪ dom tfs
tfs 0 = tfs ⊕ {n? , hi}
cfs 0 = cfs
280 17 / Data Refinement and Schemas
Next
∆CFS
n? : Name
b? : Byte
n? ∈ dom tfs
tfs 0 = tfs ⊕ {n? , (tfs n?) _ hb?i}
cfs 0 = cfs
Stop
∆CFS
n? : Name
n? ∈ dom tfs
tfs 0 = {n?} tfs
cfs 0 = cfs ⊕ {n? , tfs n?}
The retrieve relation is simply the conversion between the abstract type of a
file and its representation as a sequence of bytes:
RetrieveACFS
AFS
CFS
afs = cfs o9 retr file
Chapter 18
Functional Refinement
When the relations used in refining or structuring our specifications turn out
to be functions, then our proof obligations can be simplified. If the retrieve
relation is functional, then we may employ a different set of proof rules; we
may even be able to proceed by calculation. If the state description involves a
functional promotion, then the promotion of the refinement is the refinement
of the promotion. In this chapter we examine these simplifications, and show
how they may be applied to the refinement of specifications.
R ⊆ S o9 f ∼ a R o9 f ⊆ S
R ⊆ S o9 f ∼
a ∀ x : X ; y : Y • x , y ∈ R ⇒ x , y ∈ S o9 f ∼ [by def of ⊆]
a ∀x : X; y : Y • [by def of o9]
x , y ∈ R ⇒ ∃z : Z • x , z ∈ S ∧ z , y ∈ f∼
a ∀x : X; y : Y • [by def of ∼ ]
x , y ∈ R ⇒ ∃z : Z • x , z ∈ S ∧ y , z ∈ f
282 18 / Functional Refinement
F-init-func-rel ci o9 f ⊆ ai
F-fin-func-rel f ∼ o9 cf ⊆ af
F-corr-func-rel dom ao / f ∼ o9 co o9 f ⊆ ao
a ∀ x : X ; y : Y • x , y ∈ R ⇒ ∃ z : Z • x , z ∈ S ∧ z = f (y) [f is total]
a ∀ x : X ; y : Y • x , y ∈ R ⇒ f (y) ∈ Z ∧ x , f (y) ∈ S [by ∃-opr]
a ∀ x : X ; y : Y • x , y ∈ R ⇒ x , f (y) ∈ S [f is total]
a ∀ x : X ; y : Y • f (y) ∈ Z ∧ x , y ∈ R ⇒ x , f (y) ∈ S [f is total]
a ∀ x : X ; y : Y ; z : Z • z = f (y) ∧ x , y ∈ R ⇒ x , z ∈ S [by ∀-opr]
a ∀x : X; y : Y; z : Z • x , y ∈ R ∧ y , z ∈ f ⇒ x , z ∈ S [f is total]
a ∀x : X; z : Z • [by pred calc]
(∃ y : Y • x , y ∈ R ∧ y , z ∈ f ) ⇒ x , z ∈ S
a ∀ x : X ; z : Z • x , z ∈ R o9 f ⇒ x , z ∈ S [by def of o9]
a R o9 f ⊆ S [by def of ⊆]
It is, in fact, not necessary to insist that f is total; the weaker condition that
ran R ⊆ dom f is enough.
Now consider the correctness rule for forwards simulation, but with the
inverse of a total function f for the retrieve relation. We must prove that
dom ao / f ∼ o9 co ⊆ ao o9 f ∼
dom ao / f ∼ o9 co o9 f ⊆ ao
In practice, the transformed rule is simpler to prove than the the original. A
similar transformation may be made to the rule for initialisation. The rules for
refinement with a total retrieve function are presented in Table 18.1.
18.2 / Functional refinement 283
F-func ∀ C • ∃1 A • R
F-init-func ∀ A0 ; C 0 • CI ∧ R0 ⇒ AI
F-corr-func ∀ A; A0 ; C; C 0 • pre AO ∧ R ∧ CO ∧ R0 ⇒ AO
∀ A; C • pre AO ∧ R ⇒ pre CO
The new rules for the refinement of relations give rise to a set of rules for
the functional refinement of specifications. Any application of these rules will
require a proof that the retrieve relation is a total function: that is,
∀ C • ∃1 A • R
Once this requirement has been established, we are left with the rules shown
in Table 18.2. Notice that these are easier to apply than those given at the end
of the previous chapter.
If we are able to present a set of equations that defines each abstract com-
ponent as a total function of concrete components, then our retrieve relation
must be a function. The resulting form of retrieve relation is quite common in
realistic specifications.
Example 18.1 In Example 17.1, the retrieve relation was defined by an equation
expressing the abstract component—a set—as a total function of the concrete
component—a list.
ListRetrieveSet
ASystem
CSystem
s = ran l
Since the retrieve relation is a total function, we could have proved the
simpler set of proof obligations:
∀ CSystem 0 ; ASystem 0 •
CSystemInit ∧ ListRetrieveSet 0 ⇒ ASystemInit
F-init-calc-rel wi == ai o9 f ∼
F-corr-calc-rel wo == f o
9 ao o9 f ∼
wo == f o
9 ao o9 f ∼
To see how this works, observe that the definition chosen for wo solves the
forwards simulation inequality exactly. The result is the largest relation that is
a refinement of ao.
f ∼ o9 wo
= f ∼ o9 f o
9 ao o9 f ∼ [by definition]
= id[ran f ] o9 ao o9 f ∼ [by relational calculus]
= ao o9 f ∼ [since f is surjective]
Again, notice that, rather than insisting that f is surjective, the weaker condition
that dom ao ⊆ ran f would suffice. The rules for calculating refinements are
given in Table 18.3.
To see how this extends to specifications, suppose that we have an abstract
state described by the schema A, an initialisation
b [ A0 | p ]
AI =
and an operation
b [ ∆A | q ]
AO =
Suppose further that we have a total surjective function f from the concrete
state C to A which is used in the retrieve relation
b [ A; C | θA = f (θC) ]
F =
286 18 / Functional Refinement
F-init-calc CI =
b CI o
9 F0
F-corr-calc CO =
b F o
9 AO o9 F 0
If this is the case, then the weakest refinement with respect to f is given by the
initialisation
CI =
b CI o
9 F0 = [ C 0 | p[f (θC 0 )/θA0 ] ]
CO =
b F o
9 AO o9 F 0 = [ ∆C | q[f (θC), f (θC 0 )/θA, θA0 ] ]
FTemp
f : ◦F
StdTemp == 65
FTempInit
FTemp 0
f 0 = StdTemp
The temperature can always be incremented, provided that the value does not
go above the maximum:
FTInc
∆FTemp
f ≤ 4999
f0 = f +1
Similarly, the temperature can always be decremented, provided that the value
does not go below the minimum:
FTDec
∆FTemp
f ≥ −458.4
f0 = f −1
At the design stage, the internal representation need not be kept in Fahren-
heit: it could easily be maintained in Celsius. Celsius values are those above
288 18 / Functional Refinement
b [c : C ]
CTemp =
RetrieveFC
FTemp
CTemp
f = (9/5) ∗ c + 32
CTemp 0
(9/5) ∗ c 0 + 32 = StdTemp
We may rewrite the predicate part of this schema to make the initial value of c 0
more explicit:
CTempInit
CTemp 0
c 0 = (5/9) ∗ (StdTemp − 32)
∆CTemp
(9/5) ∗ c + 32 ≤ 4999
(9/5) ∗ c 0 + 32 = (9/5) ∗ c + 32 + 1
CTInc
∆CTemp
c ≤ 2759 ∗ (4/9)
c 0 = c + (5/9)
∆CTemp
(9/5) ∗ c + 32 ≥ −458.4
(9/5) ∗ c 0 + 32 = (9/5) ∗ c + 32 − 1
CTDec
∆CTemp
c > 272 ∗ (4/9)
c 0 = c − (5/9)
Given the new representation, we can calculate the refinement. The calculation
is straightforward, although the results may need to be simplified before they
are used; this is typical of the calculational approach.
b [f : I →
P= 7 S]
Promote
∆S
∆P
i? : I
i? ∈ dom f
θS = f (i?)
f 0 = f ⊕ {i? , θS 0 }
PO =
b ∃ ∆S • Promote ∧ SO
[X , Y ]
apply : (X → Y ) × X → Y
ovr : ((X → Y ) × X ) × Y → X → Y
img : (X ↔ Y ) → P X → P Y
∀ f : X → Y ; x : X • apply(f , x) = f (x)
∀ f : X → Y ; x : X ; y : Y • ovr ((f , x), y) = f ⊕ {x , y}
∀ R : X ↔ Y ; S : P X • (img R)S = R (| S |)
id
Φ(op) == cp o
9 k o
9 ovr
apply o9 op
r = img(id k ρ)
Observe that each side is a relation drawn from A × I ↔ A; we require the index
of that part of the state which is being updated.
We may rewrite the left-hand side of this inequation using the laws of the
relational calculus, the definitions of apply and ovr , and the properties of our
retrieve and promotion relations.
(r k id) o9 Φ(co)
id
= (r k id) o9 cp o
9 k o
9 ovr [by definition of Φ(co)]
apply co o
9
r k id id
⊆ cp o
9 k o
9 k o
9 ovr [property of cp]
r k id apply co o
9
(r k id) o9 id
= cp o
9 k o
9 ovr [abiding property]
(r k id) apply co
o
9
o
9
r k id
= cp o
9 k o
9 ovr [id unit]
(img(id k ρ) k id) o9 apply o9 co
r k id
= cp o
9 k o
9 ovr [property of apply]
apply ρ co
o
9
o
9
r k id
⊆ cp o
9 k o
9 ovr [since A v C]
apply o9 ao o9 ρ
id o9 (r k id)
= cp o
9 k o
9 ovr [id unit]
apply ao ρ
o
9
o
9
id r k id
= cp o
9 k o
9 k o
9 ovr [abiding property]
apply ao o
9 ρ
292 18 / Functional Refinement
A ao
C co
(ao)
(A)
(C) (co)
id img(id k ρ) k id
= cp o
9 k o
9 k o
9 ovr [definition of r ]
apply o9 ao ρ
id
= cp o
9 k o
9 ovr o
9 img(id k ρ) [property of ovr ]
apply ao
o
9
id
= cp o
9 k o
9 ovr o
9 r [definition of r ]
apply o9 ao
= Φ(ao) o9 r [definition of Φ(ao)]
This shows that the promotion of co correctly refines the promotion of ao, as
in Figure 18.1. The application of this should be obvious: we may specify an
abstract data type, and then calculate its promotion. When we come to refine
the abstract data type, the same calculation promotes the more concrete data
type into a correct refinement of the promoted system.
18.4 / Refining promotion 293
Example 18.5 The simple temperature sensor may be used in situations where
there is more than one temperature to record. As a specification, we may take
the obvious promotion of the Fahrenheit data type. If Ind represent the set of
indices used to refer to the individual temperatures, then the state consists of
a total function:
b [ fd : Ind → FTemp ]
FTDisplay =
FTPromote
∆FTDisplay
∆FTemp
i? : Ind
θFTemp = fd i?
fd 0 = fd ⊕ {i? , θFTemp 0 }
FTDisplayInc =
b ∃ ∆FTemp • FTPromote ∧ FTInc
FTDisplayDec =
b ∃ ∆FTemp • FTPromote ∧ FTDec
b [ cd : Ind → CTemp ]
CTDisplay =
CTPromote
∆CTDisplay
∆CTemp
i? : Ind
θCTemp = cd i?
cd 0 = cd ⊕ {i? , θCTemp 0 }
CTDisplayInc =
b ∃ ∆CTemp • CTPromote ∧ CTInc
CTDisplayDec =
b ∃ ∆CTemp • CTPromote ∧ CTDec
Chapter 19
Refinement Calculus
Specification (schemas)
e refinement
Design (schemas)
e translation
e refinement
e translation
The abstract programming notation that we will use is a refinement calculus for
the schema language. In this chapter, we will see how to translate schemas into
specification statements, and how to refine these statements towards executable
program code.
296 19 / Refinement Calculus
The operation changes the value of available! so that it is equal to the value of
free(θBoxOffice).
This represents a program that may fail to terminate whatever the initial state.
Even if it does terminate, we can say nothing about the final state.
Another statement that may produce any result is choose w. In this case,
the program is guaranteed to terminate, but may do anything to the values of
variables in w:
The special case in which there are no variables to modify—the list w is empty—
is described by skip:
19.2 Assignment
x0 = X − Y
" #
x=X
x, y : ; x := y − x
y =Y , y0 = X
P vQ ∧ Q vR ⇒ P vR
In our use of the refinement calculus, each refinement step will correspond to
the application of a law involving the v symbol.
A simple way to refine a specification statement is to strengthen the post-
condition: we are then agreeing to do more than was originally required.
—then
v (strengthen postcondition)
The refinement step has produced a more deterministic program, one that in-
sists upon a closer approximation.
19.2 / Assignment 299
we have that
v (weaken precondition)
pre ⇒ ∃ x 0 : X ; y 0 : Y • post
f (a) ∗ f (b) ≤ 0
f (a) ∗ f (b) ≤ 0 ⇒
∃ m 0 : R • −0.01 < f (m 0 ) < 0.01 ∧ a ≤ m 0 ≤ b
is infeasible.
300 19 / Refinement Calculus
Law 19.3 (introduce local block) If x is a variable that does not appear in frame
w, and inv is a predicate on x, then
Where the scope of the declaration is clear, we may choose to omit the begin
and end symbols.
Example 19.6
begin
var x, y : T •
−0.1 < f (m 0 ) < 0.1
" #
f (a) ∗ f (b) ≤ 0
x, y, m :
a≤b , a ≤ m0 ≤ b
end
x : [ true, x 0 < x ]
19.2 / Assignment 301
which requires that x be decreased: its value afterwards must be strictly less
than its value before.
If there is a substitution of decorated variables that makes the postcon-
dition an immediate consequence of the precondition, then we may refine the
specification statement to an assignment.
then
w, x : [ pre, post ] v w := E
Any variable that is not assigned to will have the same value after the operation
as before, hence the substitution of x for x 0 above.
x : [ true, x 0 = x + 1 ] v x := x + 1
If the current values of the variables in the frame are enough to make the
postcondition true, then the specification statement can be replaced by skip.
x, y : [ x = 5 ∧ y = x 3 , x 0 = 5 ]
v skip
302 19 / Refinement Calculus
x0 = X − Y
" #
x=X
x, y : ; x := y − x
y =Y , y0 = X
The variables X and Y will not form part of the final code; they are merely a
logical device. They are called logical constants, and may be declared as follows:
begin
con X • x : [ x = X , x 0 > X ]
end ;
begin
con X • x : [ x = X , x 0 > X ]
end
The scope of each logical constant is important. The following statement de-
scribes a program with quite different behaviour:
begin
con X • x : [ x = X , x 0 > X ] ; x : [ x = X , x 0 > X ]
end
Logical constants are used to give names to things that already exist. A
simple example is the way in which they may be used to fix the before value of
a variable: for example,
The variable X takes a value that makes subsequent preconditions true, if pos-
sible. Within the scope of X , there is only one precondition, x = X , and X takes
19.3 / Logical constants 303
on this value: namely, the value of x before the specification statement. This
particular statement is equivalent to
More generally, we can introduce a logical constant in much the same way as
we would introduce an existential quantifier.
Logical constants are rarely found in programming languages, and are cer-
tainly not the kind of constant that one finds in C. In order to declare a more
conventional constant, one can introduce a state variable which has an invari-
ant that says that it never changes. For example, one might wish to declare a
constant whose value is a fixed approximation to π . This is done by introducing
a variable, and then constraining it to be constant:
begin
var pi : R | pi = 22/7 •
..
.
end
304 19 / Refinement Calculus
x : [ pre, mid ]
To obtain the precondition and postcondition for the second program, some
substitutions are necessary.
In the precondition of the second program, we use x to refer to the value of
x when that program starts. This is precisely the value of that variable when the
first program finishes, represented by x 0 in mid. The precondition might also
make reference to the value of x when the first program starts, represented by
x in mid. To avoid confusion, we will replace this value with a logical constant
X.
The postcondition is similar to post , except that we can no longer use x to
refer to the value of x when the first program starts. We must replace x with
the same logical constant X . The second program is then
Notice that the second task may involve changing variables other than x.
The predicate mid must not contain before variables other than x.
Example 19.10
x : [ true, x 0 = x + 2 ]
19.4 / Sequential composition 305
con X •
x : [ true, x 0 = x + 1 ] ;
x : [ x = X + 1, x 0 = X + 2 ]
Example 19.11 Suppose that we want to swap two variables without using a
third variable to store the intermediate value. Suitable code might be developed
as follows:
x, y : [ true, x 0 = y ∧ y 0 = x ]
v (assignment introduction)
x := x − y
‡
v (assignment introduction)
y := x + y
†
v (assignment introduction)
x := y − x
Note the use of marginal markers. The / symbol always points to the next
part of the program to be refined. Other marginal markers refer to parts of the
program whose development proceeds at a later point.
306 19 / Refinement Calculus
x, y : [ x = X ∧ y = Y , x = Y ∧ y = X ]
v
begin con X1 , Y1 : Z •
begin con X2 , Y2 •
x := x − y ;
y := x + y
end ;
x := y − x
end
v
x := x − y ;
y := x + y ;
x := y − x
Law 19.9 (simple sequential composition) If the predicates mid and post make
no reference to before variables, then
19.5 / Conditional statements 307
These rules are easy to apply. First we decide upon the assignment that is to
be performed, then we calculate the new specification statement.
if G1 → com1
G2 → com2
..
.
Gn → comn
fi
if G1 → w : [ G1 ∧ pre, post ]
G2 → w : [ G2 ∧ pre, post ]
..
w : [ pre, post ] v .
Gn → w : [ Gn ∧ pre, post ]
fi
If the specification is required to terminate, then the conditional must not abort:
the precondition must establish that at least one guard is true. Furthermore,
whichever branch is taken must implement the specification, under the assump-
tion that the corresponding guard is true.
We will also employ a generalised form of the conditional statement, in
which the indexing variable may take any finite range:
if i : S Gi → comi fi
308 19 / Refinement Calculus
Example 19.12 Given two variables x and y, we may develop a program that will
ensure that x ≤ y, by preserving their values, or swapping them if necessary:
x ≤ y ∧ x0 = x ∧ y0 = y
x, y :
∨
true , y ≤ x ∧ x 0 = y ∧ y 0 = x
v (conditional introduction)
x ≤ y ∧ x0 = x ∧ y0 = y
if x ≤ y → x, y :
∨
[/]
x ≤ y , y ≤ x ∧ x0 = y ∧ y0 = x
x ≤ y ∧ x0 = x ∧ y0 = y
y ≤ x → x, y : ∨
[†]
0 0
y ≤ x , y ≤ x ∧ x = y ∧ y = x)
fi
v (strengthen postcondition)
x, y : [ x ≤ y, x ≤ y ∧ x 0 = x ∧ y 0 = y ]
v (skip introduction)
skip
†
v (strengthen postcondition)
x, y : [ y ≤ x, y ≤ x ∧ x 0 = y ∧ y 0 = x ]
v (assignment introduction)
x, y := y, x
19.6 / Iteration 309
Notice that the disjunction of the guards is true, thus validating the introduction
of the conditional. The program is
if x ≤ y → skip
y ≤ x → x, y := y, x
fi
To end this section, we present a pair of laws for manipulating the frame
in a specification statement. If we drop the name of a variable from the frame,
then it cannot change; thus, we may remove the after decoration from any of
its occurrences in the postcondition.
19.6 Iteration
do G1 → com1
G2 → com2
..
.
Gn → comn
od
When this statement is reached, the guards are evaluated and one of the com-
mands whose guard is true is executed. This is repeated until no guard is true,
at which point the statement terminates.
310 19 / Refinement Calculus
To see how a refinement law for this construct may be formulated, sup-
pose that inv is an invariant property of the loop: that is, a predicate that must
be true before and after every iteration. Suppose further that G represents the
disjunction of guards: the condition that must be true if the statement is not to
terminate immediately. Such a loop may refine a specification statement that
includes inv in the precondition, and inv ∧ ¬G in the postcondition.
For the refinement to be correct, the loop must be guaranteed to terminate.
It is enough to identify a variant for the loop: an integer-valued function that
must decrease with each iteration, but which never passes zero. If V is such a
function, then the loop
will refine the statement w : [ inv, inv[w 0 /w] ∧ ¬G[w 0 /w] ]. More formally,
we have the following law:
begin
conI : 1 . . #s •
i : [ s(I ) = target , s(i 0 ) = target ]
end
Our variant will be I − i, and our invariant will be that I indexes the target , and
that we haven’t passed it by: s(I ) = target ∧ i ≤ I .
In order to set up the pattern for the loop introduction rule, we split the
specification in two, thus:
Now the specification matches the pattern for the application of the loop
rule, we turn the handle, and out pops
do
s(i) ≠ target
→
s(I ) = target s(I ) = target
i0 ≤ I
i :
i ≤I
s(i) ≠ target , 0 ≤ I − i 0 < I − i
od
i := 1 ; do
s(i) ≠ target →
i := i + 1
od
these were, the appropriate pattern was set up by splitting the specification into
two, the first part becoming the initialisation, and the second part becoming
the loop itself; the invariant defined what the mid predicate should be. The
postcondition was then strengthened so that it contained the invariant, and
the remainder formed the (negated) guard.
Example 19.14 Suppose that we wish to initialise an integer array so that every
entry is zero. An array may be modelled mathematically as a total function from
a set of indices to values (in this case, numbers):
ar : (1..n) → N
The initialisation operation has the task of assigning the value 0 to every ele-
ment in the array. Its specification is given by
Our first step in developing the code for this rather simple operation is to
use an obvious transformation of the postcondition:
The reason for this is that we intend to implement the operation using a loop,
and the universal quantifier points to the way that the loop might be developed.
One strategy for loop development is to take such a quantified expression, and
replace a constant by a variable. The following shorthand helps us in doing
this:
zeroed(i, ar ) = ∀ j : 1..i • ar j = 0
The development of the code now follows. The refinement calculus should be
used with a light touch, rather than in this heavy-handed manner; however, we
go into greater detail so that the reader may follow our reasoning.
ar : [ true, zeroed(n, ar 0 ) ]
v
var j : 1 . . n + 1 •
j, ar : [ true, zeroed(n, ar 0 ) ]
The variable j will be used as a loop counter; thus it will range from the smallest
element in the domain of ar to just after the highest.
We introduce the semicolon in order to choose the loop invariant. At the be-
ginning of the loop, and after each iteration, we will have zeroed all the blocks
up, but not including j. The specification statement before the semicolon must
establish the invariant, and the one after must be developed into the loop.
v (assignment introduction)
j := 1
†
v (strengthen postcondition)
j, ar : [ zeroed(j − 1, ar ), zeroed(j 0 − 1, ar 0 ) ∧ j 0 = n + 1 ]
v (loop introduction)
do j ≠ n + 1 →
0 ≤ n − j0 + 1 < n − j + 1
" #
j ≠n+1
j, ar :
zeroed(j − 1, ar ) , zeroed(j 0 − 1, ar 0 )
od
v (following assignment)
" #
j ≠n+1
ar : ; [/]
zeroed(j − 1, ar ) , zeroed(j, ar 0 )
j := j + 1
The only thing left to do is to free the next element of ar , that is, the j th
element:
v (assignment introduction)
ar := ar ⊕ {j , 0}
There are several small proof obligations to be fulfilled: the more signific-
ant ones are
• zeroed(0, ar )
• zeroed(j − 1, ar ) ∧ j = n + 1 ⇒ zeroed(n, ar )
The first and third predicates are simple properties of zeroed, and the second
follows from the properties of equality. Summarising our development, we
have shown that Init is refined by
begin
var j : N | 1 ≤ j ≤ n + 1 •
j := 1 ;
do j ≠ n + 1 →
ar := update(ar , j, 0) ;
j := j + 1
od
end
PROCEDURE Init ;
BEGIN
FOR j := 1 TO n DO ar[j] := 0
END
following expression:
1 ∗ 27 + 0 ∗ 26 + 0 ∗ 25 + 1 ∗ 24 + 1 ∗ 23 + 1 ∗ 22 + 0 ∗ 21 + 0 ∗ 20
= 156
Clearly, the calculation has used a polynomial to relate binary to decimal num-
bers. To express this, suppose for convenience that the number in base β is
presented in an array of n elements, with each digit in a separate element, and
with a1 representing the least significant digit. The elements of the array rep-
resent the coefficients of a polynomial of degree n − 1:
a1 + a2 ∗ β + a3 ∗ β2 + · · · + an ∗ βn−1
How should we develop some code that will evaluate this polynomial for a
particular value of β? A straightforward approach might be to adapt some
code that simply sums the elements of an array. If we insert into each location
i in the array the value of the expression ai ∗ βi−1 , then this solution will be
correct; however, it is far from optimal.
A more efficient algorithm may be developed if we recall from numerical
analysis the method known as Horner’s rule. This is based on the identity:
a1 + a2 ∗ β + a3 ∗ β2 + · · · + an ∗ βn−1
= a1 + β ∗ (a2 + β ∗ (· · · β ∗ (an−2 + β ∗ (an−1 + β ∗ an )) · · ·))
The intended algorithm starts from the high-order coefficients and works
downwards. Clearly this requires fewer multiplications than the straightfor-
ward approach. As a formal specification, let’s take the recurrence relation
that a numerical analyst would use:
Pn
i=1 ai ∗ βi−1 = H1,n
where
Hn,n = an
Hi,n = ai + β ∗ Hi+1,n for i < n
Now, suppose that we have a number in base β with digits an an−1 . . . a2 a1 , then
our algorithm must satisfy the specification
Pn
d : [ true, d 0 = i=1 ai ∗ βi−1 ]
d : [ true, d 0 = H1,n ]
316 19 / Refinement Calculus
The strategy for calculating the code for this algorithm is quite clear: we can
develop a loop which varies the first index of H . It is easy enough to establish
Hn,n , and we want to end up with H1,n , so the loop counter is decreasing, and
the invariant will involve d = Hj,n , for loop counter j.
d : [ true, d 0 = H1,n ]
v
var j : 1 . . n •
d, j : [ true, d 0 = H1,n ]
v (assignment introduction)
d, j := an , n
†
v (strengthen postcondition)
d, j : [ d = Hj,n , d 0 = H1,n ∧ j 0 = 1 ]
v (strengthen postcondition)
d, j : [ d = Hj,n , d 0 = Hj 0 ,n ∧ j 0 = 1 ]
The second refinement is, of course, an equivalence. We are now ready to in-
troduce a loop:
v (loop introduction)
do
j ≠1→
0 ≤ j0 < j
" #
j ≠1
d, j :
d = Hj,n , d 0 = Hj 0 ,n
od
0 ≤ j0 ≤ j
! !
j ≠0
d, j : [ [j − 1/j], [j − 1/j] ]
d = Hj+1,n d 0 = Hj 0 ,n
19.6 / Iteration 317
v (leading assignment)
j := j − 1 ;
0 ≤ j0 ≤ j
" #
j ≠0
d, j :
d = Hj+1,n , d 0 = Hj 0 ,n
v (contract frame)
" #
j ≠0 0≤j ≤j
d:
d = Hj+1,n , d 0 = Hj,n
v (strengthen postcondition)
d : [ j ≠ 0 ∧ d = Hj+1,n , d 0 = aj + β ∗ Hj+1,n ]
v (strengthen postcondition)
d : [ j ≠ 0 ∧ d = Hj+1,n , d 0 = aj + β ∗ d ]
v (assignment introduction)
d := aj + β ∗ d
begin
var j : 1 . . n •
d, j := an , n ;
do j ≠ 1 →
j := j − 1 ;
d := aj + x ∗ d
od
end
PROCEDURE Translate ;
BEGIN
d := a[n] ;
FOR j := n DOWNTO 1 DO
d := a[j] + x * d
END
Chapter 20
A Telecommunications Protocol
This chapter describes a case study in using abstraction and refinement. The
subject of the study is a telecommunications protocol, Signalling System No. 7,
an international standard for signalling between telephone exchanges.
We begin with an abstract specification of the protocol. This may then
serve as an independent correctness criterion for a subsequent refinement. The
abstraction explains the service offered by the protocol; the refinement explains
how this is to be achieved.
Let M be the set of messages that the protocol handles. The abstract specific-
ation of this protocol is quite small, and we call it the external view:
Ext
in, out : seq M
∃ s : seq M • in = s _ out
The state comprises two sequences: the messages that have come in and those
that have gone out. We will use these sequences to keep track of the message
traffic handled by the protocol.
As we shall see, messages will be added to the left-hand end of these se-
quences, so that the oldest messages are to be found towards the right. The
invariant states that the out sequence is a suffix of the in sequence: the pro-
tocol must deliver messages without corruption or re-ordering. The missing
messages are in flight, as it were, in the system. For example, a suitable pair of
320 20 / A Telecommunications Protocol
This models the situation in which five messages have been input to the pro-
tocol, three have been output, and two are still in flight.
Initially, no messages have been sent:
b [ Ext 0 | in 0 = hi ]
ExtInit =
Transmit
∆Ext
m? : M
in 0 = hm?i _ in
out 0 = out
Messages are received from the in-flight stream, which might not have reached
the destination. Thus, either the output sequence gets one longer (and by the
state invariant, this must be the next message in sequence), or there is no mes-
sage available in flight:
Receive
∆Ext
in 0 = in
#out 0 = #out + 1 ∨ out 0 = out
There are many other operations relevant to trunk signalling, but these two will
serve to illustrate our refinement.
Section
route : iseq SPC
rec, ins, sent : seq(seq M )
route ≠ hi
#route = #rec = #ins = #sent
rec = ins _
_ sent
front sent = tail rec
where ins represents the sequence of messages currently inside this section,
rec represents the sequence of messages that have been received, and sent
represents the sequence of messages that have been sent.
The expression ins _
_ sent denotes the pair-wise concatenation of the two
sequences. Thus, if we have that
rec = ins _
_ sent
[X ]
_
_ : seq(seq X ) × seq(seq X ) →
7 seq(seq X )
∀ s, t : seq(seq X ) | #s = #t •
∀ i : dom s •
(s _ t )i = (s i) _ (t i)
That is, the pairwise concatenation of any two sequences of sequences is the
sequence obtained by concatenating each element of the first sequence with
the corresponding element of the second.
We now rewrite our operations in terms of the new viewpoint. We leave
the route component unspecified in the initialisation: the resulting specification
322 20 / A Telecommunications Protocol
SectionInit
Section 0
∀ i : dom route0 • rec 0 i = ins 0 i = sent 0 i = hi
In Transmit , the new message is received by the first section in the route; that
is, it is added to the sequence head rec:
STransmit
∆Section
m? : M
route0 = route
head rec 0 = hm?i _ (head rec)
tail rec 0 = tail rec
head ins 0 = hm?i _ (head ins)
tail ins 0 = tail ins
sent 0 = sent
The Receive operation should be delivering the message to the output from the
protocol. In the sectional view, this means transferring it to last sent . But where
should this message come from? The answer is that it comes from within the
previous section:
SReceive
∆Section
route0 = route
rec 0 = rec
front ins 0 = front ins
last ins 0 = front (last ins)
front sent 0 = front sent
last sent 0 = hlast (last ins)i _ (last sent )
Daemon0
∆Section
∃ i : 1 . . #route − 1 |
ins i ≠ hi •
ins 0 i = front (ins i)
ins 0 (i + 1) = hlast (ins i)i _ ins(i + 1)
∀ j : dom route | j ≠ i ∧ j ≠ i + 1 • ins 0 j = ins j
This operation is not part of the user interface. The user cannot invoke Daemon,
but it is essential to our understanding of the system and to its correctness.
How do such operations fit into our view of specification?
We imagine such operations as daemons that work in the background,
invoked nondeterministically. It should be clear that we could dispense with
such operations, but only by adding the required degree of nondeterminism to
the remainder of our specification. The important thing about a daemon is that
its effects are invisible from an abstract point of view. In this specification, the
sectional operation Daemon corresponds to the external operation ΞExt .
We should remark that the specification of the abstract state does not in-
sist that messages actually arrive, since it is possible to satisfy the specification
trivially by insisting that out = hi. This problem could be addressed by adding
an integer-valued variant to the state to measure the progress of a message
through the system. We could then add a constraint to each operation to insist
that the variant is decreased while the message is in flight. This would then be
enough to guarantee delivery.
We should prove that two views that we have of our protocol are consistent,
both internally and with one another. In our description of the external view,
we insisted that the protocol should deliver messages without corruption or
re-ordering. To retrieve the external view from the sectional view, we have only
to observe that the head of rec corresponds to the sequence in; the last of
sent corresponds to the sequence out ; and the distributed concatenation of ins
corresponds to the discrepancy between them.
The consistency of the external view is summed up by the predicate part
324 20 / A Telecommunications Protocol
∃ s : seq M • in = s _ out
There must be some sequence of messages s which, when prefixed to the se-
quence out , produces the sequence in.
The consistency of the sectional view depends on the same property hold-
ing true in terms of the concrete model. That is, with head rec in place of in,
last sent in place of out , and the contents of all the sections, in order, in place
of the existentially quantified variable s.
The following inference asserts that this property holds in Section:
Section
head rec = (_/ ins) _ (last sent )
We may prove this by induction upon the length of the route, #route. The base
case requires that the result holds for #route = 1:
The sectional view is more detailed than the external view, and the above
result helps to demonstrate that it is in some way a refinement. The proof of
20.3 / Relationship between external and sectional views 325
RetrieveExtSection
Ext
Section
in = head rec
out = last sent
This may be used to prove that the various operations on the concrete sectional
view are correct with respect to their corresponding abstractions. Notice that
the retrieve relation is actually functional from concrete to abstract. We must
prove that the retrieve relation is a total function:
The additional detail in the sectional view allows us to introduce concepts that
were not relevant in the external view. For example, we may wish to add a new
signalling point to the route. This operation has no relevance at the higher
level, and cannot be expressed in terms of the abstract model.
To describe such an operation, we require an operator on sequences that
allows us to insert an additional element after a given point: for example,
[X ]
insert : seq X × (N × X ) → seq X
∀ s : seq X ; i : N; x : X •
s insert (i, x) = (1 . . i) / s _ hxi _ squash((1 . . i) s)
InsertSection
∆Section
s?, new? : SPC
s? ∈ ran(front route)
new? ∉ ran route
∃ i : 1 . . (#route − 1) |
i = route∼ s? •
route0 = route insert (i, new?)
rec 0 = rec insert (i, sent i)
ins 0 = ins insert (i, hi)
sent 0 = sent insert (i, rec i + 1)
To end the chapter, we prove a useful result about our design: messages
in transit are unaffected by the insertion of a new section. If ins and ins 0 are
related according to the definition of InsertSection above, then
_/ ins 0
20.4 / Enriching the model 327
This shows that messages in transit are not affected by the insertion of a new
signalling point in the route. That is, the lower-level operation of manipulating
the route in this way is invisible at the higher level of abstraction. The operation
is therefore a daemon; this is important for the integrity of the abstraction.
Chapter 21
21.1 Processes
Current Since there is a single processor, at any time, there will be at most one
process running. We will call this the current process.
Ready There may be several processes that are waiting to use the processor.
These processes are said to be ready.
330 21 / An Operating System Scheduler
Blocked There may be some processes that are waiting, not for the processor,
but for a different resource or event. These processes are said to be
blocked.
While a process is running it has exclusive use of the processor. At some point,
control will be passed to the kernel. This may happen because the current
process has issued a service call, or it may be that the kernel interrupts it.
Whatever, the scheduler will be asked to dispatch another process.
21.2 Specification
n:N
Each process will be associated with a process identifier, or pid. For our pur-
poses, a pid can be represented by a number between 1 and n.
PId == 1 . . n
Zero is used to represent the ‘null process’: a marker that says that there is no
process where this value is found.
nullPId == 0
The abstract state of the scheduler classifies every process in one of four ways:
a process is either the current process, or it is ready, blocked, or free. There
might not be a current process:
AScheduler
current : OptPId
ready : P PId
blocked : P PId
free : P PId
h{current } \ {nullPId},
ready,
blocked,
freei partition PId
21.2 / Specification 331
The free set describes those process identifiers that are not currently in
use. Initially, all process identifiers are free, and there is no current process:
ASchedulerInit
AScheduler 0
current 0 = nullPId
ready 0 = ∅
blocked 0 = ∅
free0 = PId
If there is no current process, then any process that is ready may be dispatched
to the processor:
ADispatch
∆AScheduler
p! : PId
current = nullPId
ready ≠ ∅
current 0 ∈ ready
ready 0 = ready \ {current 0 }
blocked 0 = blocked
free0 = free
p! = current 0
ATimeOut
∆AScheduler
p! : PId
current ≠ nullPId
current 0 = nullPId
ready 0 = ready ∪ {current }
blocked 0 = blocked
free0 = free
p! = current
ABlock
∆AScheduler
p! : PId
current ≠ nullPId
current 0 = nullPId
ready 0 = ready
blocked 0 = blocked ∪ {current }
free0 = free
p! = current
A blocked process may be woken up. This is the system signalling that the
required resource is now available. The woken process must wait its turn for
scheduling, so it is added to the set of ready processes:
AWakeUp
∆AScheduler
p? : PId
p? ∈ blocked
current 0 = current
ready 0 = ready ∪ {p?}
blocked 0 = blocked \ {p?}
free0 = free
When a process is created, an identifier must be assigned from the set of free
identifiers. Clearly, the free set must be non-empty for this to be possible:
ACreate
∆AScheduler
p! : PId
free ≠ ∅
current 0 = current
ready 0 = ready ∪ {p!}
blocked 0 = blocked
free0 = free \ {p!}
p! ∈ free
At the end of its life, a process may be destroyed. If the designated process
21.2 / Specification 333
is the current process, then afterwards there is no current process, and the
process identifier becomes available for further use.
ADestroyCurrent
∆AScheduler
p? : PId
p? = current
current 0 = nullPId
ready 0 = ready
blocked 0 = blocked
free0 = free ∪ {p?}
If the process is ready, it is destroyed and the identifier becomes available for
further use:
ADestroyReady
∆AScheduler
p? : PId
p? ∈ ready
current 0 = current
ready 0 = ready \ {p?}
blocked 0 = blocked
free0 = free ∪ {p?}
ADestroyBlocked
∆AScheduler
p? : PId
p? ∈ blocked
current 0 = current
ready 0 = ready
blocked 0 = blocked \ {p?}
free0 = free ∪ {p?}
ADestroy =
b ADestroyCurrent ∨ ADestroyReady ∨ ADestroyBlocked
334 21 / An Operating System Scheduler
21.3 Chains
Chain
start , end : OptPId
links : PId )7 7 PId
set : F PId
set = dom links ∪ ran links ∪ ({start } \ {nullPId})
links = ∅ ⇒ start = end
links ≠ ∅ ⇒
{start } = (dom links) \ ran links
{end} = (ran links) \ dom links
∀ e : set | e ≠ start • start , e ∈ links +
The final part of the data type invariant insists that the elements of the chain
are connected, in the sense that every one may be reached from the start pid by
applying the function links a finite number of times. This is enough to guarantee
the uniqueness of the start and end elements.
For convenience, we use the null pid to represent the start and end points
of an empty chain. With the above invariant, it is the case that
ChainInit
Chain 0
start 0 = nullPId
end 0 = nullPId
21.3 / Chains 335
We will define three operations on elements of this data type. The first
describes the effect of pushing an element onto the end of a chain. There are
two cases to consider. If there is no end point, then the injection must be empty,
and the new element will become the end point:
PushEmpty
∆Chain
p? : PId
end = nullPId
end 0 = p?
links 0 = links
If there is an end point, then we update the chain so that the end points to the
new element:
PushNonEmpty
∆Chain
p? : PId
end ≠ nullPId
links 0 = links ∪ {end , p?}
Push =
b PushEmpty ∨ PushNonEmpty
Our second operation describes the effect of popping an element from the
front of a chain; this will be successful if there is at least one element present.
Again, there are two cases to consider. If the chain has only one element—if
links must be empty but start is not null—then the new start will be null:
PopSingleton
∆Chain
p! : PId
start ≠ nullPId
links = ∅
start 0 = nullPId
links 0 = links
p! = start
Notice that the new value of end is determined by the state invariant.
336 21 / An Operating System Scheduler
If there is more than one element in the chain, then the start element is
provided as output, and the start point moved along the chain.
PopMultiple
∆Chain
p! : PId
links ≠ ∅
start 0 = links start
links 0 = {start } links
p! = start
Pop =
b PopSingleton ∨ PopMultiple
DeleteStart
∆Chain
p? : PId
p? = start
∃ p! : PId • Pop
Notice how we simply ignore the popped element, hiding the output from the
Pop operation within an existential quantification.
If the designated element is at the end of the chain, then the effect is
different. The last link in the chain simply disappears:
DeleteEnd
∆Chain
p? : PId
p? ≠ start
p? = end
links 0 = links {end}
~
links p? p? links p?
If the designated element is in the chain, but at neither end, then the
effect is different again. Consider the situation pictured in Figure 21.1. The
previous element—which is identified as links ∼ p?—will be mapped to the next
element—which is identified as links p?. In the following schema, p? itself is
removed using domain restriction:
DeleteMiddle
∆Chain
p? : PId
p? ≠ start
p? ≠ end
p? ∈ set
links 0 = {p?} links ⊕ {links ∼ p? , links p?}
Delete =
b DeleteStart ∨ DeleteMiddle ∨ DeleteEnd
21.4 Design
We will now formulate a design for the scheduler based upon the chain data
type. Our design will involve three chains: one each for the sets of ready,
blocked, and free processes.
ReadyChain =b
Chain[rstart /start , rend/end, rlinks/links, rset /set ]
BlockedChain =b
Chain[bstart /start , bend/end, blinks/links, bset /set ]
FreeChain =b
Chain[fstart /start , fend/end, flinks/links, fset /set ]
338 21 / An Operating System Scheduler
The initial states of the ready and blocked chains are defined in terms of
the initialisation schema ChainInit :
ReadyChainInit = b
ChainInit [rstart 0 /start 0 , rend 0 /end 0 , rlinks 0 /links 0 , rset 0 /set 0 ]
BlockedChainInit = b
ChainInit [bstart 0 /start 0 , bend 0 /end 0 , blinks 0 /links 0 , bset 0 /set 0 ]
FreeChainFull
FreeChain
fset 0 = PId
We will require push and pop operations on the ready and free chains,
and push and delete operations on the blocked chain. We may define these by
renaming the components of the corresponding operations on Chain:
PushReadyChain = b
Push[rstart /start , rend/end, rlinks/links, rset /set ,
rstart 0 /start 0 , rend 0 /end 0 , rlinks 0 /links 0 , rset 0 /set 0 ]
PopReadyChain = b
Pop[rstart /start , rend/end, rlinks/links, rset /set ,
rstart 0 /start 0 , rend 0 /end 0 , rlinks 0 /links 0 , rset 0 /set 0 ]
DeleteReadyChain = b
Delete[rstart /start , rend/end, rlinks/links, rset /set ,
rstart 0 /start 0 , rend 0 /end 0 , rlinks 0 /links 0 , rset 0 /set 0 ]
PushBlockedChain = b
Push[bstart /start , bend/end, blinks/links, bset /set ,
bstart 0 /start 0 , bend 0 /end 0 , blinks 0 /links 0 , bset 0 /set 0 ]
DeleteBlockedChain = b
Delete[bstart /start , bend/end, blinks/links, bset /set ,
bstart 0 /start 0 , bend 0 /end 0 , blinks 0 /links 0 , bset 0 /set 0 ]
PushFreeChain = b
Push[fstart /start , fend/end, flinks/links, fset /set ,
fstart 0 /start 0 , fend 0 /end 0 , flinks 0 /links 0 , fset 0 /set 0 ]
PopFreeChain = b
Pop[fstart /start , fend/end, flinks/links, fset /set ,
fstart 0 /start 0 , fend 0 /end 0 , flinks 0 /links 0 , fset 0 /set 0 ]
21.4 / Design 339
The state of the concrete scheduler comprises the three chains, together
with an optional current process:
CScheduler
ReadyChain
BlockedChain
FreeChain
current : OptPId
chainstore : PId → OptPId
h{current } \ {nullPId}, rset , bset , fset i partition PId
rlinks = rset / chainstore . rset
blinks = bset / chainstore . bset
flinks = fset / chainstore . fset
current ≠ nullPId ⇒ chainstore current = nullPId
It is also useful to identify the working space used: the component chainstore
is the union of the three chain functions, plus an optional map to nullPId.
Initially, there is no current process, and all chains are empty:
CSchedulerInit
CScheduler 0
ReadyChainInit
BlockedChainInit
FreeChainFull
current 0 = nullPId
CDispatch
∆CScheduler
p! : PId
ΞBlockedChain
ΞFreeChain
current = nullPId
rset ≠ ∅
PopReadyChain
current 0 = p!
340 21 / An Operating System Scheduler
When the current process times out, it is pushed onto the ready chain. If there
is no such process, this is impossible.
CTimeOut
∆CScheduler
p! : PId
ΞBlockedChain
ΞFreeChain
current ≠ nullPId
PushReadyChain[p!/p?]
current 0 = nullPId
p! = current
When the current process is blocked, it is pushed onto the blocked chain. Again,
if there is no such process, this is impossible.
CBlock
∆CScheduler
p! : PId
ΞReadyChain
ΞFreeChain
current ≠ nullPId
PushBlockedChain[p!/p?]
current 0 = nullPId
p! = current
When a blocked process is woken up, it is pushed onto the ready chain:
CWakeUp
∆CScheduler
p? : PId
ΞFreeChain
p? ∈ bset
DeleteBlockedChain
PushReadyChain
current 0 = current
For this operation to be successful, it must be applied only when the process
identifier in question is present in the blocked chain.
21.4 / Design 341
When a process is created, an identifier is popped off the free chain and
pushed onto the ready chain.
CCreate
∆CScheduler
p! : PId
ΞBlockedChain
fset ≠ ∅
current 0 = current
PopFreeChain
PushReadyChain[p!/p?]
CDestroyCurrent
∆CScheduler
p? : PId
ΞReadyChain
ΞBlockedChain
p? = current
current 0 = nullPId
PushFreeChain
If a process is destroyed when ready, then the current process and the blocked
chain are unaffected. The appropriate identifier is deleted from the ready chain
and pushed onto the free chain:
CDestroyReady
∆CScheduler
p? : PId
ΞBlockedChain
p? ∈ rset
current 0 = current
DeleteReadyChain
PushFreeChain
342 21 / An Operating System Scheduler
CDestroyBlocked
∆CScheduler
p? : PId
ΞReadyChain
p? ∈ bset
current 0 = current
DeleteBlockedChain
PushFreeChain
To see how the abstract and concrete descriptions are related, consider the
following abstract state:
current = 3
ready = {2, 4, 6}
blocked = {5, 7}
free = {1, 8, 9, 10}
There are many concrete states that correspond to this; one possibility is
current = 3
chainstore = {1 , 8, 2 , 6, 3 , 0, 4 , 2, 5 , 0,
6 , 0, 7 , 5, 8 , 9, 9 , 10, 10 , 0}
rstart = 4
rend = 6
rlinks = {4 , 2, 2 , 6}
rset = {2, 4, 6}
bstart = 7
bend = 5
blinks = {7 , 5}
bset = {5, 7}
fstart = 1
fend = 10
flinks = {1 , 8, 8 , 9, 9 , 10}
fset = {1, 8, 9, 10}
21.5 / Correctness of the design step 343
current
8 6 0 2 0 0 5 9 10 0
1 4 7
RetrScheduler
AScheduler
CScheduler
ready = rset
blocked = bset
free = fset
and then check the simplified requirements for initialisation and correctness:
The correctness requirement should be checked for every pair of abstract and
concrete operations.
Chapter 22
22.1 Specification
A bounded buffer is a data store that may hold a finite number of values. It
behaves as a first-in first-out queue: values leave in the order in which they
arrive. We will develop a programming language implementation of a bounded
buffer with three operations:
• BufferInit, an initialisation
• BufferIn, providing input to the buffer
• BufferOut, accepting output from the buffer
The sequence buffer , and the state itself, will use a generic parameter X
to refer to the type of values to be stored:
Buffer [X ]
buffer : seq X
size : N
max size : N1
size = #buffer
size ≤ max size
The number of values present is equal to the length of the sequence, and must
never exceed the maximum buffer size.
At initialisation, the bounded buffer is empty: buffer is equal to the empty
sequence and size is zero:
BufferInit [X ]
Buffer 0 [X ]
buffer 0 = hi
The value of max size is left unconstrained: a suitable value should be chosen
when the buffer is instantiated.
The capacity of the buffer cannot be changed after instantiation; this fact
is recorded as an invariant in the following schema:
UpdateBuffer [X ]
Buffer [X ]
Buffer 0 [X ]
max size0 = max size
BufferIn0 [X ]
UpdateBuffer [X ]
x? : X
size < max size
buffer 0 = buffer _ hx?i
Extracting an item is possible only if the buffer is not empty; the value
obtained is the one at the head of buffer :
BufferOut0 [X ]
UpdateBuffer [X ]
x! : X
buffer ≠ hi
buffer 0 = tail buffer
x! = head buffer
These schemas represent partial operations: they describe the effect of a suc-
cessful insertion and a successful extraction, respectively.
To provide a more informative interface to our data type, we consider a
pair of error cases, each with its own report. We introduce a free type of reports
with three elements:
Success
report ! : Report
report ! = ok
while the other reports are associated with predicates upon the current state.
The error report full may be obtained only if size is equal to max size:
BufferInError [X ]
ΞBuffer [X ]
report ! : Report
size = max size
report ! = full
The inclusion of ΞBuffer [X ] indicates that the state of the buffer is unchanged
by this operation.
348 22 / A Bounded Buffer Module
Operation Precondition
BufferInit true
BufferIn0 size < max size
BufferInError size = max size
BufferIn true
BufferOut0 buffer ≠ hi
BufferOutError buffer = hi
BufferOut true
The report empty may be obtained only if the sequence buffer is equal to
the empty sequence:
BufferOutError [X ]
ΞBuffer [X ]
report ! : Report
buffer = hi
report ! = empty
BufferIn[X ] =
b
(BufferIn0 [X ] ∧ Success) ∨ BufferInError [X ]
BufferOut [X ] =
b
(BufferOut0 [X ] ∧ Success) ∨ BufferOutError [X ]
A simple precondition analysis will confirm that the input operation will be suc-
cessful unless the buffer is full, and that the output operation will be successful
unless the buffer is empty.
The preconditions associated with these operations are summarised in
Table 22.1. Notice that the input operation is total on valid states of the system,
as the remaining possibility—that the current buffer size is greater than the
capacity—is outlawed by the state invariant.
22.2 / Design 349
22.2 Design
Array[X ]
array : seq X
max size : N1
bot , top : N
size : N
bot ∈ 1 . . max size
top ∈ 1 . . max size
size ∈ 0 . . max size
#array = max size
size mod max size = (top − bot + 1) mod max size
When the buffer is full, we have size = max size; when the buffer is empty, we
have size = 0. In either of these two extremes, it is the case that
Initially,
InitArray[X ]
Array 0 [X ]
size0 = 0
350 22 / A Bounded Buffer Module
As in the specification, we will insist that the capacity of the buffer cannot be
changed after installation:
UpdateArray[X ]
Array[X ]
Array 0 [X ]
max size0 = max size
Since max size is included as part of the design state, the invariant property is
exactly the same.
The connection between concrete and abstract states is a simple one. If we cut
the circular buffer immediately below the bot mark, and then straighten it out,
we will find that the first size elements are the same as those in the abstract
buffer. Alternatively, if we shift the circular buffer so that bot occurs at position
1, then trim away the waste, then we have the abstract buffer.
To help us in writing the retrieve relation, we introduce a shift operator
on sequences: . We can shift an empty sequence indefinitely, but it will
still be empty.
n hi = hi
0s=s
If we shift a non-empty sequence by one place, the first becomes the last:
1 (hxi _ s) = s _ hxi
[X ]
: N × seq X → seq X
∀ n : N; x : X ; s : seq X •
n hi = hi ∧
0s=s ∧
(n + 1) (hxi _ s) = n (s _ hxi)
22.3 / A retrieve relation 351
8 1
7 2
6 3
top bot
5 4
(1 . . 4) / (2 h1, 2, 3, 4, 5, 6, 7, 8i)
= (1 . . 4) / h3, 4, 5, 6, 7, 8, 1, 2i
= h3, 4, 5, 6i
RetrieveBuffer [X ]
Buffer [X ]
Array[X ]
buffer = (1 . . size) / ((bot − 1) array)
Notice that the retrieve relation is a total, surjective function, so that we can
calculate the data refinement.
352 22 / A Bounded Buffer Module
ArrayIn0 [X ]
UpdateArray[X ]
x? : X
#((1 . . size) / ((bot − 1) array)) < max size
((1 . . size0 ) / ((bot 0 − 1) array 0 ))
= ((1 . . size) / ((bot − 1) array)) _ hx?i
We would like to simplify the predicate part of this schema, particularly the
parts involving the following expression:
We can prove that the value of the first of these parts is equal to the number
of elements in the buffer: that is, size.
To simplify the second predicate in the schema, we will make use of a simple
22.3 / A retrieve relation 353
(1 . . m / s) _ hxi
(1 . . m / s) _ hxi = (1 . . m + 1) / (s ⊕ {m + 1 , x})
The overriding operator ensures that the last element in the sequence is x.
We may now simplify the second predicate:
⇐ size0 = size + 1 ∧
bot 0 − 1 array 0 = (bot − 1 array) ⊕ {size + 1 , x?}
[property of domain restriction]
a size0 = size + 1 ∧
bot 0 − 1 array 0 =
bot − 1 (array
⊕
{(size + 1 + bot − 1 − 1 mod #array) + 1 , x?}
[property of shifting]
[arithmetic]
a size0 = size + 1 ∧
bot 0 = bot ∧
array 0 = array ⊕ {(top − bot + 1 + bot − 1 mod max size) + 1 , x?}
[arithmetic]
a size0 = size + 1 ∧
bot 0 = bot ∧
array 0 = array ⊕ {(top mod max size) + 1 , x?}
[arithmetic]
⇐ size0 = size + 1 ∧
bot 0 = bot ∧
top 0 = (top mod max size) + 1 ∧
array 0 = array ⊕ {top 0 , x?}
[property of equality]
The first step in the derivation is contingent upon size ≤ #array, which is a
consequence of the state invariant.
We have found a stronger condition that shows how we can update the
concrete state to achieve the abstract effect. The size of the buffer is increased
by 1; bot is unchanged; top is moved one place; and the new element is added
at the new top of the buffer.
The result of our calculation is a concrete operation schema that describes
the effect of a successful input:
22.3 / A retrieve relation 355
ArrayIn0 [X ]
UpdateArray[X ]
x? : X
size < max size
size0 = size + 1
bot 0 = bot
top 0 = (top mod max size) + 1
array 0 = array ⊕ {top 0 , x?}
The error case for the concrete operation is easily calculated. A simple
substitution into the abstract schema yields:
ArrayInError [X ]
ΞArray[X ]
report ! : Report
#((1 . . size) / ((bot − 1) array)) = max size
report ! = full
ArrayInError [X ]
ΞArray[X ]
report ! : Report
size = max size
report ! = full
ArrayIn[X ] =
b
(ArrayIn0 [X ]
∧
Success)
∨
ArrayInError [X ]
ArrayOut0 [X ]
UpdateArray[X ]
x! : X
1 . . size / (bot − 1 array) ≠ hi
(1 . . size0 / (bot 0 − 1 array 0 ) =
tail (1 . . size / (bot − 1 array))
x! = head (1 . . size / (bot − 1 array))
a size ≠ 0
[previous calculation]
[previous calculation]
Provided that the buffer is not empty, we should decrement the size; increment
bot ; leave top alone; and output the element that was indexed by bot .
Using these calculations to simplify the operation schema that describes
the concrete output operation, we obtain
ArrayOut0 [X ]
UpdateArray[X ]
x! : X
size ≠ 0
size0 = size − 1
bot 0 = (bot mod max size) + 1
array 0 = array
x! = array bot
ArrayOutError [X ]
ΞArray[X ]
report ! : Report
(1 . . size) / ((bot − 1) array) = hi
report ! = empty
ArrayOutError [X ]
ΞArray[X ]
report ! : Report
size = 0
report ! = empty
ArrayOut [X ] =
b ArrayOut0 [X ] ∧ Success
∨
ArrayOutError [X ]
22.4 Implementation
var
ibuffer : array [1 . . max size] of X ;
bot , top : 1 . . max size;
size : 0 . . max size ;
and
size mod max size = (top − bot + 1) mod max size
IBufferIn[X ] v
if size < max size →
Array 0 [X ] : [ size < max size, ArrayIn0 [X ] ∧ Success ] [/]
size = max size →
Array 0 [X ] : [ size = max size, ArrayInError [X ] ] [†]
fi
22.4 / Implementation 359
/
v (strengthen postcondition)
size0 = size + 1
bot 0 = bot
0 0
Array [X ] : top = (top mod max size) + 1
ibuffer 0 = ibuffer ⊕ {top 0 , x?}
size < max size , report ! = ok
v (assignment introduction)
size := size + 1 ;
top := (top mod max size) + 1 ;
ibuffer := ibuffer ⊕ {top , x} ;
report ! = ok
†
v (strengthen postcondition)
h i
Array 0 [X ] : size = max size , report ! = full
v (assignment introduction)
report ! := full
procedure BufferIn[X ]
(val x : X ; res report ! : Report ) ;
if size < max size →
size := size + 1 ;
top := (top mod max size) + 1 ;
ibuffer [top] := x ;
report ! = ok
size = max size →
report ! := full
fi
360 22 / A Bounded Buffer Module
IBufferOut [X ]
v (conditionalintroduction)
if size ≠ 0 →
Array 0 [X ] : [ size ≠ 0, ArrayOut0 [X ] ∧ Success ]
[/]
size = 0 →
Array 0 [X ] : [ size = 0, ArrayOutError [X ] ] [†]
fi
/
v (strengthen postcondition)
size0 = size − 1
v (assignment introduction)
size := size − 1; bot := (bot mod max size) + 1; report ! := ok
†
v (strengthen postcondition)
Array 0 [X ] : [ size = 0, report ! = empty ]
v (assignment introduction)
report ! := empty
22.5 / Executable code 361
We may collect the code and encapsulate the output operation as a paramet-
erised procedure:
procedure IBufferOut
(res report : ReportType) ;
if size ≠ 0 →
size := size − 1 ;
bot := (bot mod max size) + 1 ;
report ! := ok
size = 0 →
report ! := empty
fi
Similarly, we may derive a procedure that accepts a single value and resets the
buffer to hold just that value:
procedure ResetBuffer [X ]
(val x : X ) ;
bot , top, size, ibuffer [1] := 1, 1, 1, x
MODULE Buffer;
EXPORT
max_size, ReportType, ResetBuffer,
BufferIn, BufferOut;
CONST
max_size = N;
TYPE
ReportType = ( OK, Full, Empty );
362 22 / A Bounded Buffer Module
VAR
ibuffer: ARRAY [ 1 .. max_size ] OF X;
bot, top: 1 .. max_size;
size: 0 .. max_size;
PROCEDURE ResetBuffer ( x: X );
BEGIN
bot := 1;
top := 1;
size := 1;
ibuffer[1] := x
END;
BEGIN
bot := 1;
top := max_size;
size := 0
END Buffer;
Chapter 23
A Save Area
23.1 Specification
Our specification of the save area will leave abstract the details of the records
being manipulated. We introduce a basic type
[Record]
to represent the set of all records. Each operation will return a status report;
the free type of reports is defined by
SaveArea
save area : seq Record
InitSaveArea
SaveArea 0
save area 0 = hi
We will use the sequence as a stack, with the last element as the top of the
stack. When a record is stored, it is placed at the end:
Save0
∆SaveArea
record? : Record
status! : Status
save area 0 = save area _ hrecord?i
status! = ok
It is easy to see that Save0 is total, but the Save operation may fail: there may
not always be enough room to store the new record. We do not have enough
state information to predict when this may happen.
We could remedy this by adding a component to describes the amount
of store left; this value would be updated every time save area was modified.
However, the amount of free space left in the system is influenced by factors
other than the size of the records and the number stored. We would need to
model the rest of the system in some way.
Following this path leads us away from abstraction and modularity. It
is better to admit that we do not know the circumstances—at this level of
abstraction—that determine the amount of free space and hence the success or
failure of the Save operation. Thus the error case for Save is described by
SaveFullErr
ΞSaveArea
status! : Status
status! = full
23.1 / Specification 365
Save =
b Save0 ∨ SaveFullErr
n:N
n : 1 . . 10
Although we have been more precise about the value of n, this restriction is not
a refinement in the sense described above: it is merely a tighter version of the
same specification.
A loosely-specified constant is there to be instantiated at any stage of de-
velopment: we may make this choice during specification, or retain the constant
as a parameter of the design. A nondeterministic operation—such as Save—
involves an internal choice of behaviours: we must propose an implementation
that will behave accordingly.
The Restore operation is wholly deterministic. We can restore a record
whenever there is at least one record in save area:
Restore0
∆SaveArea
r ! : Record
status! : Status
save area ≠ hi
save area = save area 0 _ hr !i
status! = ok
The last record in the save area is removed from the stack and provided as
output; the success of the operation is reported.
366 23 / A Save Area
Operation Precondition
Save Save0 true
SaveFullErr true
Save true
Restore Restore0 save area ≠ hi
RestoreEmptyErr save area = hi
Restore true
RestoreEmptyErr
ΞSaveArea
status! : Status
save area = hi
status! = empty
Restore =
b Restore0 ∨ RestoreEmptyErr
The preconditions for the operations in this interface are collected in Table 23.1.
23.2 Design
Our first design decision involves the introduction of a two-level memory. Large
amounts of data will be saved, and this will quickly exhaust the main memory
available to our program. Accordingly, we will employ secondary memory; once
the main memory is exhausted, we will copy it here.
Let n be the number of records that we can save in main memory. We
insist that the value of n—a parameter to the specification—is at least 1:
n:N
n≥1
23.2 / Design 367
We define two sets of sequences: a bounded sequence is one whose length does
not exceed n; a full sequence is one whose length is exactly n.
[X ]
bseq : P(seq X )
fseq : P(seq X )
bseq = { s : seq X | #s ≤ n }
fseq = { s : seq X | #s = n }
Our concrete design employs main and secondary memory: main memory
is a bounded sequence of records; secondary memory is a list of full sequences:
CSaveArea
main : bseq[Record]
secondary : seq(fseq[Record])
Retrieve
SaveArea
CSaveArea
save area = (_/ secondary) _ main
For a given value of n, there is only one way in which the save area sequence
can be split into main and secondary. Similarly, there is only one way that main
and secondary can be combined to make save area, if order is to be maintained.
The retrieve relation described by this schema is a total bijection, and we
may use it to derive an initialisation:
CSaveArea 0
(_/ secondary 0 ) _ main 0 = hi
InitCSaveArea
CSaveArea 0
main 0 = hi
secondary 0 = hi
∆CSaveArea
record? : Record
status! : Status
(_/ secondary 0 ) _ main 0 = (_/ secondary) _ main _ hrecord?i
status! = ok
The first equation in the predicate part of this schema describes the concrete
state change associated with this operation. It has two solutions in terms of
main and secondary: either
or
∆CSaveArea
record? : Record
status! : Status
(main 0 = hrecord?i ∧ secondary 0 = secondary _ hmaini) ∨
(main 0 = main _ hrecord?i ∧ secondary 0 = secondary)
status! = ok
23.2 / Design 369
CSave0
∆CSaveArea
record? : Record
status! : Status
( #main = n ∧
main 0 = hrecord?i ∧
secondary 0 = secondary _ hmaini
∨
#main < n ∧
main 0 = main _ hrecord?i ∧
secondary 0 = secondary )
status! = ok
CSaveFullErr
ΞCSaveArea
status! : Status
status! = full
CSave =
b CSave0 ∨ CSaveFullErr
Our second design decision concerns the implementation of main memory stor-
age. A bounded sequence such as main can be implemented using a fixed-length
array; a suitable representation might be
[X ]
Array : P(N →
7 X)
Array = (1 . . n) → X
That is, a fixed-length array can be represented by a total function from the
indices to the target type.
23.3 / Further design 371
Our new design adds an array and a counter to the existing representation
of secondary memory:
CSaveArea1
array : Array[Record]
count : 0 . . n
secondary : seq(fseq[Record])
Retrieve1
CSaveArea
CSaveArea1
main = (1 . . count ) / array
∆CSaveArea
record? : Record
status! : Status
∆CSaveArea
record? : Record
status! : Status
( count = n ∧
(1 . . count 0 ) / array 0 = hrecord?i ∧
secondary 0 =
secondary _ h(1 . . count ) / arrayi)
∨
count < n ∧
(1 . . count 0 ) / array 0 =
((1 . . count ) / array) _ hrecord?i ∧
secondary 0 = secondary )
status! = ok
count 0 = 1
and array 0 may take any value as long as its first element is record?. secondary 0
must take the value
secondary _ harrayi
This tells us that these two sequences have the same length, and hence that
count 0 = count + 1.
23.3 / Further design 373
CCSave0
∆CSaveArea1
record? : Record
status! : Status
( count = n ∧
count 0 = 1 ∧
array 0 1 = record? ∧
secondary 0 = secondary _ harrayi
∨
count < n ∧
count 0 = count + 1 ∧
array 0 = array ⊕ {count + 1 , record?} ∧
secondary 0 = secondary )
status! = ok
374 23 / A Save Area
CCSaveFullErr
ΞCSaveArea1
status! : Status
status! = full
CCSave =
b CCSave0 ∨ CCSaveFullErr
Before we move into the refinement calculus, we break the CCSave0 operation
into its component disjuncts: the secondary memory update
CCUpdateSM
∆CSaveArea1
record? : Record
status! : Status
count = n
count 0 = 1
array 0 1 = record?
secondary 0 = secondary _ harrayi
status! = ok
CCUpdateMM
∆CSaveArea1
record? : Record
status! : Status
count < n
count 0 = count + 1
array 0 = array ⊕ {count + 1 , record?}
secondary 0 = secondary
status! = ok
23.4 / Refinement to code 375
then
CCUpdateMM
∨
save = CSaveArea1, status! : CCUpdateSM
∨
true , CCSaveFullErr
We may refine this specification statement using the refinement rule for condi-
tional introduction, to obtain
if count < n →
CSaveArea1, status! : [ count < n, CCUpdateMM ] [/]
count = n →
CCUpdateSM
CSaveArea1, status! :
∨
[†]
count = n , CCSaveFullEr
fi
The first alternative (/) can be rewritten by expanding the definition of the
CCUpdateMM operation:
count 0 =
count + 1
0
array =
count , array, status! :
array ⊕ {count + 1 , record?}
status! =
count < n , ok
and then refined by assignment introduction, leaving us with the following as-
signment statement:
count 0 = 1 ∧
array 0 1 = record? ∧
0
secondary =
_
secondary harrayi ∧
count , array, status! = ok
:
secondary, status!
∨
count 0 = count ∧
0
array = array ∧
0
secondary = secondary ∧
count = n , status! = full)
con X •
status! = ok ∧
secondary 0 =
status!,
_
secondary harray 0 i
: [‡]
secondary
∨
status! = full ∧
0
true , secondary = secondary
;
count 0 = 1 ∧
array 0 1 =
record? ∧
secondary 0 =
X _ harrayi ∧
count ,
array : status! = ok ∧ status! = ok [/]
secondary = ∨
X _ harrayi count 0 = count ∧
0
∨ array = array ∧
status! = full ∧
secondary 0 = X ∧
secondary = X , status! = full
23.4 / Refinement to code 377
if status! = ok →
count 0 = 1
array 0 = array
count , :
status! = full secondary 0 = X
array
secondary = X , status! = full
fi
while the second may be refined by skip introduction. We have now removed
all the specification statements except ‡, leaving the program
if count < n →
count , array, status! := count + 1, array ⊕ {count + 1 , record?}, ok
count = n →
status! = ok ∧
secondary 0 = secondary _ harray 0 i
status!, :
∨
secondary
true , status! = full ∧ secondary 0 = secondary
;
if status! = ok →
count , array := 1, array ⊕ {1 , record?}
status! = full →
skip
fi
fi
378 23 / A Save Area
Index
closure definition
reflexive, 94 abbreviation, 74
reflexive transitive, 97 axiomatic, 77
symmetric, 94 declaration, 73
transitive, 95 free type, 137
composition generic, 79
of relations, 91 recursive, 75, 122
of programs, 304 schema, 150
of schemas, 182 derivation, 11
comprehension, 61 derived rules, 12
concatenation, 115 difference, 69
concrete discharge rules, 13
state, 246 disjunction
values, 242 logical, 13
conditional, 307 rules, 13
conjunction schema, 174
logical, 11 distributed concatenation, 118
rules, 13 distributive, 124
schema, 166 domain
connectives, 10 definition, 104
consequent, 14 restriction, 86
constants subtraction, 86
global, 77
logical, 302 elimination rules, 11
constructed types, 69 empty sequence, 115
contingencies, 25 empty set
contradictions, 23 definition, 76
correctness criterion, 319 rules, 60
counting, 129 enumerated type, 135
equality, 45
daemon, 323 equational reasoning, 123
data refinement, 241 equivalence
de Morgan’s laws, 20, 25 logical, 17
declaration relation, 93
as generator, 62 rules, 18
in quantified expressions, 32 schema, 150
variable, 73 excluded middle, 22
decoration executable code, 295
of schemas, 168 existential
of variables, 301 quantifier, 28
definite description, 52 rules, 40, 41
Index 381
interface definition, 77
file system, 217 informal, 60
protocol, 323 negation
intersection, 68 logical, 20
introduction rules, 11 rules, 20
invariant schema, 177
of loop, 310 nondeterminism, 234
of program variables, 300 normalisation, 159
inverse, 88 number range, 112
items, 132
iteration occurrence
of programs, 309 binding, 33
of relations, 95 bound, 33
free, 33
Kenny, Tony, 3 one-point rule
Kinney, Andy, 27 existential, 49
operation
lambda notation, 102
internal, 323
languages, 295
mixed, 185
Leibniz, 46
partial, 175
length, 118
precondition, 203
lifting, 245
schema, 169
linked list, 334
ordered pair, 66
local block, 300
output, 170
London Underground, 5
overriding, 106
loop
introduction, 310
paradox, 70
program, 309
plagiarist, 48
loose
postcondition
definition, 77
predicate, 296
specification, 365
strengthening, 298
magic, 297 precedence, 10
maplet, 84 precondition
marginal markers, 305 calculation, 206
membership, 58 definition, 203
merge, 253 disjunction, 210
Mornington Crescent, 8 free promotion, 212
multiplicities, 128 investigation, 215
predicate, 296
natural deduction, 9 refinement, 258
natural numbers simplification, 208
Index 383
Notation
ran (range), 85
(range subtraction), 86
. (range restriction), 86
R (real numbers), 257
v (refined by), 298
∗
(reflexive transitive closure), 97
↔ (relations), 83
reverse (reverse a sequence), 123
=
b (schema naming), 150
seq (sequences), 119
∩ (set intersection), 68
\ (set difference), 69
∈ (set membership), 58
∪ (set union), 67
] (sharp), 129
# (size), 113
split (split a relation), 251
squash (squash a function), 121
v (sub-bag), 130
Resources
There is a world-wide web site associated with this book, containing exercises,
solutions, transparencies, and additional material. The address of the site is
http://www.softeng.ox.ac.uk/usingz/. If you are unable to gain access, then
write to the authors at Oxford University Computing Laboratory, Wolfson Build-
ing, Parks Road, Oxford OX1 3QD.
A variety of tools are available for use with the Z notation, but the most
useful of these is a type-checking program called fuzz. For details, e-mail
[email protected]. The authors teach a number of short courses based
upon the material in this book, as part of Oxford’s Software Engineering Pro-
gramme. For further information, see http://www.softeng.ox.ac.uk.
This Book
This book was written during the spring and summer of 1995, using an IBM
ThinkPad and a Toshiba T6600 portable. It was edited using Emacs under OS/2,
processed using Y&YTEX, and printed using a Monotype Prism PS Plus image-
setter. The London Underground map and diagram were supplied on film by
the London Transport Museum.
The Toshiba portable was stolen at the end of the summer, together with
the corrected drafts of several chapters. If you are offered this machine—the
serial number is 064 15 15 6E—then pay no more than £200, and send us the
contents of the hard disk.
Emacs was ported to OS/2 by Eberhard Mattes, and is available from any
good ftp archive. Y&YTEX is a commercial TEX system that supports outline
fonts. It is available from Y&Y at Tuttle’s Livery, 45 Walden Street, Concord,
MA 01742-2513, USA, or see http://www.yandy.com.
Thanks
For inspiration, friendship, practical assistance, and legal advice during the
preparation of this book:
Ali Abdallah, Mark Adams, Satoru Asakawa, John Axford, Liz Bailey,
Alexandra Barros, Jose Barros, Matthew Blakstad, Tommaso Bolognesi,
Christie Bolton, Stephen Brien, Sally Brown, Jeremy Bryans, Ana Cavalcanti,
Rance Cleaveland, Peter Coesmans, Katie Cooke, Denise Cresswell,
Charlie Crichton, Edward Crichton, Anna Curtis, Mats Daniels, Aled Davies,
Tim Denvir, Jeremy Dick, Frankie Elston, Susan Even, Mike Field,
Paul Gardiner, Dave Gavaghan, Michael Goldsmith, Sally Goodliffe,
Liz Goodman, Josh Guttman, Jackie Harbor, Malcolm Harper, Guy Hart-Davis,
Jon Hill, Jane Hillston, Robyn Hitchcock, Tony Hoare, Brendan Hodgson,
Andy Holyer, Fleur Howles, Jason Hulance, Dave Jackson, Alan Jeffrey,
Marina Jirotka, Liz Johns, Randolph Johnson, Rhona Johnston, Geraint Jones,
Mathai Joseph, Mark Josephs, Mike Kalougin, Steve King, Alice King-Farlow,
Chris Lansley, Stefan Leue, Lauren Lytle, Grainne Maher, Andrew Martin,
David Mayers, Steve McKeever, Quentin Miller, Andrew Miranker,
Swapan Mitra, Carroll Morgan, Dave Neilson, Andrew Newman, John Nicholls,
Nimal Nissanke, Colin O’Halloran, Duncan Oliver, Monica Payne,
David Penkower, Divya Prasad, Mike Reed, Phil Richards, Andi Roberts,
Bill Roscoe, Peter Ryan, Bahram Sadr-Salek, Bryan Scattergood,
Steve Schneider, Jenni Scott, Eleanor Sepanski, Carolyn Shafran, Juliet Short,
Alan Shouls, Andy Simpson, Jane Sinclair, David Skillicorn, Ib Sørensen,
Mike Spivey, Vicki Stavridou, Joe Stoy, Bernard Sufrin, Wilson Sutherland,
Muffy Thomas, Jacqui Thornton, Pete Verey, Matt Wallis, Elaine Welsh,
Shirley Williams, Ken Wood, Maureen York, and Ernst Zermelo.