LNCS 3167, Pp
Total Page:16
File Type:pdf, Size:1020Kb
Refinement: An Overview Ana Cavalcanti1, Augusto Sampaio2, and Jim Woodcock1 1 Department of Computer Science University of York York, UK 2 Centro de Inform´atica Universitates Federal de Pernambuco Recife - PE, Brazil The purpose of this initial chapter is to introduce concepts and techniques assumed as general background in the remaining chapters of this book. The relevant notions are introduced using a simple and well-known programming notation: Dijkstra’s language of guarded commands [81], presented in Section 1. Three classical approaches to assigning semantic meaning to programs are then explored. In Section 2 we discuss the annotation of programs with assertions and the associated reasoning framework (Hoare Logic). Section 3 is dedicated to a calculational style where the behaviour of a program is defined in terms of a predicate transformer: its weakest precondition. Partial and total correctness of programs are contrasted in these two sections. The important notion of program refinement is introduced in Section 4. We start with some intuition and then we give a weakest precondition based definition, followed by an alternative (but equivalent) definition in terms of nondeterminism. In Section 5, we explore another approach to program semantics, known as refinement algebra, which is based on equations and inequations (laws) relating programming constructs; algebraic laws allow a term rewriting style of program transformation. We then show, in Section 6, how the programming constructs can be embedded into a more abstract space of specifications; we introduce Morgan’s specification statement and illustrate Morgan’s refinement calculus concerning both algorithmic and data refinement. In Section 7 we discuss how a programming (or specification) language with a refinement ordering can be regarded as a lattice. This allows using well-established results of lattice theory in programming methodologies. We conclude this chapter with a brief discussion of refinement in other programming paradigms and the importance of tools to support program refinement in practice. 1 A Simple Programming Notation The version of Dijkstra’s Guarded Command Language (GCL) adopted here is summarised below; c stands for a command, x for a list of variables, e for a list of expressions, and ψ for a predicate. A. Cavalcanti, A. Sampaio, and J. Woodcock (Eds.): PSSE 2004, LNCS 3167, pp. 1–17, 2006. c Springer-Verlag Berlin Heidelberg 2006 ! 2 A. Cavalcanti, A. Sampaio, and J. Woodcock c::=skip abort do nothing, abortion x :=| e c; c assignment, sequence | if []i |ψ c fi conditional | • i → i do []i ψi ci od iteration | c c • → nondeterminism | var" x : T c local variable block | • The primitive constructs are standard. The command skip has no effect and, when executed, terminates immediately. In contrast, the command abort has a completely arbitrary behaviour; rather than being deliberately written by a pro- grammer, it may arise as a result of some undesirable computation like division by zero, or an infinite loop without any externally visible effect. The remaining primitive command of GCL is assignment. The program x := e is a multiple (or simultaneous) assignment, where x is a list of distinct variables and e an equal-length list of expressions. The components of e are evaluated and assigned to the corresponding components of x in the same position. All the contributing chapters of this book assume that expressions have no side-effect. In this chapter we further assume that expressions are always well-defined (their evaluation is always successful). In Chapter 2, on refinement of object-oriented programs, we consider assignments whose expressions might fail when evaluated, and the impact of side effects in expressions is discussed. As a simple example of a multiple assignment, the command x, y := y, x swaps the values of x and y. In GCL, the body of the conditional is a guarded command set. A guarded command takes the form ψ c, where ψ, the guard, is a predicate. The choice of which command executes→ is between those whose guards evaluate to true. If more than one guard is satisfied, the choice is nondeterministic; if no guard evaluates to true, the conditional behaves like abort. As an example, we consider the following command that assigns to z the greatest value held by x or y. if (x y) z := y [] (y ≤ x) → z := x fi ≤ → When x = y, the choice of which assignment executes is nondeterministic. The body of an iteration (do []i ψi ci od) is also a guarded command set. Similarly to the conditional, the• choice→ of which guarded command executes depends on the evaluation of the guards. If more than one guard is satisfied, the choice is nondeterministic; if no guard evaluates to true, the iteration terminates successfully, behaving like skip. In the program fragment below, the final value of r is the factorial of the natural number assigned to n. r := 1; do (n > 1) r := r n; n := n 1 od → ∗ − Refinement: An Overview 3 The program c c denotes an arbitrary (also known as demonic) choice be- 1 " 2 tween the commands c1 and c2 , in the sense that either one can be selected for execution. For instance, consider the program fragment below. x := 1 x := 2; if (x = 1") skip [] (x = 2) → abort fi → In this context, either x := 1 or x := 2 will be selected, so that is possible that the execution of the conditional leads to abortion. It is important to ob- serve, however, that, according to equality and refinement notions that reflect total correctness, any program that might abort (like the one above) is actually identified with abort. Some specification languages (like the one introduced in the next chapter) include a complementary notion of nondeterminism known as angelic choice. In this case the most suitable command for a given context is selected for execution. If the choice in the above example were angelic, the assignment x := 1 would have been selected. Operationally, the view is that of backtracking in the search for the best possible execution of the program. For further considerations on demonic and angelic choices see, for instance, [17]. The program (var x : T c) declares the variable x of type T for use in the command c. Local blocks• of this form may appear anywhere a command is expected. The occurrence of a variable x in the scope of a local declaration is bound, and free otherwise. For example, y is bound in var y : T x := y , but free in x := y. The program fragment that computes the factorial of•n, previously presented, can be redesigned to leave n untouched, using a local variable. r := 1; var t : N t := n; do (n > 1) r := r t; t := t 1 od • → ∗ − In the following two sections we discuss two well-established approaches to define a formal semantics to programming languages; the guarded command language introduced in this section is used as illustration. 2 Assertions and Hoare Logic A classical approach to assigning formal meaning to (and reasoning about) pro- grams, well-known as Hoare logic, is presented in [114]. Like in other branches of mathematics, the basis of this approach is to define the behaviour of pro- gramming constructs in terms of axioms and inference rules. Axioms define the semantics of the primitive commands like skip, abort and assignment. Each ax- iom takes the form of a Hoare triple, P c Q, where c is a command and P and Q are logical assertions, playing the role{ o}f pre- and postcondition, respectively. A Hoare triple is interpreted as follows: if P is true and c terminates successfully, then Q must be established. The semantics of language operators like sequential composition and conditional is defined by inference rules which assume that a Hoare triples hold for the arguments. 4 A. Cavalcanti, A. Sampaio, and J. Woodcock The original formulation of Hoare logic is for partial correctness, which means that the axioms and inference rules assume successful termination of a program, as can be inferred from the above interpretation. Nevertheless, several subsequent formulations of Hoare logic as, for instance, [74, 106, 203], address total correct- ness. In this case, the interpretation of a triple P c Q is: if P is true, then c must terminate successfully and establish Q. While{ } the semantics described here regards partial correctness, the weakest precondition semantics defined in the next section embodies termination. The Hoare triples for the language presented in the previous section are sum- marised in Table 1. Table 1. Hoare triples for GCL P skip P { } true abort false { } P[e/x] x := e P { } If P c1 Q and Q c2 R then P c1 ; c2 R { } { } { } If, for all i, (ψi P) ci Q then P (if []i ψi ci fi) Q ∧ { } { • → } If, for all i, (ψi P) ci P then P do []i ψi ci od (P ( i ψi )) ∧ { } { • → } ∧ •¬ If P c1 Q and P c2 Q then P c1 c2 Q { } { } { # } If P c Q then ( x : T P) var x : T c ( x : T Q) ! { } ∀ • { • } ∃ • As skip has no effect, any logical assertion that is true before its execution, remains true after it terminates. Being totally unpredictable, nothing can be guaranteed concerning what abort could establish as postcondition, if it termi- nates. The axiom for assignment formalises that if P is to be established after the assignment of e to x, then the assertion obtained from P, by replacing all free occurrences of x with e, must be true before the assignment. The semantics of the remaining constructs is defined by inference rules. For c1 ; c2 , the precondition is that of c1 , and the postcondition is that established by c2 ; furthermore, the postcondition established by c1 coincides with the pre- condition of c2 .