* Pre-condition vs. Post-condition @ 1991-03-15 3:57 Chris M. Little 1991-03-15 19:07 ` Michael Feldman 0 siblings, 1 reply; 18+ messages in thread From: Chris M. Little @ 1991-03-15 3:57 UTC (permalink / raw) Say we have a function CAPITAL which, given a country's name, returns its capital city. If the given country does not exist, an exception COUNTRY_ERROR is raised. Should the given country's presence be listed as a pre-condition for this function, or should its absense (it doesn't exist) and the raising of COUNTRY_ERROR be listed as a post-condition? I brought this question up in class today and the outcome was a split decision. I think exception raising and/or handling is as valid an outcome of a function or procedure as any other outcome, so I'm tempted to cover the issue in the post-condition comment. My opponents believe that a function's pre-conditions should be the conditions under which it would complete "normally", that is, without any exceptions being raised. I'd appreciate any insight on the issue. E-mail please. Thanks. -- Chris Little, Graduate Asstistant - CML8@JAGUAR.UOFS.EDU (VMS) Department of Computing Sciences - CML8@SCRANTON.BITNET (VMS) University of Scranton, Pennsylvania. - CML8@ROBIN.CS.UOFS.EDU (UNIX) ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-15 3:57 Pre-condition vs. Post-condition Chris M. Little @ 1991-03-15 19:07 ` Michael Feldman 1991-03-17 12:26 ` George C. Harrison, Norfolk State University ` (2 more replies) 0 siblings, 3 replies; 18+ messages in thread From: Michael Feldman @ 1991-03-15 19:07 UTC (permalink / raw) >Say we have a function CAPITAL which, given a country's name, returns its >capital city. If the given country does not exist, an exception COUNTRY_ERROR >is raised. Should the given country's presence be listed as a pre-condition >for this function, or should its absense (it doesn't exist) and the raising >of COUNTRY_ERROR be listed as a post-condition? > >I brought this question up in class today and the outcome was a split decision. >I think exception raising and/or handling is as valid an outcome of a function >or procedure as any other outcome, so I'm tempted to cover the issue in the >post-condition comment. My opponents believe that a function's pre-conditions >should be the conditions under which it would complete "normally", that is, >without any exceptions being raised. Hmmm. Interesting question. I have always taught - and thought of - pre- conditions as a set of "contract terms" which, if they are met, would obligate the function writer to write code that delivers the right results. From a verification point of view, I think you are correct that raising an exception is a _valid_ outcome of the function, and so the function has to be tested with cases of "bad" input to check that the exception indeed is raised under those conditions. If the pre- and post-conditions are used to drive tests (or formal verification), I agree that _explicit_ exception- raising by the function is a post-condition matter: it needs to be tested. Failure of a caller to meet a pre-condition violates the contract between function writer and function user, in a way that the behavior of the function is _unpredictable_, for example the actual parameter is unitialized. If the "garbage" in the parameter _happens_ to constitute an in-range value, the function delivers a "correct" answer, coincidentally. Otherwise, an exception may be _unexpectedly_ raised (constraint_error, say). Since the caller has violated the contract, he gets what he deserves (an unexpected propagation of an exception). So the pre-conditions are really saying "if you violate these, all bets are off on what this function does." This argument makes sense to me from a theoretical standpoint. From a practical standpoint, in describing the interface to a function, how does one distinguish between violations that result in a _predictable_ behavior and those that do not? I can see why your students may have disagreed. It's a confusing matter. I'm posting this to the net to provoke other readers to join this thread if they are interested. Mike Feldman ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-15 19:07 ` Michael Feldman @ 1991-03-17 12:26 ` George C. Harrison, Norfolk State University 1991-03-18 15:04 ` Joe Hollingsworth 1991-03-19 18:17 ` Mike Gilbert 2 siblings, 0 replies; 18+ messages in thread From: George C. Harrison, Norfolk State University @ 1991-03-17 12:26 UTC (permalink / raw) In article <2865@sparko.gwu.edu>, mfeldman@seas.gwu.edu (Michael Feldman) writes: >>Say we have a function CAPITAL which, given a country's name, returns its >>capital city. If the given country does not exist, an exception COUNTRY_ERROR >>is raised. Should the given country's presence be listed as a pre-condition >>for this function, or should its absense (it doesn't exist) and the raising >>of COUNTRY_ERROR be listed as a post-condition? >> >>I brought this question up in class today and the outcome was a split decision. >>I think exception raising and/or handling is as valid an outcome of a function >>or procedure as any other outcome, so I'm tempted to cover the issue in the >>post-condition comment. My opponents believe that a function's pre-conditions >>should be the conditions under which it would complete "normally", that is, >>without any exceptions being raised. > > Hmmm. Interesting question. I have always taught - and thought of - pre- > conditions as a set of "contract terms" which, if they are met, would > obligate the function writer to write code that delivers the right results. > From a verification point of view, I think you are correct that raising > an exception is a _valid_ outcome of the function, and so the function has > to be tested with cases of "bad" input to check that the exception indeed > is raised under those conditions. If the pre- and post-conditions are used > to drive tests (or formal verification), I agree that _explicit_ exception- > raising by the function is a post-condition matter: it needs to be tested. > Lots of stuff deleted. This problem raises some interesting questions: Should pre and post conditions define the complete functionality of a subroutine? Should a function which has only one returned value (in Ada) be allowed to have a compound post condition? (old question) How exceptional should exceptions be used? (or something like that.) > This argument makes sense to me from a theoretical standpoint. From a > practical standpoint, in describing the interface to a function, how does > one distinguish between violations that result in a _predictable_ behavior > and those that do not? I can see why your students may have disagreed. > It's a confusing matter. I'm posting this to the net to provoke other > readers to join this thread if they are interested. > > Mike Feldman On a practical (the theoretical) view the user probably should redo his function as a procedure returning TWO values (the captial and a boolean object SUCCESSFUL); write the usualy pre and post conditions for that procedure; then make a functional isomorphism back to the original function. Actually, IMHO, if a practical intent of the function IS to guard against wrong countries, then a procedure might be better anyway. -- George C. Harrison ----------------------- ----- Professor of Computer Science ----------------------- ----- Norfolk State University ----------------------- ----- 2401 Corprew Avenue, Norfolk, Virginia 23504 ----------------------- ----- INTERNET: g_harrison@vger.nsu.edu --------------------------------- ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-15 19:07 ` Michael Feldman 1991-03-17 12:26 ` George C. Harrison, Norfolk State University @ 1991-03-18 15:04 ` Joe Hollingsworth 1991-03-18 19:51 ` Marlene M. Eckert 1991-03-19 7:38 ` Jim Showalter 1991-03-19 18:17 ` Mike Gilbert 2 siblings, 2 replies; 18+ messages in thread From: Joe Hollingsworth @ 1991-03-18 15:04 UTC (permalink / raw) In article <2865@sparko.gwu.edu> mfeldman@seas.gwu.edu () writes: >>Say we have a function CAPITAL which, given a country's name, returns its >>capital city. If the given country does not exist, an exception COUNTRY_ERROR >>is raised. Should the given country's presence be listed as a pre-condition >>for this function, or should its absense (it doesn't exist) and the raising >>of COUNTRY_ERROR be listed as a post-condition? >> >>I brought this question up in class today and the outcome was a split decision. >>I think exception raising and/or handling is as valid an outcome of a function >>or procedure as any other outcome, so I'm tempted to cover the issue in the >>post-condition comment. My opponents believe that a function's pre-conditions >>should be the conditions under which it would complete "normally", that is, >>without any exceptions being raised. > >Hmmm. Interesting question. I have always taught - and thought of - pre- >conditions as a set of "contract terms" which, if they are met, would >obligate the function writer to write code that delivers the right results. >From a verification point of view, I think you are correct that raising >an exception is a _valid_ outcome of the function, and so the function has >to be tested with cases of "bad" input to check that the exception indeed >is raised under those conditions. If the pre- and post-conditions are used >to drive tests (or formal verification), I agree that _explicit_ exception- >raising by the function is a post-condition matter: it needs to be tested. ....stuff deleted..... >Mike Feldman I'm glad you raised the point of formal verification. I'd like to point out how the use of exception handling complicates verification (be it formal or informal). Examine the following possible implementation for Pop (popping a stack). procedure pop(s: stack) -- this pop doesn't return the top of the stack, just discards it. begin if(not empty(s)) then -- pop the stack else raise underflow end pop; The point here is that there are 2 distinct paths through the procedure which will have to be verified with respect to the pre and post conditions (pre and post conditions not given). The two paths are easy to spot. Now look at another implementation of Pop based on Booch's stack package: procedure pop(s: stack) -- this pop doesn't return the top of the stack, just discards it. begin stack.top := stack.top - 1; exception when Constraint_Error => raise underflow end pop; Here there are also two paths through the procedure, but it's not so obvious to the reader (verifier) how the path through the exception handler gets taken. The verifier has to realize that the variable stack.top has been defined as Natural and that a Constraint_Error will be raised by the run time system when trying to set it to -1. Since the control flow in this example is not explicit, let's call it implicit. I believe that verifying (formally or informally) code written using implicit control flow is harder than code using explicit control flow. If this point is valid, one might then believe that code written using implicit control flow stands a better chance of NOT being verified correctly, especially if it is informally verified. What's more I also believe that using exceptions leads one toward this kind of programming (i.e. depending on implicit control flow). I'm a follower of many of the rules found in Kernighan & Plauger's "The Elements of Programming Style", two of those rules reads as follows: "Write clearly - don't be too clever." "Say what you mean, simply and directly." I believe that exception handling leads one to writing programs that are not as clear as they could be (see the second example above which doesn't say what it means simply and directly). With respect to verification, if the verifier is mechanical, then it probably doesn't matter if the code uses implicit or explicit control, the verifier will be able to handle it. But, when was the last time any of us used a mechanical verifier on our Ada programs? Chances are the verifier is the person writing the code, and that person is doing it informally. All the more reason to "Write clearly..." and "Say what you mean, simply and directly." The above arguments lead me to believe that exception handling should be used only sparingly (if at all)! (Mike, you wanted to get a debate going on this? The above opinions should help!) Joe Joe Hollingsworth Computer and Information Science @ OSU holly@cis.ohio-state.edu ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-18 15:04 ` Joe Hollingsworth @ 1991-03-18 19:51 ` Marlene M. Eckert 1991-03-19 19:07 ` Michael Feldman ` (2 more replies) 1991-03-19 7:38 ` Jim Showalter 1 sibling, 3 replies; 18+ messages in thread From: Marlene M. Eckert @ 1991-03-18 19:51 UTC (permalink / raw) How about exceptions should be raised only in _EXCEPTIONAL_ situations? Reaching the end-of-file or trying to POP off an empty stack are NOT exceptional conditions. I would go so far as to say an exception should never be raised after a system has been delivered. Don't get me wrong, I love Ada exception handling... Exception Handling made my last large integration go much smoother than I expected. Any comments?? Michael Reznick Structured Systems & Software (3S) sss@cerf.net ---------------------------------------------------------------------- Standard disclaimer... ---------------------------------------------------------------------- ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-18 19:51 ` Marlene M. Eckert @ 1991-03-19 19:07 ` Michael Feldman 1991-03-21 3:01 ` Jim Showalter 1991-03-19 20:38 ` Charles H. Sampson 1991-03-19 21:07 ` Jim Showalter 2 siblings, 1 reply; 18+ messages in thread From: Michael Feldman @ 1991-03-19 19:07 UTC (permalink / raw) In article <311@nic.cerf.net> sss@nic.cerf.net (Marlene M. Eckert) writes: >How about exceptions should be raised only in _EXCEPTIONAL_ >situations? Reaching the end-of-file or trying to POP off an >empty stack are NOT exceptional conditions. > Well, this is getting to be an interesting thread on exception-handling philosophy. I disagree with you. A client of a stack package which - due to a logic bug in the client algorithm - tries to pop a stack which turns out to be empty, is indeed committing an error. IMHO this in indeed an exceptional situation, and raising Stack_Underflow or whatever is quite appropriate. The client should use a Stack_Is_Empty boolean function to test for the empty condition, but s'pose he doesn't? IMHO _both_ entities should be exported from a stack package. I agree that a program that tests for the empty condition by trying to pop and then handling the exception is doing violence to exceptions. Roughly the same is true for end-file conditions. Text_IO exports a perfectly good function End_Of_File for this purpose. Nevertheless, s'pose a client of Text_IO screws up and doesn't test, or tests in the wrong place? The package should raise End_Error for this unintentional attempt to read past EOF. As in the stack case, one should NOT write clients that test for normal EOF by just reading and reading until the exception is raised. Once again, that's abusive. Here's a more obvious one: Given TYPE Days IS (Mon, Tue, Wed, Thu, Fri, Sat, Sun); we find Tomorrow by writing IF Today = Days'Last THEN Tomorrow := Days'First ELSE Tomorrow := Days'Succ(Today); END IF; NOT BEGIN Tomorrow := Days'Succ(Today); EXCEPTION WHEN Constraint_Error => Tomorrow := Days'First; END; Some bit-fiddlers have argued that the latter is slightly more efficient, but I still think it's abusive. Monday follows Sunday EVERY WEEK. The only thing surprising about it is that Ada doesn't allow cyclic types! Mike Feldman ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-19 19:07 ` Michael Feldman @ 1991-03-21 3:01 ` Jim Showalter 1991-03-21 16:34 ` Exception usage design issues (was: Pre-condition vs. Post-condition) John Goodenough 1991-03-21 18:40 ` Pre-condition vs. Post-condition Michael Feldman 0 siblings, 2 replies; 18+ messages in thread From: Jim Showalter @ 1991-03-21 3:01 UTC (permalink / raw) >Some bit-fiddlers have argued that the latter is slightly more efficient, BIT FIDDLER!?!?!? BIT FIDDLER!?!?!?!? Stab me in the heart and TWIST THE KNIFE, why don't you? Sheesh. Of all the low blows over the years, this has GOT to take the cake. I realize I made an argument based on efficiency, but I assure you that was a momentary lapse. I've been accused of over-abstraction before, but never bit fiddling. I HATE bits--if there was a way to run software without hardware I'd jump at it. I'm a software architect, software process and methodology consultant, and OO/Ada trainer. Efficiency is about 96th on my list of concerns: I generally tell people the way to improve performance is to bid faster hardware, not worry about this or that register allocation word alignment or whatever. Bit fiddler. Sheesh. -- ***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd ever be able to find a company (or, for that matter, very many people) with opinions like mine. -- "When I want your opinion, I'll read it in your entrails." ^ permalink raw reply [flat|nested] 18+ messages in thread
* Exception usage design issues (was: Pre-condition vs. Post-condition) 1991-03-21 3:01 ` Jim Showalter @ 1991-03-21 16:34 ` John Goodenough 1991-03-21 18:40 ` Pre-condition vs. Post-condition Michael Feldman 1 sibling, 0 replies; 18+ messages in thread From: John Goodenough @ 1991-03-21 16:34 UTC (permalink / raw) Seeing all this discussion about the philosophy of exception usage reminded me of a short position paper I wrote a few years ago on this point. I'm posting it here because it presents some views that haven't been stated yet, and someone once told me that it had been helpful when they were designing a real system. \f USING EXCEPTIONS: SOME DESIGN ISSUES John B. Goodenough Software Engineering Institute Carnegie Mellon University, Pittsburgh, PA 15213 September 21, 1988 1 Summary This paper addresses Topic 9 of the Application Software Design working group: Discuss the best use of exceptions in the design of management information systems, their benefits and problems, including scope, propagation, control, and usage. Only Ada and PL/I provide explicit language support for handling exceptions. Since exceptions are not supported explicitly by the most commonly used programming languages,(FORTRAN, COBOL, and C) few programmers and system designers have experience in making effective use of this language feature. It's easy to think of exceptions as a kind of ``add-on'' feature of a software design, i.e., a facility whose use is left to the designers of individual modules and routines. In fact, exceptions are used most effectively only if the strategy guiding their use is developed in the earliest stages of a design. Moreover, effective use of exceptions requires attention throughout a design. In this paper, I will discuss some of the questions that should be considered by information system designers when deciding how to treat exceptions in designs. 2 Definitions First some definitions. An exception situation arises when an operation is invoked and cannot be completed in a ``normal'' fashion. From a design viewpoint, I think it is best to view an exception situation as a situation that occurs when an operation is invoked and certain input predicates fail to hold. The invoked operation detects this failure and allows the invoker to deal with the situation appropriately. The advantage of identifying and using exceptions is that it allows the effect of an operation to be extended to cover a wider range of situations, since the invoker is given the responsibility. What are some examples of input predicates whose failure gives rise to an exception situation? - For a POP operation of a stack or queue, an input predicate is ``the stack or queue has at least one element.'' An exception situation occurs when POP is invoked and this predicate does not hold. - For a READ operation on a file, an input predicate is ``there exists a record to be read.'' - For a TABLE_LOOKUP operation, an input predicate is ``the sought-for value exists.'' - For MATRIX_INVERSION, an input predicate is ``an inverse exists.'' An exception condition is signaled to the invoker of an operation when the corresponding exception situation is detected. A handler for the condition specifies the invoker's response to the exception situation. Exception situations are not always errors. They may be boundary conditions whose significance is known primarily to the operation's invoker. Exceptions serve to generalize operations (making them usable in a wider variety of situations) because: - The response to an exception situation is provided by the invoker instead of by the operation itself. - An arbitrary, operation-defined response to the situation can be replaced by an appropriate user-defined response. 3 Design Issues My definition of an exception situation leads to natural questions that should be addressed during the design phase. - First, identify the input assertions associated with an operation, i.e., the assumptions and preconditions that allow the operation to complete in a ``normal'' manner. - Then evaluate each assertion, using criteria such as: * How easy is it for the operation's invoker to check the assertion before calling the operation? * When the check fails, is a fixed response (provided by the operation) always acceptable? * An operation's behavior may be undefined if an operation is invoked when an input assertion is unsatisfied. Are the consequences of such behavior likely to be important? Depending on the answers to these questions, a designer can decide whether an exception situation should be handled by signalling an exception condition. Given a decision to handle an exception situation, there are some other design decisions that need to be considered: - Should an inquiry function be provided? - Should a boolean function be provided to check whether the exception situation will occur? An inquiry function is called after an exception is raised and provides additional information about the nature of the situation. Such functions are useful when the invoker's response to an exception situation might change depending on specific details involved in the situation. For example, if an operation detects ill-formed terminal input, the most helpful response may depend on the specific characters that were typed in. Since an exception handler does not, in general, have access to all the information available to the operation that signals the exception, the needed additional information must be provided to the invoker. There are at least two ways of doing this: - If the programming language permits exceptions to be raised with parameters, the designer needs to decide what parameters should be provided. - If no parameters can be associated with exceptions, an inquiry function must be provided. Providing an inquiry function extends the usability of an abstract data type, but it can easily violate information hiding principles, since the details of a particular exception situation might well reflect an implementation approach. Nonetheless, such functions are very useful when investigating mysterious program behavior. If they are not provided consistently throughout a design, it can be very difficult to extract the needed information by using a debugging tool. The MULTICS operating system, written in PL/I, was careful to provide such functions for all basic operations. In one case when I was using MULTICS, PL/I told me that I had encountered a device error. I would have been unable to diagnose the reason for the problem if I hadn't been able to call a function returning the full status word for the device. Of course, such a function is device dependent, and this is the disadvantage of such functions. Whenever it's not too costly, it's a good idea to provide a boolean function that checks for an exception situation. For example, if a READ operation will raise an exception when the end of a file is encountered, a function should also be provided that checks for the end of file. These functions sometimes allow more readable programs to be written. For example, if the programmer is primarily interested in whether a file is at its end, it is certainly more convenient to check this directly by calling the end-of-file function rather than by calling the READ operation just to see if it raises an exception! (And if it doesn't, you need to preserve the value that was read.) In addition, some language constructs depend on the use of predicates rather than exceptions. For example, a guard in an Ada select statement is a boolean expression. If the value of a guard depends on the state of a file and there is no end-of-file function, the programmer must go to considerable trouble to obtain the correct boolean value. Of course, it's not always appropriate to define an exception for every input predicate. For example, a precondition for the correct operation of a binary search function is that the table being searched is ordered. Checking that the table is ordered each time the search is called would completely defeat the purpose of the algorithm! Here is a case where no exception should be defined. Similarly, it is not always appropriate to provide a function to check for an exception situation independently of invoking the operation. For example, a matrix inversion operation should raise an exception when the matrix has no inverse, but since it is almost as costly to check for this situation as to attempt to find the inverse, it's probably not sensible to provide a function in this case. 4 Example As a simple example of these ideas, consider a simple function that is to calculate the sum of a set of numbers presented in some input stream. What preconditions could lead to exception situations, and what inquiry functions might be provided? For which exception situations should corresponding boolean functions be provided? The preconditions could be: - There exists a sum (i.e., the sum does not overflow; it is representable). - The input stream is not empty. - The input is syntactically valid. Possible inquiry functions and their results could be: - When overflow occurs, the inquiry function returns the sum just before the exception was raised, together with the number that was read. - When any exception occurs, an inquiry function returns how many numbers have been read, telling the programmer how much of the input stream has been processed. - When syntactically invalid input is read, an inquiry function can return the invalid string that was read. REFERENCES Issues concerning the use of exceptions, particularly in Ada, are discussed in some detail in pages 94-127 of Ada in Practice.(Ausnit, C. N., Cohen, N. H., Goodenough, J. B., and Eanes, R. S, Springer-Verlag, 1985.) -- John B. Goodenough Goodenough@sei.cmu.edu Software Engineering Institute 412-268-6391 ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-21 3:01 ` Jim Showalter 1991-03-21 16:34 ` Exception usage design issues (was: Pre-condition vs. Post-condition) John Goodenough @ 1991-03-21 18:40 ` Michael Feldman 1 sibling, 0 replies; 18+ messages in thread From: Michael Feldman @ 1991-03-21 18:40 UTC (permalink / raw) In article <jls.669524498@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes: >>Some bit-fiddlers have argued that the latter is slightly more efficient, > >BIT FIDDLER!?!?!? BIT FIDDLER!?!?!?!? > >Stab me in the heart and TWIST THE KNIFE, why don't you? Well, OK, you're not a bit fiddler. It's just that in discussing design- oriented questions like pre- and post- conditions, appropriate design with exceptions, and the like, too many folks STILL jump too quickly into micro-efficiency matters and questions of how compilers implement exceptions. This gives me a sense of deja-vu from the early days of structured programming etc., when people argued that go-to's were faster than loops and procedures, and self-modifying code was even faster than that. So what? We all know that computers and compilers both get faster over time. Actually, I wrote the "bit fiddler" comment even before I read your note, which was several notes later in my news reader. I chuckled when I read your note, because I knew SOMEONE would raise the issue. Performance issues are NOT unimportant - even this fuzzyheaded professor would agree with that - but I think discussions of marginal gains or losses in efficiency, traded off against more important design matters, just muddy the waters. Sorry, Jim .. I didn't even know your note was out there...nothing personal. Mike ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-18 19:51 ` Marlene M. Eckert 1991-03-19 19:07 ` Michael Feldman @ 1991-03-19 20:38 ` Charles H. Sampson 1991-03-21 3:06 ` Jim Showalter 1991-03-19 21:07 ` Jim Showalter 2 siblings, 1 reply; 18+ messages in thread From: Charles H. Sampson @ 1991-03-19 20:38 UTC (permalink / raw) In article <311@nic.cerf.net> sss@nic.cerf.net (Marlene M. Eckert) writes: >How about exceptions should be raised only in _EXCEPTIONAL_ >situations? Reaching the end-of-file or trying to POP off an >empty stack are NOT exceptional conditions. O. K., but that just pushes the issue off to deciding what the word _exceptional_ means. The definition I prefer to use is: "An exceptional condition is one that occurs infrequently or unexpectedly." (Not original, but I forgot whom I stole it from. I think it was John Barnes.) When teaching Ada, immediately after giving that definition I point out to the students that it does not require all exceptional conditions to be handled by exceptions. What's happening here is that we're in an area where a lot of design decisions have to be made. I'm not sure that there is a rule that can be applied to all cases and I am sure that if there is one we haven't found it yet. Unlike Mike Feldman, I have no philosophical problem with using End_error to detect end-of-file or stack underflow to determine that the stack is now empty, both satisfying the _infrequent_ criterion. (I doubt that I would ever use the latter myself, but my reasons are more esthetic than anything else.) Before I would condemn these uses in any particular situation, I would want to hear the reasons for them. This has been an interesting thread to follow as we grapple with this problem. I particularly appreciate the fact that very few dogmatic positions have been put forward. Charlie ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-19 20:38 ` Charles H. Sampson @ 1991-03-21 3:06 ` Jim Showalter 0 siblings, 0 replies; 18+ messages in thread From: Jim Showalter @ 1991-03-21 3:06 UTC (permalink / raw) >When teaching Ada, immediately after giving that definition I point out to the >students that it does not require all exceptional conditions to be handled >by exceptions. Exactly. Some are better handled by status parameters, or status fields in abstract data types, or whatever. > What's happening here is that we're in an area where a lot of design >decisions have to be made. I'm not sure that there is a rule that can be >applied to all cases and I am sure that if there is one we haven't found >it yet. Exactly. Although I do think there are heuristics, such as the infrequency criterion. For example, I have no problem with blowing up on underflow instead of pre-checking it. But I wouldn't use an exception to implement a cyclic type, since the whole point of a cyclic type is that it will be cycled through, so using the exception on 'succ is poor form. In short, I use exceptions whenever their being raised would signal to someone in a debugger that something undesirable has occurred. Underflowing a stack is undesirable. Cycling a type is normal, so trapping that in a debugger would confuse the hell out of the maintenance programmer (it brings in the oxymoronic notion of a "normal exception"!). -- ***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd ever be able to find a company (or, for that matter, very many people) with opinions like mine. -- "When I want your opinion, I'll read it in your entrails." ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-18 19:51 ` Marlene M. Eckert 1991-03-19 19:07 ` Michael Feldman 1991-03-19 20:38 ` Charles H. Sampson @ 1991-03-19 21:07 ` Jim Showalter 2 siblings, 0 replies; 18+ messages in thread From: Jim Showalter @ 1991-03-19 21:07 UTC (permalink / raw) >Reaching the end-of-file or trying to POP off an >empty stack are NOT exceptional conditions. How to you justify this? Exceptions protect clients from incorrect algorithmic implementation. If a client attempts to read past end of file or pop past empty stack, it should be notified of the fact, since the client is in error. Other mechanisms (e.g. status flags) must be checked by the client to be effective (polling model), whereas exceptions cannot be overlooked (interrupt model). Thus, exceptions are more fail-safe. You also say that you don't think an exception should ever be raised after a system is delivered. I don't see how you can meet this constraint. For example, consider interaction with a user, a la Enumeration_Io. If the user enters bogus data--which you have no control over from within the program--an exception will be raised. This is not only unavoidable, but seems desirable to me. -- ***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd ever be able to find a company (or, for that matter, very many people) with opinions like mine. -- "When I want your opinion, I'll read it in your entrails." ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-18 15:04 ` Joe Hollingsworth 1991-03-18 19:51 ` Marlene M. Eckert @ 1991-03-19 7:38 ` Jim Showalter 1991-03-19 14:46 ` Joe Hollingsworth 1991-03-22 15:18 ` Pre-condition vs. Post-condition Brad Balfour 1 sibling, 2 replies; 18+ messages in thread From: Jim Showalter @ 1991-03-19 7:38 UTC (permalink / raw) >procedure pop(s: stack) >begin > if(not empty(s)) then > -- pop the stack > else > raise underflow >end pop; >procedure pop(s: stack) >begin > stack.top := stack.top - 1; > exception > when Constraint_Error => raise underflow >end pop; Note that in the second case the procedure is faster, since it doesn't have to do the check first. Not only is it faster, it is safer, since without using tasks you cannot guarantee that between the time you checked and the time you popped it hadn't been popped elsewhere. For both of these reasons, I'd say the second version is far better than the first version, and that the original poster's thesis that exceptions should be used rarely if ever has been contradicted by the very examples provided to support his/her case! P.S. You need an "end if" in the first example. -- ***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd ever be able to find a company (or, for that matter, very many people) with opinions like mine. -- "When I want your opinion, I'll read it in your entrails." ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-19 7:38 ` Jim Showalter @ 1991-03-19 14:46 ` Joe Hollingsworth 1991-03-21 2:46 ` Jim Showalter 1991-03-21 5:12 ` Explicit vs implicit checks (was Pre-condition vs. Post-condition) Scott Carter 1991-03-22 15:18 ` Pre-condition vs. Post-condition Brad Balfour 1 sibling, 2 replies; 18+ messages in thread From: Joe Hollingsworth @ 1991-03-19 14:46 UTC (permalink / raw) In article <jls.669368339@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes: >>procedure pop(s: stack) >>begin >> if(not empty(s)) then >> -- pop the stack >> else >> raise underflow end if; >>end pop; > >>procedure pop(s: stack) >>begin >> stack.top := stack.top - 1; >> exception >> when Constraint_Error => raise underflow >>end pop; > >Note that in the second case the procedure is faster, since it doesn't >have to do the check first. I figured I'd see this argument, but I don't buy it. 1) Refering back to Kernighan & Plauger's "The Elements of Programming Style," I'll quote another rule: "Write clearly - don't sacrifice clarity for 'efficiency'." 2) Ok, so you think that the second version of Pop is faster because it is not doing the test. Well there may not be an explicit test, but there sure has to be an implicit one. The compiler has to generate code to test to see if a constraint error needs to be raised, i.e. there is run time checking going on here. And you sure can't use the SUPRESS pragma to get rid of it. There is testing going on here, either you see it explicitly in the code, or it gets done for you by run time checks. >Not only is it faster, it is safer, since >without using tasks you cannot guarantee that between the time you >checked and the time you popped it hadn't been popped elsewhere. The examples given were for the sequential world, there was no mention in the original posting or my follow up about tasking. >For both of these reasons, I'd say the second version is far better than >the first version, and that the original poster's thesis that exceptions >should be used rarely if ever has been contradicted by the very examples >provided to support his/her case! I don't think the follow up poster has really come up a with good case. His/her case is mainly based on efficiency concerns, which obviously doesn't hold since run time checks have to be performed. And, the follow up poster's far better version is still not as clear as the first version. >P.S. You need an "end if" in the first example. Thank you, I've edited the above example to include "end if." Joe holly@cis.ohio-state.edu ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-19 14:46 ` Joe Hollingsworth @ 1991-03-21 2:46 ` Jim Showalter 1991-03-21 5:12 ` Explicit vs implicit checks (was Pre-condition vs. Post-condition) Scott Carter 1 sibling, 0 replies; 18+ messages in thread From: Jim Showalter @ 1991-03-21 2:46 UTC (permalink / raw) >1) Refering back to Kernighan & Plauger's "The Elements of Programming >Style," I'll quote another rule: >"Write clearly - don't sacrifice clarity for 'efficiency'." Is this Kernighan of Kernighan & Ritchie? (If so, he doesn't follow his own advice.) >2) Ok, so you think that the second version of Pop is faster >because it is not doing the test. Well there may not be an explicit >test, but there sure has to be an implicit one. The compiler has >to generate code to test to see if a constraint error needs to be >raised, i.e. there is run time checking going on here. Yes, but the checking doesn't have to involve the overhead of a context switch, as is required in your version with the call to another subprogram. So my version is still faster, even with the checking. >>Not only is it faster, it is safer, since >>without using tasks you cannot guarantee that between the time you >>checked and the time you popped it hadn't been popped elsewhere. >The examples given were for the sequential world, there was no >mention in the original posting or my follow up about tasking. Yeah, but if you do it right the first time, migrating to the parallel world isn't a maintenance nightmare. >I don't think the follow up poster has really come up a with good >case. His/her case is mainly based on efficiency concerns, Well, I didn't make the case, but I actually think the version with the exception is at least as easy to understand as the first version. In this particular case the distinction is not as great, but consider the classic "if/then" checking that is performed in languages without exceptions: begin if some_precondition and then some_other_precondition and then yet_another_precondition then do_what_you_really_wanted_to_do; end if; end; Contrast this to the exception version: begin do_what_you_really_wanted_to_do; exception when this_problem_occurs => take_corrective_action; when that_problem_occurs => take_other_corrective_action; end; I argue that it is far easier in the exception case to figure out what is supposed to happen, and that the reader can skip all the error handling/checking stuff if he/she is just interested in the main control flow. I've seen code written with so much precondition checking that finding the actual statement that did anything was like finding a needle in a haystack. With exceptions I don't have this problem. Furthermore, consider the use of procedures that raise exceptions with the intent that they express the preconditions required: procedure assert_condition_valid (...) is begin if not some_precondition then raise this_problem_occurs; end assert_condition_valid; procedure assert_another_condition_valid (...) is... -- Similar. begin assert_condition_valid (...); assert_another_condition_valid (...); do_what_you_really_wanted_to_do; exception when this_problem_occurs => take_corrective_action; when that_problem_occurs => take_other_corrective_action; end; Now in this case, I not only can find what is really being done, I can also find out what the preconditions are, all without getting snarled up in N levels of if-then checking. It reads well, it is easy to maintain, and it depends on exceptions. -- ***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd ever be able to find a company (or, for that matter, very many people) with opinions like mine. -- "When I want your opinion, I'll read it in your entrails." ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Explicit vs implicit checks (was Pre-condition vs. Post-condition) 1991-03-19 14:46 ` Joe Hollingsworth 1991-03-21 2:46 ` Jim Showalter @ 1991-03-21 5:12 ` Scott Carter 1 sibling, 0 replies; 18+ messages in thread From: Scott Carter @ 1991-03-21 5:12 UTC (permalink / raw) In article <98063@tut.cis.ohio-state.edu> Joe Hollingsworth <holly@cis.ohio-state.edu> writes: >In article <jls.669368339@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes: >>[ use built-in checking via CONSTRAINT_ERROR rather than explicit test ] >>Note that in the second case the procedure is faster, since it doesn't >>have to do the check first. > >I figured I'd see this argument, but I don't buy it. > >1) Refering back to Kernighan & Plauger's "The Elements of Programming >Style," I'll quote another rule: > >"Write clearly - don't sacrifice clarity for 'efficiency'." " - at first, until you find the real cpu burners [if they are localized]. Recode them for efficiency (document it!), so you have more slop to make most of the code clearer and more maintainable." POP is an example of a system utility for which the tradeoffs push more for efficiency than in most of the code (maybe). > > >2) Ok, so you think that the second version of Pop is faster >because it is not doing the test. Well there may not be an explicit >test, but there sure has to be an implicit one. The compiler has >to generate code to test to see if a constraint error needs to be >raised, i.e. there is run time checking going on here. And you >sure can't use the SUPRESS pragma to get rid of it. Don't be so sure about this, or assume that the check has the anywhere near the same cost as the explicit test. A constraint test for < 0 is a single instruction in some machines (e.g. TBNDU on the 88000, or on any machine which has a trap on bit instruction), and further that instruction has more slop as far as where in the canonical order it must go compared to the explicit test. A really snazzy compiler which is inlining Is_empty() might reduce the explicit check cost down to about three cycles (and introduce a block boundary), but we just aren't there yet. > >Joe >holly@cis.ohio-state.edu Scott Carter - McDonnell Douglas Electronic Systems Company carter%csvax.decnet@mdcgwy.mdc.com (preferred and faster) - or - carters@ajpo.sei.cmu.edu (714)-896-3097 The opinions expressed herein are solely those of the author, and are not necessarily those of McDonnell Douglas. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-19 7:38 ` Jim Showalter 1991-03-19 14:46 ` Joe Hollingsworth @ 1991-03-22 15:18 ` Brad Balfour 1 sibling, 0 replies; 18+ messages in thread From: Brad Balfour @ 1991-03-22 15:18 UTC (permalink / raw) In article <jls.669368339@rutabaga> jls@rutabaga.Rational.COM (Jim Showalter) writes: >>procedure pop(s: stack) >>begin >> if(not empty(s)) then >> -- pop the stack >> else >> raise underflow >>end pop; >>procedure pop(s: stack) >>begin >> stack.top := stack.top - 1; >> exception >> when Constraint_Error => raise underflow >>end pop; >Note that in the second case the procedure is faster, since it doesn't >have to do the check first. Not only is it faster, it is safer, since >without using tasks you cannot guarantee that between the time you >checked and the time you popped it hadn't been popped elsewhere. For >both of these reasons, I'd say the second version is far better than >the first version, and that the original poster's thesis that exceptions >should be used rarely if ever has been contradicted by the very examples >provided to support his/her case! It should be kept in mind, however, that the first example will always produce correct results (except in the presence of tasks where it should be replaced with a concurrent component), but that the second example breaks in the presence of a "pragma supress". However, on most compilers, it is not necessary to change the code to get this effect. Instead, all one has to do is add a switch to the compiler run to turn off the constraint checks. Then, push will trash memory randomly and pop will return garbage rather than raise underlfow. Also, the second example is not safe in the presence of multiple tasks. It is possible for a second thread of control to be changing the contents of the stack at the same time as the first so that between the read of stack.top and the computation of the -1 and then the assignment there are plenty of opportunities of the push to change (and write to) stack.top. It is a mistake to assume that the line "stack.top := stack.top - 1;" is a single atomic assignment. Brad Balfour EVB Software Engineering, Inc. brad@terminus.umd.edu ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: Pre-condition vs. Post-condition 1991-03-15 19:07 ` Michael Feldman 1991-03-17 12:26 ` George C. Harrison, Norfolk State University 1991-03-18 15:04 ` Joe Hollingsworth @ 1991-03-19 18:17 ` Mike Gilbert 2 siblings, 0 replies; 18+ messages in thread From: Mike Gilbert @ 1991-03-19 18:17 UTC (permalink / raw) >>Say we have a function CAPITAL which, given a country's name, returns its >>capital city. If the given country does not exist, an exception >>COUNTRY_ERROR >>is raised. Should the given country's presence be listed as a pre-condition >>for this function, or should its absense (it doesn't exist) and the raising >>of COUNTRY_ERROR be listed as a post-condition? >> >> ... > >Hmmm. Interesting question. I have always taught - and thought of - pre- >conditions as a set of "contract terms" which, if they are met, would >obligate the function writer to write code that delivers the right results. > ... >Failure of a caller to meet a pre-condition violates the contract between >function writer and function user, in a way that the behavior of the >function is _unpredictable_, for example the actual parameter is >unitialized. If the "garbage" in the parameter _happens_ to constitute >an in-range value, the function delivers a "correct" answer, coincidentally. >Otherwise, an exception may be _unexpectedly_ raised (constraint_error, say). >Since the caller has violated the contract, he gets what he deserves (an >unexpected propagation of an exception). So the pre-conditions are really >saying "if you violate these, all bets are off on what this function does." > >This argument makes sense to me from a theoretical standpoint. From a > practical standpoint, in describing the interface to a function, how does >one distinguish between violations that result in a _predictable_ behavior >and those that do not? > >Mike Feldman I agree with Mike Feldman's description of a function's pre-conditions as a contract, which, if satisfied, will cause the function to produce the documented post-conditions, and, if violated, will produce an _unpredictable_ result. Given this point of view, if a function guarantees to raise a specific exception under certain pre-defined conditions, then the raising of that exception must be considered to be part of the contract. Because of the guarantee of an exception, this case is very different from a function's unpredictable behavior if preconditions are violated. Thus, to answer the original question, the non-existence of a country causing COUNTRY_ERROR to be raised should be listed as a post-condition. If COUNTRY_ERROR is a post-condition, then the programmer has several options for how to handle the possible COUNTRY_ERROR exception in the calling context: a If the programmer can guarantee that only valid countries will be passed to CAPITAL, then, by the terms of the post-condition, the function will never raise COUNTRY_ERROR, and the calling context doesn't have to allow for it. b If the programmer can't absolutely guarantee that the country name is valid, but he doesn't expect to pass an invalid one, then the choice of whether the calling context should handle COUNTRY_ERROR depends on how robust the programmer wishes to make the program. (For example, the country name could be generated by another function which has not been formally verified but it believed to generate correct output.) c If the programmer has no idea whether the country name is valid, then the calling context had better handle COUNTRY_ERROR. (For example, the country name could be read as input from a user.) Contrast these options with an alternate specification of CAPITAL in which the country name must be valid as a pre-conditon, and thus an invalid country name violates the function's contract. With this specification, the function CAPITAL will be _unpredictable_ if passed an invalid country name. That is, CAPITAL _could_ raise COUNTRY_ERROR, it _could_ raise any other exception, it _could_ always return "SHANGRI-LA", it _could_ go into a loop, etc. etc. So, if COUNTRY_ERROR is not a possible post-condition, then options "b" and "c" above are not possible, because the programmer can't count on COUNTRY_ERROR being raised. What this all comes down to is that the post-conditions are what a function's user can _count on_ given input that satisfies the pre-conditions. Thus, since the original question stated that COUNTRY_ERROR _is raised_ for a non-existent country, then that specific exception should be listed as a post-condition. Alternatively, if the original question had said something like "we've currently implemented CAPITAL to raise COUNTRY_ERROR if the input country doesn't exist, but we don't want to promise that it will always do so," then users of CAPITAL would _not_ be able to count on that behavior, and then the appropriate specification would be a pre-condition that the country must exist. BTW, this is a distinction with a great deal of practical importance. Many large integrated software systems (e.g., a complex database package modified to support distributed data by using a commercial network package) develop errors because the integrator counts on behavior that a software package _currently_ exhibits (esp. under unusual circumstances), but that the developer never intended to _guarantee_. Then, when the next release of the software package comes out, with different behavior under those circumstances, the integrated system fails. Mike Gilbert Software Leverage ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~1991-03-22 15:18 UTC | newest] Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 1991-03-15 3:57 Pre-condition vs. Post-condition Chris M. Little 1991-03-15 19:07 ` Michael Feldman 1991-03-17 12:26 ` George C. Harrison, Norfolk State University 1991-03-18 15:04 ` Joe Hollingsworth 1991-03-18 19:51 ` Marlene M. Eckert 1991-03-19 19:07 ` Michael Feldman 1991-03-21 3:01 ` Jim Showalter 1991-03-21 16:34 ` Exception usage design issues (was: Pre-condition vs. Post-condition) John Goodenough 1991-03-21 18:40 ` Pre-condition vs. Post-condition Michael Feldman 1991-03-19 20:38 ` Charles H. Sampson 1991-03-21 3:06 ` Jim Showalter 1991-03-19 21:07 ` Jim Showalter 1991-03-19 7:38 ` Jim Showalter 1991-03-19 14:46 ` Joe Hollingsworth 1991-03-21 2:46 ` Jim Showalter 1991-03-21 5:12 ` Explicit vs implicit checks (was Pre-condition vs. Post-condition) Scott Carter 1991-03-22 15:18 ` Pre-condition vs. Post-condition Brad Balfour 1991-03-19 18:17 ` Mike Gilbert
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox