ELSEVIER
Information
Processing
Letters 53 (1995)
Information Processing Letters
159-163
Constructing a program with exceptions K. Rustan M. Leino ’ Department of Computer Science, 256-80, California Institute of Technology, Pasadena, CA 91125, USA
Abstract An exception is a form of a structured jump. A program that uses exceptions can sometimes be written in a way that is simpler, easier to understand, and more efficient than a similar program written without exceptions. Moreover, program constructs in a language with exceptions are not appreciably more difficult to reason about than those in a language without exceptions. In fact, the weakest preconditions of these constructs make up a nice algebra over functions of two arguments. We prove some theorems in this algebra using the calculational method, and then show a novel derivation of a simple program. Keywords: Programming
calculi; Exceptions;
Program
derivation;
Formal semantics
1. The program constructs We consider program statements that have two possible ways of terminating, normally and exceptionally. Interested in their semantics, we give the weakest precondition [ 21 for each program construct (cf. [ 1,7,6] ) . Since a statement may have two outcomes, the weakest precondition of a statement maps a pair of predicates over the final state to a predicate on the initial state. More precisely, the predicate wp.S. (P, Q) is true of exactly those initial states from which execution of statement S is guaranteed to terminate, and either terminates exceptionally in a state satisfying P or normally in a state satisfying Q. So, for example, if
fies false. We begin by showing the weakest precondition of an assignment statement n := E, where, for simplicity, we take E to be a total expression. wp.(x
:= E).(p,Q)
= Q[x := E].
The right-hand side of this formula is the predicate Q with every free occurrence of x textually replaced by E. We calculate, wp. ( x := E) . (pulse, true) =
{:=} true[x := E]
=
{substitution} true.
wp.S. Cfalse, true)
= true
holds for some program S, then S always terminates normally, never exceptionally, because no state satis’ This paper was written while the author was supported as a summer research intern at the DEC System Research Center. Email:
[email protected]. 0020-0190/95/$09.50 @ 1995 Elsevier Science B.V. All rights reserved XYDI0020-0190(94)00197-9
This calculation lets us conclude that statement always terminates normally. We now define two statements that the program state, skip and raise. The terminates normally, the latter always wp.skip.(P,Q)
= Q
the assignment do not modify former always exceptionally. (1)
K.R.M. L.eino/Informtion
160
wp.ruise.(P,Q) = P
Processing Letters 53 (1995) 159-163
Sequential (normal) composition of two statements S and T, written S ; T, is defined as wp:(S;T).(P,Q)
= wp.S.(P,wp.T(P,Q)).
(3)
In words, wp. (S ; T) , (P, Q) is true of those initial states from which either S terminates exceptionally in P (then T is ignored), or S terminates normally in a state from which T either terminates exceptionally in P or normally in Q. We also define exceptional composition, also known as the exception handler. It is written S Q T (“S try T”). The idea is that T “handles” any exception raised by S. wp.(S
a T).(P,Q)
(f (0(g (0h)).b,q)
(2)
= wp.S.(wp.T.(P,Q),Q).
{O
=
f.((g (0h).(p,q),q)
{lo)
=
f.(g.(h.(p,q),q),q) =
((01 (f
=
WI ((f
of two arguments
=
((01 L.(f.(p,q),q)
=
IL)
.f.(pvq) =
{Ll f.(L.(p,q),q)
(f(os>.(p,q) = f.(g.(p,q),q) and (fo)g).(p,q) = f.(P9g.(P9q))* We develop some theorems regarding these functions and compositions. Starting with the associativity of (0, we calculate,
((01 (f
and their
If it were not clear from the above English descriptions of ; and a, it is certainly clear from formulas (3) and (4) that there is some duality between the two program compositions. Reviewing ( 1) and (2), we also detect a duality. Indeed, by identifying a program with its weakest precondition, we find that functions skip and raise project to one of the two components of a pair. We will write these functions as R and L, respectively. Furthermore, we can write ; as right composition and a as left composition, written 0) and (0, respectively, over functions of some type D x D + D. That is, for any domain D, functions f,g:D x D -+ D, and elements p. q E D, we have
(0 h).(p,q).
(L (0 f).(PY4)
=
2. Functions compositions
(0 d
Next, we show that L is the identity of (0.
(4)
Hence, wp. (S a T) .( P, Q) is true of those initial states from which either S terminates normally in Q (then the handler T is ignored), or S terminates exceptionally in a state from which the handler T terminates exceptionally in P or normally in Q.
(og>.(h.(p,q),q)
(oL).(Pvq).
Now for the relation between R and (0. (R (0 f).(p, =
s>
((0) R.(f.(~vq)vq)
=
-CR)
=‘{R} R. (P, s>. This shows that R is a left zero of (0. Similar calculations show that o), too, is associative, that R is the identity of o), and that L is a left zero of 0). For brevity, we omit mention of such dual properties in the sequel. We introduce a new operator, double composition, written (0). (f (4 g).(P*q)
=
f.(g.(Pvq),g.(P,q)).
There is a relation between single (left and right) compositions and double composition.
K.R.M. L.eino/lnformation Processing Letters 53 (1995) 159-163
f
(4g = (f (0R) 4 g.
We invite the reader to prove that L and R are left identities of (o). We show one more theorem.
We prove this relation. ((f =
(0 R) 4 g).(p,q) =
(4)
=
((01
w
We find this curious property worthy of a special notation, 1J,
f.(g.hA4),g.bq)) =
{ (4 ) (f
{R is identity of 0) } f (0 R
f. (R. (P9 g. (P9 4) >9g. (lx 4) ) =
f (4 R {double/ single trade} (f (0 R) 4 R
(f (0 R).(p,g.(p,q)) =
161
LfJ = f (4 R.
(4 d-@,q)
Note that we stated this property as an equality between functions; however, its proof applies those functions to an arbitrary pair (p, q) . We strive toward calculations at the level of functions, since they tend to be more concise and easier to read. As it turns out, having shown the above relation between single and double compositions, we are now able to carry out our calculations at the level of functions. Continuing our exploration, we calculate,
The investigation of the properties of operator 1J is left to the reader. Back in the realm of programs, IS] is the program S with an exception handler skip. That is, [SJ is S with every exceptional outcome turned normal. We have introduced a simple algebra over functions of two arguments and their compositions. Proving theorems in this algebra, we showed the calculational method in action. The application of the algebra to programming immediately shows us properties such as
(f (4 8) 4 h = {double/single
raise a S = S = S a raise trade, and associativity
of o) } and the more subtle
(f (0 R) 4 g 4 h =
{double/single
trade, and associativity
of 0) }
f (4 (g 4 h) This property reveals that (0) and o), in that order, associate. We are now equipped to prove (0) associative. f (4 (g (4 h) = {double/single
trade}
f (4 ((g (0 R) 4 h) =
{mutual associativity
of (0) 0) }
Sa(TaU)
= (SaT)aU
The former simply states that raise is the identity of a,and the latter, which first appears in [ 71, states that a is associative. The algebra reveals these properties in a way that is more accessible than studying the program constructs directly. Moreover, the calculus clarifies that any programming language whose exception behavior is described by (o and o) exhibits these properties. More details of the algebra are found in [ 61, and a generalization of these compositions is developed in [8].
(f (4 (g (0 JG > 4 h =
{mutual associativity ((f
=
(4 g) (0 R) 4 h {double/ single trade}
(f (4 g) (4 h
of (0) (0 )
3. A program derivation We now turn our attention to deriving a program in the presence of exceptions. Our task will be to design
K.R.M. L.eino/Information Processing Letters 53 (1995) 159-163
162
a program that, given value x and two-dimensional array a of size M x N, computes b, i,j to satisfy
for the outer and inner loops, respectively, late S as
Q:
i := 0; doi#M+ j := 0; doj#N+ “establish a[ i, j] # x”; j:=j+l
(b+QO)
A (++Ql),
QO:
O
A O
Ql:
(Vm,nlO
where A a[i,j]=x A O
D u[m,n]
(S, b := false)
The program segment “establish a[ i, j] # x” concerns us since we are not allowed to modify any of these variables at this stage. Were it not for the presence of exceptions, we would need a miracle at this time. But, since we do have exceptions at our disposal, we just need to verify that one can be raised if a[ i, j] # x does not hold. We observe that Pl A j # N A a[ i, j] = x implies QO. so we can use a raise here. Replacing “establish a[ i, j] # x” with if u[i, j] = x -+ raise 0 u[i, j] # x --+ skip fi, we are done, and write the entire program as ( i := 0; doi#M-+ j := 0; doj#N-+ if u[i, j] = n + raise 0 u[i, j] # x + skip fi; j:=j+l
A O
D aEmn1 PO A i#M
od; i:=i+l od; b := false > a b := true
4. Discussion
O< i< M A (m,riIO
Pl:
i:=i+l od
a b := true
for some S to be developed. Let us now develop program S, whose normal postcondition we want to be Ql. Moreover, the theorem from [6] tells us that any raise statement in S must have precondition QO. Twice using the well-known technique of replacing a constant by a variable (see e.g. [2], [4, Chapter 161, [3], or [ 10, Chapter 81), we find invariants PO:
Od;
# x).
(The notation (VW 1 R D P) is a universal quantification and can be read as “for all w such that R holds, P holds”.) As our guide, we will use the last theorem from [6], which states that if, in a proof, every free occurrence of raise in S has precondition R, then R can be used as the precondition for handler T in a proof of S a T. Our plan will be to make use of the fact that control sometimes flows through T and sometimes not. We will let these two cases correspond to the cases b and -b. But which goes with which? In order to conclude that x is not present in a, every element of a needs to be tested. This can be done using two nested loops. In order to set i and j to a coordinate of a whose value is X, the program first needs to find such a coordinate. This, too, can be done using two nested loops, but there is no reason to continue the search once an x has been encountered. Exceptions provide a means of breaking out of such loops prematurely. For this reason, we write the first approximation of our program as
and calcu-
# x)
A O
A (tlnIO
To prove the correctness of our program, we only needed to show that normal termination of the loops maintains the invariants. The rest follows from the invariants, the guards, and the theorem from [ 61. The proof of this program is thus simple.
163
K.R.M. L.eino/lnformation Processing Letters 53 (1995) 159-163
Looking back at the program through operational spectacles, we see the loops followed by b := false as trying to establish the absence of x in a. However, should an x be present, an exception is raised when an x is first encountered. The operation of this program is thus easy to understand. Finally, consider a similar program that, without exceptions, uses stronger guards to facilitate exiting the loops before i = M and j = N, respectively. For example, twice applying the Bounded Linear Search Theorem (cf. [ 2,3] ), we arrive at the program
counterpart without exceptions. In short, the study of semantics applied to program design swiftly led us to a correct, simple, and efficient program. We conclude with the remark that many language implementations impose performance penalties for using exceptions. In programs like the one shown, a compiler can detect the raise statement and handler statically. Thus, in these cases, the compiler can simply generate code for a jump. We would like to see that feature provided by more compilers in the future.
b,m :=false,O; doTb Am#M+ c, n := false, 0; do~c A n#N-+ c, i, j,n := (a[m,n]
Acknowledgements
= x),m, n,n + 1
od;
I would like to thank Rick Hehner, Peter Hofstee, Adam Rifkin, David Gries, Greg Nelson, Mani Chandy, Roland Backhouse, Beverly Sanders, and the referees for suggestions on the presentation.
b,m:=c,m+l od
References In addition to having more complicated invariants (because the invariants need to record the information to prove the postcondition for both conjuncts of Q),this program is arguably less efficient than the one that uses exceptions (because of the extra tests). Thus, we consider our program efficient. The point, however, is not to show that a structured jump can produce a more efficient program; the point is that we have an easy way of constructing such a program hand in hand with its proof. The heated forum discussion [9] discusses programs that attempt to solve a related programming problem. How exception handling can simplify the structure of certain programs is also discussed in, for example, [ 11. More recently, [ 51 shows exceptions in the construction of programs through refinements.
5. Conclusions We have seen that the weakest preconditions of the primitive program constructs of a language with exceptions make up a nice algebra. We also showed an example of a program derivation that resulted in the use of exceptions. The program was no more difficult to construct or prove than a similar program without exceptions. On the contrary, it appears easier to understand, and may be considered more efficient than a
[ll F. Cristian, Correct and robust programs, IEEE Trans. Sofhvare Engineering 10 (1984)
163-174.
[21 E.W. Dijkstra, A Discipline of Programming (Prentice-Hall, EnglewoodCliffs, NJ, 1976). t31 E.W. Dijkstra and W.H.J. Feijen, A Method of Programming
(Addison-Wesley, Reading, MA, 1988). t41 D. Gries, The Science of Programming (Springer, Berlin,
1981).
[51 S. King and C.C. Morgan, Exits in the refinement calculus, Tech. Rept. PRG-TR-6-93, Programming Research Group, Oxford, 1993. K.R.M. Leino and J.L.A. van de Snepscheut, Semantics of exceptions, in: E.-R. Olderog, ed., Proc. IFIP WG2.I/WG2.2/WG2.3 Working Con% on Programming Concepts, Methods, and Calculi, San Miniato, Italy, 6-10
June 1994 (Elsevier, Amsterdam, 1994) 447-466.
1 MS. Manasse and C.G. Nelson, Correct compilation of control structures, Tech. Rept., AT&T Bell Laboratories, September 1984. [81 R. Manoharand K.R.M. Leino, Theory and use of conditional composition, Tech. Rept. Caltech-CS-TR-94-12, California Institute of Technology, 1994. [91 E Rubin, “GOT0 considered harmful” considered harmful, Comm. ACM 30 (3) (1987) 195-196. (See also responses to this letter in the May through August, November, and December issues.) 1101 J.L.A. van de Snepscheut, What Computing Is All About (Springer, Berlin, 1993).