|
Prev: Teaching OO
Next: multimethod + multiple inheritance
From: Dmitry A. Kazakov on 3 Mar 2006 13:46 On Fri, 03 Mar 2006 18:13:15 +0000, Miguel Oliveira e Silva wrote: > You assume that exceptions raised due to false preconditions, > disguised as a postcondition, are part of a correct program. > I do not (is this reasonable to you?). No. In my view a run-time precondition [a real one] if evaluated, then that shall happen on an independent context. So exception propagation would be wrong. Exception is a synchronous transfer of control. Failed precondition shall perform asynchronous transfer of control (ATC.) Like hardware interrupts do. Ada has software ATCs. Anyway, after an ATC any continuation on the context caused ACT is impossible. Basically it is an abort, this the only way real preconditions might be checked at run-time - by an *independent* system. The states of that system are not ones of the monitored system. > My point, is that P3 is not part of "do_y"'s precondition > (an exception is never a precondition of normal > routines/instructions, only for catch/rescue blocks). Yes. > So I was using a "reductio ad absurdum" > argument, in which I attempted to show > that exceptional behavior (being goto like) > is outside normal structured (single entry > and exit points) view of programs. Sort of. It is like jumping out of a loop. It might become very nasty when misused. Still exceptional behavior is a behavior. Program error is not. > So, exceptions are required to take into > consideration code outside normal > program structured instructions > (as you did, by including a possible > catch block). Using them for normal > program behavior is not a structured > approach, hence it makes it much > harder to reason on program > correctness. They need better language support. For example, Java's contracted exceptions is a great idea, though, maybe, poorly implemented. When considered as a part of the contract, exceptions can be statically checked. This definitely requires language designers to do some homework. But if done properly, it should allow building much safer fault-tolerant systems. Did you saw a program which can really recover after a storage allocation fault? -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Miguel Oliveira e Silva on 3 Mar 2006 15:42 Oliver Wong wrote: > "Miguel Oliveira e Silva" <mos(a)det.ua.pt> wrote in message > news:44087328.68DFA1A7(a)det.ua.pt... > > Oliver Wong wrote: > > [snipped everything except Contract-A and contract-B, which seems to be the > core issue now] > > >> There's is no disagreement here. I'm just saying there exist a > >> mapping > >> from contract A to contract B, where A has one or more pre-condition, and > >> contract B has fewer pre-conditions, such that any client who accepts A > >> will > >> be willing to accept B as well. > >> > >> E.g.: > >> > >> /* > >> Contract A: > >> Pre-condition: s is never null. > >> Post-condition: returns s. > >> > >> Contract B: > >> Pre-condition: none. > >> Post-condition: if s is null, throws NullPointerException; otherwise, > >> returns s. > >> */ > >> > >> Any client who accepts contract A here, should also be willing to > >> accept > >> contract B. From his perspective, they do the same thing. > > > > In contract B the precondition should have been the same > > as in A: (s != NULL). > > You just made the mistake to hide it inside the postcondition > > (wrongly making the clients belief that the routine can do > > useful work regardless of the value of s). > > Whether something is "useful" or not depends on the client's opinion. > Perhaps the client specifically wants a NullPointerException (and not , > e.g., AssertionException) to be thrown when s is equal to null, and > therefore B is MORE useful than A. If throwing an exception is useful to the client (other than to track program errors), then I claim that he is using a disguised goto (the exception). I claim that in your example, there is always a much simpler structured control instruction that can do the same useful work, in a much better way (and easier to ensure its correctness). That way we need not to confuse normal behavior with exceptional behavior. So, what I am arguing, is that that exception should only be reserved to signal a program error (s == NULL) on the responsibility of the client (precondition). B's contract does not guarantee the object's invariant when the exception is throwned. A does. Thinking better, A ensures even more than the invariant. It guarantees that *nothing* happens inside the object when (s==NULL). > A does not specify what will happen if s is null. If it is DbC and a runnable assertion: an exception will be raised. > If the client is > writing code which critically depends on NullPointerException being thrown > when s is null, then the client MUST use B, and CANNOT use A. If you insist to specify a particular value for the exception (NullPointerException), then simply put that behavior in the precondition comment, thus ensuring to the client that the object's state won't be modified if "s" equals NULL (which is much more information to the client than in B's). > If the client will never set s = null, the nthe client can use either B > or A, as they will both perform identically for all situations the client > will encounter. Correct. > That is why I say any client who accepts contract A will also accept > contract B. Wrong. (Unmodified object.) > > > > Contract B, is much (much) worse than A's because, > > if s happens to be NULL, the client is not ensured (since > > the exceptional behavior is in the postcondition) that > > the routine did not do nothing else besides raising an > > exception. Hence he may lose the extremely important > > guarantee that the supplier object invariant is still correct > > even if the exception was raised (DbC ensures that). > > That's a very (very) serious problem resulting > > from putting the (exceptional) behavior of broken > > preconditions inside postconditions. > > This is a problem with all contracts: post conditions generally don't > list all the things they DON'T do. Precisely one of my points. I'm glad that you agree with me that it is absurd to express non-behavior in assertions. > E.g.: > /* > Pre-condition: None. > Post-condition: Returns true. Does not emit anything to standard out. Does > not allocate RAM. Does not kill children. Does not cause your car to > explode. Etc. > */ > bool getTrue() { > return true; > } > > Usually, if a contract does not mention behaviour Foo, it can safely be > assumed that the method will NOT do contract Foo. Only the theoretical optimistic programming world. In the practical (and also theoretical) world we can only be sure that nothing nasty has not been done by a routine *before* it starts its execution (precondition). > If this is a problem for > the client, the client should negotiate a new contract where the desired > behaviour is made explicit. Here I agree with you. I just don't agree that any client who accepts A also accepts B. However, anyone accepting B will most likely accept A, if it is ensured that a NullPointerException exception is raised in the precondition fails (Right?). > Contract B says that the method will return s except when s is null, in > which case it will throw NullPointerException. Note that the post-condition > does not say things like "BTW, I am free to violate any class constraints, > etc." And because it doesn't say that, it won't do that. You are a very optimistic person. If a condition is tested after the fact, it cannot be 100% sure something nasty has occurred, neither on the exact location inside the routine in which the exception was throwned. > Exceptions do not indicate that class invariants might have been > violated. This fear is unjustified. Exceptions are an abrupt and immediate interruption of the execution of a routine. Hence they cannot ensure nothing. It is only their *location* that ensures (or not) the invariant. The only location in which we are ensured that nothing has happened in the object, is before the routine starts its execution (precondition). > > > > Another serious problem with B's like contracts > > happens when the programmer misses an > > exceptional behavior in postconditions for > > some runnable (hidden) precondition. > > The client knows that such a precondition > > exists, but he will be unsure if the supplier > > will indeed respond with an exception. > > Isn't it much simpler to take that for > > granted as in DbC (without moving > > assertions to wrong places)? > > There is NO pre-condition in contract B. Not even a hidden one. I don't > know why you say there is. Just tell me what useful work -other than error detection- a client expects from that exception, that should not be done with normal structured instructions? If the client wants an exception as useful work (whatever that might be) out of the routine, why then doesn't he raises the exception itself? I claim that if there is no useful work out of that exception (other than error handling) then *it is* a disguised precondition. A postcondition with exceptions, that do not result from a false assertion (program error), is just a way to ask for trouble, and to promote the use of exceptions as goto's. > If the client "knows" that such a pre-condition > exists, then that client has obviously misunderstood the contract. There is > not pre-condition. > > If the client carefully reads the contract, he will see that the > behaviour of the method is to return s when s is not null, and to throw > NullPointerException when s is null. > The client therefore knows the behaviour of the method in the cases > where s = "Dog", s = "Hello World!", s = null, and an just about every other > situation that will arise. The behaviour is explicitly stated in the > contract. > > > > > Tell me, why do you put exceptional behavior > > inside postconditions, and not inside preconditions? > > (Which would bring your normal *and* exceptional > > behavior to match DbC.) > > Contract A and contract B describe two different behaviours. If I want > the behaviour of contract B, I must use contract B. I cannot use contract A, > because it (might) do something different than contract B. > > The original claim up thread is that all contracts with runnable > pre-conditions that are accepted by a given client can be converted into > another contract with fewer (possibly zero) pre-conditions which will also > be accepted by a client. That claim is wrong (unmodified object state). > The reverse is not nescessarily true. I have the opposite view. A client expecting B will easily accept A, if it is ensured that a precondition failure raises that very same exception. He is 100% ensured in this case that the object did not do anything. > > > > Afterall, the exception was caused by something > > that the client did (s=NULL), and not as a result > > of something inside the supplier's code. (Do we > > agree in this point?) > > No, I'm not sure that we are in agreement. When you talk about "cause", > it can get philosophical too: > > A: "Why was this exception thrown?" > B: "Because there's a throw clause in the supplier's code." > A: "Why is there a throw clause in the supplier's code?" > B: "So that the code satisfies the contract given." > A: "Why is the contract written the way it is written?" > B: "Because that's what the client and the supplier agreed upon." > etc. > > > > > Again, DbC approach is much simpler and safe: > > any detected false assertion raises an exception. > > This sentence does not nescessarily imply that ALL raised exceptions are > caused by a detected false assertion. Agreed. (Just be careful to not enforce the use disguised goto's.) > - Oliver -miguel -- Miguel Oliveira e Silva
From: Daniel T. on 3 Mar 2006 19:08 In article <16nNf.9996$dg.1352(a)clgrps13>, "Oliver Wong" <owong(a)castortech.com> wrote: > "Miguel Oliveira e Silva" <mos(a)det.ua.pt> wrote in message > news:4405BE7C.41AD05C9(a)det.ua.pt... > > > >> yet you spend a lot of bandwidth > >> considering what the behavior of the code should be when a precondition > >> is not met... > > > > (... normal versus exceptional ...) > > The problem is the conflating of two concepts of "exceptional" here. > There's exceptional situations which are correctly handled (e.g. by using > catch blocks) and can be part of the well defined behaviour of a correctly > implemented program. Then there's the "super-exceptional" situations which > indicate that there is a bug in the program. > > /* > Pre-conditions: filename should refer to a valid file. > Post-conditions: Returns a handle to the specified file, or throws > FileNotFoundException if the file does not exist. > */ > void File openFile(String filename) { > //Implementation goes here > } > > If the file doesn't exist, by contract, the method MUST throw an > FileNotFoundException. It is NOT a pre-condition that the file must exist. > The file may or may not exist; the behaviour of the method is defined in > both cases. Nor is it possible for the file's existence to be in the require clause. Despite the fact that it can be tested, the file may be removed between the test and the actual attempt to open it. This is something Miguel didn't catch. > > Another thing that puzzles me, is why do you keep arguing that > > runnable preconditions are not preconditions just because > > they do what is required to be done when an incorrect program > > is detected? > > I believe Daniel is taking the position that if "preconditions" are > runnable, then they are part of the well defined behaviour of the program, > and thus are part of the post-condition contract. It's not something I agree > with though. Close, I'm taking the position that if "preconditions" are runnable AND they are part of the well defined behavior of the function (as these so called "runnable preconditions" in Eiffel, then they are part of the post-condition contract. If you explicitly define what happens if value == 0, then that is what *must* happen, it is no longer undefined. > (IMHO,) whether something is a pre-condition or not depends on the > person writing the contract, and NOT on the implementation of the method > that is supposedly fufilling that contract. I agree with the above, in Eiffel when you put an assertion in the require clause, you are establishing a definitive behavior for a particular condition, not simply leaving it undefined. As I said, this is a good thing. Undefined behavior is bad, lots of preconditions are bad.
From: Miguel Oliveira e Silva on 4 Mar 2006 11:17 "Daniel T." wrote: > In article <16nNf.9996$dg.1352(a)clgrps13>, > "Oliver Wong" <owong(a)castortech.com> wrote: > > > "Miguel Oliveira e Silva" <mos(a)det.ua.pt> wrote in message > > news:4405BE7C.41AD05C9(a)det.ua.pt... > > > > > >> yet you spend a lot of bandwidth > > >> considering what the behavior of the code should be when a precondition > > >> is not met... > > > > > > (... normal versus exceptional ...) > > > > The problem is the conflating of two concepts of "exceptional" here. > > There's exceptional situations which are correctly handled (e.g. by using > > catch blocks) and can be part of the well defined behaviour of a correctly > > implemented program. Then there's the "super-exceptional" situations which > > indicate that there is a bug in the program. > > > > /* > > Pre-conditions: filename should refer to a valid file. > > Post-conditions: Returns a handle to the specified file, or throws > > FileNotFoundException if the file does not exist. > > */ > > void File openFile(String filename) { > > //Implementation goes here > > } > > > > If the file doesn't exist, by contract, the method MUST throw an > > FileNotFoundException. It is NOT a pre-condition that the file must exist. > > The file may or may not exist; the behaviour of the method is defined in > > both cases. > > Nor is it possible for the file's existence to be in the require clause. > Despite the fact that it can be tested, the file may be removed between > the test and the actual attempt to open it. Correct. > This is something Miguel didn't catch. I did mentioned that problem, which I have classified as an external contract broken. http://groups.google.com/group/comp.object/tree/browse_frm/thread/b9462e645bc4798d/fcc473cb64842ae2?rnum=21&_done=%2Fgroup%2Fcomp.object%2Fbrowse_frm%2Fthread%2Fb9462e645bc4798d%2F76f50acc016b1fbb%3Ftvc%3D1%26#doc_269e3a59a5710360 My position about it, is that normal program behavior should rule out normal attempts to open inexistent files (meaning that a program attempt to open a file is required to be always preceded by the precondition that the file exists). To increase the reliability of a program, one is required to expect exceptions when such (rare, but quite possible) situations occur, and to act accordantly. > > > Another thing that puzzles me, is why do you keep arguing that > > > runnable preconditions are not preconditions just because > > > they do what is required to be done when an incorrect program > > > is detected? > > > > I believe Daniel is taking the position that if "preconditions" are > > runnable, then they are part of the well defined behaviour of the program, > > and thus are part of the post-condition contract. It's not something I agree > > with though. > > Close, I'm taking the position that if "preconditions" are runnable AND > they are part of the well defined behavior of the function (as these so > called "runnable preconditions" in Eiffel, then they are part of the > post-condition contract. Don't call it postcondition. Postconditions are conditions which apply only after the execution of a routine. A failed precondition ensures the client that the exception was raises before the routine executes (nothing happens in the supplier object). When you put a exception behavior in a postcondition, as a result of a hidden precondition (as you are acknowledging) the client looses the absolute certainty that the exception was indeed raises *before* anything has happened inside the supplier object. It is expected, in such exceptional behaviors (which we both agree that should occur), that the supplier won't do anything useful (to raise an exception is never the goal of a routine, or else the client could do it himself directly). So it is an exceptional behavior resulting from something the client did: Hence it is a precondition. Why don't you put the exceptional behavior within the precondition (if s == NULL then exception NullStringPointer is raised)? This way the contract is clearer, and the client will be correctly informed that if he persists to pass NULL pointer to the routine he will receive an exception. > If you explicitly define what happens if value > == 0, then that is what *must* happen, it is no longer undefined. Broken detected contracts have a very well defined behavior (exceptions). > > (IMHO,) whether something is a pre-condition or not depends on the > > person writing the contract, and NOT on the implementation of the method > > that is supposedly fufilling that contract. > > I agree with the above, in Eiffel when you put an assertion in the > require clause, you are establishing a definitive behavior for a > particular condition, not simply leaving it undefined. > > As I said, this is a good thing. Agreed. > Undefined behavior is bad, lots of preconditions are bad. Why do you keep attaching undefined behavior to preconditions? A precondition is a condition expected to occur before a routine starts its execution. That is not necessarily equal (as I have been arguing since the beginning) to undefined behavior when the precondition is false. If such broken contract is detected, its behavior is to raise an exception. To move this to postconditions is to fool everyone about the real (useful) routine's intents (which is never to raise exceptions), and to loose the guarantee that the supplier object will remain untouchable (and usable). Contracts, either expressed as preconditions, postconditions and invariants, are good in the construction of reliable quality programs. Best regards, -miguel -- Miguel Oliveira e Silva
From: Miguel Oliveira e Silva on 4 Mar 2006 12:17
"Dmitry A. Kazakov" wrote: > On Fri, 03 Mar 2006 18:13:15 +0000, Miguel Oliveira e Silva wrote: > > > You assume that exceptions raised due to false preconditions, > > disguised as a postcondition, are part of a correct program. > > I do not (is this reasonable to you?). > > No. In my view a run-time precondition [a real one] if evaluated, then that > shall happen on an independent context. Well, in DbC -although both worlds share the program state- the exceptional world should be taken as being separated from the normal world (this has been one of my main "fights" in this thread). That is why we should never use exceptions to do normal useful work in programs (using exceptions as goto's). > So exception propagation would be > wrong. Wrong would be to not propagate exceptions when a false assertion is detected. > Exception is a synchronous transfer of control. A very important property for assertions, as it ensures nothing else happens in the program state (no attempt to write in arrays outside their boundaries, etc.) after its failure. > Failed precondition > shall perform asynchronous transfer of control (ATC.) (Do you mean that the program is allowed to continue after such failure?) What if, in the program being monitored, a false precondition was detected in a routine which launches a nuclear missile? The synchronous internal exceptional behavior is essential (regardless of possible external concurrent, synchronous or asynchronous, entities). On the other hand, to detect false assertions one is required to observe the programs internal state. That is what assertions do. Nothing prevent you from having external independent entities to redundantly approach that monitoring behavior, but it is essential that inside programs a false detectable assertions is taken care of in the right place (where the assertion applies, so in the case of preconditions: before the routine starts its execution, and not in postconditions). As I said, in DbC, assertions not only allow a precise and correct (although not complete) error detection, but they also prevent that nasty things happen to the program state (out of bound accesses to array, etc.). So, many times we can recover safely from a broken exception. If not, then not only the program aborts, but also, with DbC assertion mechanism, it may also precisely inform the outside world what has happened, thus allowing a more precise program replacement. > Like hardware > interrupts do. Ada has software ATCs. Anyway, after an ATC any continuation > on the context caused ACT is impossible. So the failed program behavior is synchronous after all. It stop immediately. Are ATC's an exception mechanism to outside concurrent entities? Exception are a similar internal thing. They stop normal program execution, passing the control to the separated exceptional world (we don't disagree that much in this regard). > Basically it is an abort, this the > only way real preconditions might be checked at run-time - by an > *independent* system. I've already showed you that is not true (you take a very extreme outside view of programs, as if programs could not know nothing of themselves!). A correctly placed precondition prevents the program to harm itself. Hence its state remains quite trustable. It is trivial to test inumerous real preconditions when a program is executing (non negative square roots, etc.). That does not mean, of course, that *all* preconditions can always be safely tested. In such cases I agree with you that it is important to rely on outside independent entities (protected by process alike runtime boundaries). But, Dmitry, to pick in such *particular* cases and generalize them to *all* cases as you are doing, is simply absurd. > The states of that system are not ones of the > monitored system. That is good, because they are protected. That, however, does not rule out the inumerous cases in which the states can be safely shared. > > My point, is that P3 is not part of "do_y"'s precondition > > (an exception is never a precondition of normal > > routines/instructions, only for catch/rescue blocks). > > Yes. > > > So I was using a "reductio ad absurdum" > > argument, in which I attempted to show > > that exceptional behavior (being goto like) > > is outside normal structured (single entry > > and exit points) view of programs. > > Sort of. Either it is, or it isn't! > It is like jumping out of a loop. And isn't that exactly what I'm saying (goto like)? > It might become very nasty when misused. Of course. Structured programming should be part of the formation of all programmers. > Still exceptional behavior is a behavior. Program error is not. Exceptional behavior is the behavior a incorrect program should take. Then, either the program is unable to cope with the failure and exceptions are propagated until the final abortion of the program (nothing stops another program to take its place as you suggest); or else somewhere in between the program is able to safely replace the faulty code (avoiding a new "big-bang"). > (...) > -- > Regards, > Dmitry A. Kazakov > http://www.dmitry-kazakov.de -miguel -- Miguel Oliveira e Silva |