comp.lang.ada
 help / color / mirror / Atom feed
* 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 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-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

* 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-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-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-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: 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

* 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: 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

* 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-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

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