To save content items to your account,
please confirm that you agree to abide by our usage policies.
If this is the first time you use this feature, you will be asked to authorise Cambridge Core to connect with your account.
Find out more about saving content to .
To save content items to your Kindle, first ensure no-reply@cambridge.org
is added to your Approved Personal Document E-mail List under your Personal Document Settings
on the Manage Your Content and Devices page of your Amazon account. Then enter the ‘name’ part
of your Kindle email address below.
Find out more about saving to your Kindle.
Note you can select to save to either the @free.kindle.com or @kindle.com variations.
‘@free.kindle.com’ emails are free but can only be saved to your device when it is connected to wi-fi.
‘@kindle.com’ emails can be delivered even when you are not connected to wi-fi, but note that service fees apply.
In this book, we are studying the correct development of distributed programs by means of various examples. So far we have done this in Chapter 4 (file transfer protocol) and in Chapter 6 (bounded retransmission protocol). In later chapters, we shall also study some distributed program developments: leader election on a ring-shaped network in Chapter 10, synchronizing processes on a tree in Chapter 11, routing algorithm in Chapter 12, leader election on a connected network in Chapter 13. We shall also study the correct development of sequential programs in Chapter 15. In this chapter, we shall study another kind of execution paradigm, namely that of concurrent programs.
Comparing distributed and concurrent programs
The distinction between sequential and distributed programs must be clear. But the one between distributed and concurrent ones might be less obvious. Here are the main differences which we consider between the two.
Distributed programs
In the case of distributed programs, the entire algorithm is performed by various agents executing some sequential programs (sometimes the same one) on different computers. But, at the same time, these agents are supposed to cooperate in order to achieve together a well-defined goal, which is the purpose of the algorithm.
This cooperation could be made easy by having a centralized agency, the role of which would be to schedule the various participating agents. But we suppose that such an agency does not exist.
In this chapter, we extend the file transfer protocol example of Chapter 4. The added constraint with regard to the previous simple example is that we suppose now that the data and acknowledgment channels situated between the two sites are unreliable. As a consequence, the effect of the execution of the bounded re-transmission protocol (for short BRP) is to only partially copy (but sometimes totally also) a sequential file from one site to another. The purpose of this example is precisely to study how we can cope with this kind of problem of dealing with fault tolerance and how we can formally reason about them. Notice that, in this chapter, we do not develop proofs as much as in the previous chapters; we only give some hints and let the reader develop the formal proof. This example has been studied in many papers among which is the one by J.F. Groote and J.C. Van de Pool [1].
Informal presentation of the bounded re-transmission protocol
Normal behavior
The sequential file to be transmitted is supposed to be transported piece by piece from one site, the sender site, to another one, the receiver site. For that purpose, the sender sends a certain data item on the so-called data channel connecting the sender to the receiver. As soon as the receiver receives this data item, it stores it in its own file and sends back an acknowledgment to the sender on the so-called acknowledgment channel connecting the receiver to the sender.
In this chapter, we shall see how to develop sequential programs. We present the approach we shall use, and then we propose a large number of examples.
Sequential programs (e.g. loops), when formally constructed, are usually developed gradually by means of a series of progressively more refined “sketches” starting with the formal specification and ending in the final program. Each such sketch is already (although often in a highly non-deterministic form) a monolithic description which resumes the final intended program in terms of a single formula. This is precisely that initial “formula”, that is gradually transformed into the final program.
We are not going to use this approach here. After all, in order to prove a large formula, a logician usually breaks it down into various pieces, on which he performs some simple manipulations before putting them together again in a final proof.
A systematic approach to sequential program development
Components of a sequential program
A sequential program is essentially made up of a number of individual assignments that are glued together by means of various constructs. Typical constructs are sequential composition (;), loop (while), and condition (if). Their role is to explicitly schedule these assignments in a proper order so that the execution of the program can achieve its intended goal.
This final chapter is entirely devoted to problems which you might try solving. They are all to be done with the Rodin Platform, which you can download from the web site “event-b.org”. We recommend beginners who are downloading the Rodin Platform for the first time to “perform” the tutorial which is included on this site before engaging in any of the problems presented in this chapter.
These problems are divided up into three categories called Exercises (Section 1), Projects (Section 2), and Mathematical developments (Section 3).
Exercises are small and easy developments, mainly corresponding to the construction of simple sequential programs. But we also find models of simple complete systems and even the development of a small electronic circuit. Exercises can be proposed in a beginner course.
Projects are more serious problems, which require more investment than exercises. They can be proposed in an advanced course.
Mathematical developments come from pure mathematics. They involve more complicated proofs than in the two previous cases; most of them are not performed automatically by the provers of the Rodin Platform. As such, they represent excellent exercises to improve our ability to perform interactive proofs.
For Exercises and Projects, we are required to write a requirements document as well as a refinement strategy before engaging in the formal development with the Rodin Platform. Most of the time, we will have to use some contexts to define the carrier sets and the constants of the problem at hand.
This title is certainly provocative. We all know that this claim corresponds to something that is impossible. No! We cannot construct faultless systems; just have a look around. If it were possible, it would have been already done a long time ago. And anyway, to begin with, what is a “fault”?
So, how can we imagine the contrary? We might think: yet another guru trying to sell us his latest universal panacea. Dear reader, be reassured, this Prologue does not contain any new bright solutions and, moreover, it is not technical; you'll have no complicated concepts to swallow. The intention is just to remind you of a few simple facts and ideas that you might use if you wish to do so.
The idea is to play the role of someone who is faced with a terrible situation (yes, the situation of computerized system development is not far from being terrible – as a measure, just consider the money thrown out of the window when systems fail). Faced with a terrible situation, we might decide to change things in a brutal way; it never works. Another approach is to gradually introduce some simple features that together will eventually result in a global improvement of the situation. The latter is the philosophy we will use here.
The intent of this chapter is to introduce a complete example of a small system development. During this development, we will become aware of the systematic approach we are using: it consists in developing a series of more and more accurate models of the system we want to construct. This technique is called refinement. The reason for building consecutive models is that a unique one would be far too complicated to reason about. Note that each model does not represent the programming of our system using a high-level programming language, it rather formalizes what an external observer of this system could perceive of it.
Each model will be analyzed and proved, thus enabling us to establish that it is correct relative to a number of criteria. As a result, when the last model is finished, we will be able to say that this model is correct by construction. Moreover, this model will be so close to a final implementation that it will be very easy to transform it into a genuine program.
The correctness criteria alluded to above will be made completely clear and systematic by giving a number of proof obligation rules which will be applied on our models. After applying such rules, we shall have to prove formally a number of statements. To this end, we shall also give a reminder of the classical rules of inference of the sequent calculus. Such rules concern propositional logic, equality, and basic arithmetic.
When this book was first published, recent aspects of human factors research had led to methodologies that integrated usability into the development of interactive systems. MUSE was one of the pioneering methods for Usability Engineering. It provides an environment in which human factors contributions can realise their full potential. MUSE supports active human factors involvement in both design specification and evaluation, and takes system development from user requirements to user interface design. Its methods describe how design errors can be avoided or rectified throughout system development, as well as showing how errors can be identified and providing support for inter-disciplinary design planning and coordination. It therefore ranks as one of the best developed and most completely structured human factors methods. This book reviews the motivation for developing MUSE, and provides readers with a manual for method application. It will be essential reading for all involved with systems development, whether from the HCI or software engineering communities, and can be used as well for course accompaniment.
This book describes the use of qualified types to provide a general framework for the combination of polymorphism and overloading. For example, qualified types can be viewed as a generalization of type classes in the functional language Haskell and the theorem prover Isabelle. These in turn are extensions of equality types in Standard ML. Other applications of qualified types include extensible records and subtyping. Using a general formulation of qualified types, the author extends the Damas/Milner type inference algorithm to support qualified types, which in turn specifies the set of all possible types for any term. In addition, he describes a new technique for establishing suitable coherence conditions that guarantee the same semantics for all possible translations of a given term. Practical issues that arise in concrete implementations are also discussed, concentrating in particular on the implementation of overloading in Haskell and Gofer, a small functional programming system developed by the author.
Modern computer networks now circle the world, but the transmission of information between them depends on the many different protocols that define the behaviour of the sender and receiver. It is clear therefore, that the accurate description of these protocols is important if harmonious communication is to be maintained. In this book the authors use the formal specification language PSF to provide an unambiguous description of several communication protocols of varying levels of complexity, ranging from the alternating bit protocol to the token ring protocol. Beginners, as well as professionals in the field of communication protocols, will benefit from both the methods of specification described and the protocols discussed in this book.
The goal of this book is to provide a comprehensive and systematic introduction to the important and highly applicable method of data refinement and the simulation methods used for proving its correctness. The authors concentrate in the first part on the general principles needed to prove data refinement correct. They begin with an explanation of the fundamental notions, showing that data refinement proofs reduce to proving simulation. The book's second part contains a detailed survey of important methods in this field, which are carefully analysed, and shown to be either incomplete, with counterexamples to their application, or to be always applicable whenever data refinement holds. This is shown by proving, for the first time, that all these methods can be described and analysed in terms of two simple notions: forward and backward simulation. The book is self-contained, going from advanced undergraduate level and taking the reader to the state of the art in methods for proving simulation.