Unformatted text preview: 6 Introduction
lies closer to the original vague intentions, not the code. So the rst step
should always be to think carefully about your intentions and try to re ne
them to a more precise speci cation.
After that, the next step is to convert globality (speci cation) into locality
(code), and this is much easier after the initial thought. In fact, there are
speci c mathematical techniques, which we shall discuss later, that make much
of this process automatic. At the same time, they tie the speci cation and
code carefully together so you know as part of the coding process that the
link between them is made. Figure 1.2 illustrates the progression from vague
intention to precise code via precise speci cation.
expressed in English
how the algorithm does it
expressed in a programming language
thinking typing precise requirements
what the algorithm does
comments or speci cation
expressed in logic Figure 1.2
This distinction that we have made between speci cation and code,
corresponding to users and computer, also makes sense inside a program. It
is common to nd that part of the program, with a well-de ned task, can
be made fairly self-contained and it is then called | in various contexts |
a subprogram, or subroutine, or procedure or function, or, for larger, more
structured pieces of program, a module. The idea is that the overall program
is a composite thing, made up using components: so it takes on the role of
A module can be speci ed, and this describes how its environment, the rest
of the program, can call on it and what that achieves. The speci cation Programming in the large 7 describes all that the rest of the program needs to know about the module.
The implementation of the module, the code that it contains, its inner
workings, is hidden and can be ignored by the rest of the program.
Modularization is crucial when you want to write a large program because
it divides the overall coding problem into independent subproblems. Once you
have speci ed a module, you can code up the inside while forgetting the
outside, and vice versa. The speci cations of the modules also act as bulkheads,
like the partitions in the hold of a ship that stop water from a hole spreading
everywhere and sinking the ship. The speci cations compartmentalize the
program so that if an error is discovered in one module you can easily check
whether or not correcting it has any consequences for the others. This helps
to avoid the `Hydra' problem, in which correcting one error introduces ten
new ones. 1.8 Programming in the large
This book makes a signi cant simplifying assumption, namely that speci cations
can be got right rst time. This is usually (though not always) realistic for
small programs, and so the techniques that we shall present are called those
of programming in the small. The underlying idea, of understanding the users'
point of view through a speci cation, is still important in large-scale programs,
but the techniques cannot be applied in such a pure form (specify rst, then
code). To understand why, you must understand what could possibly be
wrong with a speci cation.
The ultimate test | in fact the de nition | of quality of software is that
it is t for its purpose. To be sure, the speci cation is supposed to capture
formally this idea of tness, and if that has been done well then a correct
program, one for which the code satis es the speci cation, will indeed be a
quality one. But, conversely, speci cations can have mistakes in them, and
this will manifest itself in unexpected and unwanted features in a formally
correct program. Hence correctness is only an approximation to quality.
Now there are many advantages to forgetting quality and working for
correctness. For instance, we have precise objectives (write the code to
satisfy the speci cation) that are susceptible to mathematical analysis, and
we can modularize the program and work for correctness of small, easy parts,
forgetting the wider issues. The widget manufacturer who takes an order for
2000 blue, size 15 widgets will nd life easier if he does not ask himself
whether they are really the right colour, let alone whether or not their end
use is to help train dolphins to run suicide missions smuggling cocaine.
However, the true proof of the program is, despite all we have said, its
behaviour in real life, and ultimately no programmer should forget that. The
speci cation and reasoning are merely a means to an end. Never forget the 8 Introduction
possibility that the speci cation is faulty. This will be obvious if correct code
plainly gives undesirable behaviour, but earlier warning signs are when the
coding is unexpectedly complicated or perhaps even impossible.
If the speci cation is faulty, then it can be revised, which will involve
checking existing code against the revised speci cations. Alternatively, the
speci cation can be left as it is for the time being, with the intention of
revising it for future versions or in the light of future experience. This is often
quite reasonable, and provides some stability to the project, but it should be
chosen after consideration and not out of inertia. The universal experience is
that the later corrections are left, the more expensive it is to make them (A
Stitch in Time Saves Nine), and large software projects have been destroyed
by the accumulation of uncorrected errors.
For programming in the large, many of the practical techniques that people
use can be seen as being there to help to correct speci cational faults as
early as possible, while they are still cheap to x. For instance, requirements
elicitation is about how to communicate as e ectively as possible with the
users, to nd out what they really do need and want then a number of
design methodologies help to obtain a good speci cation before coding starts
and prototyping produces some quick, cheap code in order to nd those faults
(such as di culty of use in practice) that are best exposed by a working
version. All of these are important issues but they are ignored in the rest of
this book. 1.9 Logical notation
English is not always precise and unambiguous | that is why computer
programming languages were invented. In general, the fewer things that a
language needs to talk about, the more precise it can be.
In our speci cations, we are going to make use of logic to make precise one
particular aspect of what we want to say, namely how di erent properties
connect together. In English there are connecting words such as `and', `or',
`but', `not', `all', `some', and so on, and in logic these are systematized and
given individual symbols. The reason for the importance of these connectives
is that it is the logical connections between the given properties that allow us
to deduce new ones.
For instance, suppose an instruction manual tells you:
`If anyone envelops the distal pinch-screw parascopically, then the
pangolin will unbundle.'
Suppose you also know that the anterior proctor has just enveloped the distal
pinch-screw parascopically. You do not need to be an expert on pangolins to
realize that it is likely to unbundle. The reason is that you have spotted Logical notation 9 the underlying logical structure of these facts, and it does not depend on the
nature of pinch-screws, pangolins or proctors.
This logical structure shows up best if we introduce some abbreviations: E (x) (where x stands for any person or thing) stands for `x
envelops the distal pinch-screw parascopically'.
A stands for `the anterior proctor'.
P stands for `the pangolin unbundles'.
As a special case of this notation, if we substitute A for x then: E (A) stands for `the anterior proctor envelops the distal pinch-screw
These abbreviations are not in themselves logical notation that comes in
when we connect these statements together. Logic writes |
^ for `and'
8 for `for all' (re ecting `anyone')
! for `implies' (re ecting `if : : : then : : : ') Now our known facts appear as
8x: E (x) ! P ] ^ E (A)
and just from this logical structure we can deduce P .
Much of logic is about making such deductions based on the logical structure
of statements. The general pattern is that we start from some statements A
called the premisses, and then deduce a conclusion B . The argument from A
to B is valid if in any situation where A is true, it follows inevitably that B
is true, too. Logic gives formal rules | that is to say, rules that depend just
on the form of the statements and not on their content or meaning | for
making valid deductions, and if these rules give us an argument from A to B
then we write A ` B (A entails B ).
If I have loads of money, then I buy lots of goods.
I have loads of money.
` I buy lots of goods.
is a valid argument There are situations where the premisses are false (for
instance, if, as it happens, I am a miser then the rst premiss is false), but
that does not a ect the validity of the argument. So long as the premisses
are true, the conclusion (`I buy lots of goods') will be, too. However,
If I have loads of money, then I buy lots of goods.
I go on a spending spree. 10 Introduction
` I have loads of money. is not a valid argument. Even if the premisses are true, the conclusion need
not be since I might be making imprudent use of my credit card.
In this book we shall use logic to help us with, broadly speaking, two kinds
of deduction related to a given speci cation of a program: rst, deducing
new facts about how the program will behave when we come to use it and,
second, deducing that the program code, or implementation, really does meet
the speci cation. Part II of this book is entirely devoted to logic itself. 1.10 The need for formality
English, and natural language in general, is tremendously rich and can express
not only straightforward assertions and commands but also aspects of emotion,
time, possibility and probability, meaning of life, and so on. But there is a
cost. Much of it relies on common understanding and experience, and on
the context. Look at the following three examples, and see how they contain
progressively more that is unspoken:
1. `She sang like her sister.'
2. `She sang like a nightingale.'
3. `He sang like a canary.'
(1) is fairly literal, but (2) is not | the comparison is not of the songs
themselves but of their beauty, and the compliment works only because
everyone knows (even if only by repute) that nightingales sing beautifully. As
for (3), in a gangster lm \He" might well be a criminal who, on arrest, told
the police all about his accomplices. But it is extremely inexplicit, and would
be hard to understand out of context.
Di erent people lead di erent lives, so these unspoken background
assumptions of experience and understanding are imprecise, and this leads to
an imprecision in English. To say anything precisely and unambiguously you
must drastically restrict the range of what can be said, to the point where
any background assumptions can also be made explicit. Then there is a direct
correspondence between the language and its meaning, and you can treat the
language `formally', that is, as symbols to be manipulated (which, after all, is
what a computer has to do), and be con dent that such manipulations are
re ected validly in the meaning.
An important example of a formal language that you must already know
is algebra, the formal language of numbers. Problems can often be solved
symbolically by algebraic manipulations without thinking about the numbers
behind the symbols, and you still obtain correct answers. An extension of
this is calculus. Again the symbolic manipulations | the various rules for Can programs be proved correct? 11 di erentiating and integrating | can be carried through without you having
to remember what the derivatives and integrals really mean.
In fact, this is only a particular application of the word `calculus', which is
Latin for `little stone'. In ancient times, one method of calculating was by
using little stones roughly like an abacus, and the idea is that you can obtain
correct answers about unmanipulable things through surrogate manipulations
of the little stones. We now use formal symbols instead of little stones,
but the word `calculus' is still often used for such a formal language | for
instance, one part of logic is often called the `predicate calculus'.
The other formal languages that you will see in this book are as follows:
logic This is the language of logical connections between statements. This is
a very narrow aspect of the statements and so the logical notation will
usually need to be combined with other notations, but once we have the
logical symbols expressing the logical structure, we can describe what are
logically correct arguments. Another point is that the logical symbols
are more precisely de ned than English words. For instance, there is
a logical connective `_' that, by and large, means `or': `A or B ' has
the logical structure `A _ B '. But sometimes the English `or' carries an
implicit restriction `but not both' (the so-called exclusive or), and then
the logic must take care to express this, as (A _ B ) ^ :(A ^ B ).
programming languages These are the languages of computer actions
(roughly | this is more true for the imperative language Modula-2 than
for the functional language Miranda). Once they are made formal then
one can work with them by symbolic manipulation and this is exactly
what computers do when they compile and interpret programs. 1.11 Can programs be proved correct?
We have already distinguished between quality and correctness, and explained
how `correctness', conformance to the speci cation, is only relative: if the
speci cation is wrong (that is, not what the user wanted) then so, too, will be
the code, however `correct' it is. But at least the speci cation and code are
both formal, so there is the possibility of giving formal proofs of this relative
correctness | one might say that this is the objective of formal methods in
It is worth pointing out that what you will see in this book are really only
`informal formal methods'. There are two main reasons for this.
The rst is that to give a formal correctness proof you need a formal
semantics of your programming language, a mathematical account of what the
programs actually mean in relation to the speci cations. We shall not attempt
to do this at all, but instead will rely on your informal understanding of
what the programming constructs mean. 12 Introduction
The second is that true formal reasoning has to include every last detail.
This might be ne if it is a computer (via a software tool) that is checking
the reasoning, but for humans such reasoning is tedious to the point of
impracticability, and hides the overall shape of the argument | you cannot
see the wood for the trees. Even in pure mathematics, proofs are `rigorous'
| to a high standard that resolves doubts | but not formal. Our aim is to
introduce you to rigorous reasoning.
Now even rigorous reasoning runs the risk of containing errors, so if in
this book we cannot claim unshakable mathematical correctness you might
wonder what the point is. We do not seem to be working to a Reasoned
Program as an error-free structure. Nevertheless, the structure of the Reasoned
Program, with its speci cation and reasoning included, is much more stable
than an Unreasoned Program, that is, code on its own. We have a clearer
understanding of its working, and this helps us both to avoid errors in the
rst place and, when errors do slip through, to understand why we made
them and how to correct them. 1.12 Summary
The code is directed towards the computer, giving it its instructions.
The speci cation is directed towards the users, describing what they will
get out of the program. It is concerned with quality ( tness for purpose).
By reasoning that the code satis es the speci cation, you link them
together into a reasoned program.
By putting the speci cation rst, as objectives to be achieved by the
code, you engage in reasoned programming. Coding is then concerned
with correctness (conformance with speci cation).
This separation also underlies modularization. The speci cation of a
module or subroutine is its interface with the rest of the program, the
coding is its (hidden) internal workings.
This book is about programming in the small. It makes the simplifying
assumption that speci cations can be got right rst time.
In practice, speci cations can be faulty | so that correctness does not
necessarily produce quality. Be on your guard against this.
The earlier faults are corrected, the better and cheaper.
There are numerous practices aimed at obtaining good speci cations
early rather than late, for instance talking to the customer, thinking
hard about the design and prototyping, but this book is not concerned
To match the formality of the programming language, we use formal
logical notation for speci cations. It is also possible to use formal
semantics to link the two, but we will not do this here. Part I
Programming Chapter 2 Functions and expressions 2.1 Functions
From the speci cation point of view a function is a black box which converts
input to output. `Black box' means you cannot see | or are not interested in
| its internal workings, the implementing code. Mathematically speaking, the
input and output represent the argument to the function and the computed
result (Figure 2.1). `Give me input'
`I'll give you output' Figure 2.1
In Figure 2.2 the function add1 simply produces a result which is one
more than its given argument. The number 16 is called an argument or an
actual parameter and the process of supplying a function with a parameter
is called function application. We say that the function add1 is applied to
16. Similarly, the function capital takes arguments which are countries and
returns the capital city corresponding to the given country.
15 16 Functions and expressions
16 Denmark argument type:Countries argument type:number capital add1 Copenhagen 17 result type:Cities result type:number Figure 2.2
From mathematics we are all familiar with functions which take more than
one argument. For example, functions + and * require two numbers as
arguments. Figure 2.3 gives some examples of applications of multi-argument
3 8 3 4 4 3 3 6 8 smaller power power smallest 3 81 64 3 Figure 2.3
When we rst de ne a function we need to pay attention both to the
way it works as a rule for calculation (the code) and also to its overall
global, external behaviour (the speci cation). But, when a function comes to
be used, only its external behaviour is signi cant and the local rule used in
calculations and evaluations becomes invisible (a black box). For example,
whenever double is used the same external behaviour will result whether
double n is de ned as 2*n or as n+n. 2.2 Describing functions
We can describe functions in a number of ways. We can specify the function
value explicitly by giving one equation for each individual input element or Describing functions 17
8 double 16 Figure 2.4
we can draw a diagram | a mapping diagram showing for each input element
its corresponding result (Figure 2.5). However, often there will be many, even
in nitely many, individual elements to consider and such methods will clearly
add1 x = x+1
add1 0 = 1
add1 1 = 2
a few equations
1 2 2 .
. 3 natural
numbers . an equation
for each possible
numbers showing individual mappings Figure 2.5
An alternative method is to describe the function using a few general
equations (Figure 2.5). Here we can make use of formal parameters, which are
names that we give to represent any argument to which the function will be
applied. For example, the formal parameter x in the de nition of add1 stands
for any number. The right hand side of the rule (or equation) describes
View Full Document
- Spring '11
- Logic, speci cation