|
Prev: Teaching OO
Next: multimethod + multiple inheritance
From: Oliver Wong on 2 Mar 2006 12:15 "Miguel Oliveira e Silva" <mos(a)det.ua.pt> wrote in message news:440712A1.1B61BC5C(a)det.ua.pt... > Oliver Wong wrote: > >> /* >> 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. > > Don't put a failure of a precondition in the postcondition. > If the (runnable) precondition fails the function won't > be executed. > > I know that in languages with the try/catch construct, > you are used to think of exceptions as a result of the > execution of a block, but that is not entirely correct. > After all if an exception happens inside a function > it will only be cached in the first nested try/catch block, > not necessarily in the direct caller of the function. > >> */ >> void File openFile(String filename) { >> //Implementation goes here >> } >> >> If the file doesn't exist, by contract, the method MUST throw an >> FileNotFoundException. > > Yes, if it is in the contract (and it should), an exception > must be raised. But not as a result of a postcondition > (the failure was in the precondition). This is not a mere > detail (so please excuse my stubbornness). A precondition > failure, points the accusing finger towards the caller. > There is no need to express that fact in a postcondition. > The postcondition should only be concern with > the correctness (within the normal execution world, > and not in the exceptional world) assertions applicable > to the implementation of the routine. This DbC approach > simplifies things tremendously. > > The exception should be raised not because the > postcondition says so, but simply because there > was a failure in the precondition. > > I have also a pragmatic critique to that approach. > How do you impose, at runtime, that "postcondition" > part of the contract to a method (and to all its possible > redefinitions in descendant classes)? > > Why put something as a comment (//throw exception) > when it can (and should) be part of normal runnable > assertion semantics? > > What if you forgot to express exceptions in the > postconditions in method with (runnable) > preconditions? > > Does that mean that not raising an exception would > be a valid "behavior" of the module when the > precondition fails? > > In programming (as in life) there are many > things we take from granted (an exception is > the response to a false assertion). No need, and > no gain, to express them everywhere. > >> 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. >> >> However, if the filename does not refer to a legal file name; for >> example if it is equal to "!!*$*!(*", then the behaviour is NOT specified >> by >> the contract. > > The normal behavior isn't (by definition). But if the method has the > runnable precondition: file_exists(filename), then an exception > will be raised and no harm was done to the object's state > (the routine was not executed). I think there was a miscommunication about the pre-condition here. The string called filename must be a valid filename. There may or may not exist a file with such a name. The pre-condition only asks that the filename is valid for the given OS (e.g. "*" is not a valid filename in Windows). If the filename is valid, but there does not exist such a file, then the pre-condition is NOT violated. Can you re-respond to my previous example with this new understanding in mind? [...] >> >> So Daniel, I think, is saying that runnable pre-conditions can always >> be >> converted to post-conditions. > > That's where he is wrong. You see, in your workaround runnable assertion > the precondition is correctly putted *before* the routine starts the > execution > of its hopefully useful algorithm. If I call "foo" in (C++/Java) without a > try/catch block the exception will pass by me (without I'm being aware of > it). > Hence: > > // somewhere in a program > do_x; > r = foo(s); > do_y(r); > > before "do_y" the assertion that this code ensures is the postcondition > of "foo", which -in order for the program not to be incorrect- should > ensure do_y's precondition. > It is completely absurd and useless to thing that "do_y" will receive > a possible exception raised by foo (see my point?). The exception > is there not necessarily for the direct client of the function, but > to the program as whole to signal that an error has occurred. I'm not sure of what programming language you're thinking of, but in the ones I'm aware of, if foo(s) throws an exception, this exception will not be "received" by do_y(). The exception moves up the call stack, and execution stops unless there's an apporpriate catch statement. [...] >> They are not THEMSELVES pre-conditions; > > Yes they are (regardless of the fact that > they can't always express the whole intended > precondition). I think we're getting into philosophy here. It's like me saying "red" is not actually the colour red, but a string whose semantical contents refer to that colour. (IMHO) Eiffel require-clauses aren't themselves pre-conditions, but rather Eiffel's particular implementation of allowing you to encode the pre-conditions of a method into the source code representation of that method. Again, to me it's just a minor philosophical point, so I'm happy to agree to disagree on this one. > > What you say is that, since exceptions have > a known behavior (the program does something), > then if they are expressed in a "contract" then > the contract is not broken. A very confusing way > to look at programs and contracts. In DbC > exceptions are simply the program's behavior > as a response to a detected broken contract > (period). If this is true, then DbC is useless in all cases where the contract-designer explicitly wants exceptions thrown under certain conditions. [snip] > >> The fewer pre-conditions your methods have, the fewer situations in >> which the method will behave in an undefined manner, > > The *useful* work expected from a method is not to raise > an exception. Preconditions are essential to make clear > what is the usable work a method can produce. But raising the exception might be part of the useful work! For example, let's say you're invoking an old library that uses return-codes to signal error conditions. You might write a function which takes as input an error code, and as a result, raises the corresponding exception. void convertToException(int i, Object errInfoObject) { if (i == 1) { throw new DivideByZeroException(); } if (i == 2) { String filename = (String)errInfoObject; throw new FilNotFoundException(filename); } //etc. } You seem to be neglecting that exceptions may be a very useful part of program behaviour, and not just for ensuring the correct of the program. > > Should my car accept sand as fuel? > (It would be a weaker precondition.) If it's possible, the engineers should specify the behaviour of the car when sand is used as fuel. This isn't a very realistic example, but perhaps the principle ideas will be clear anyway. Suppose that you have a car, and there's a pre-condition on it saying that "sand shall not be used as fuel". Now say you are being held hostage by a terrorist, and he tells you at gunpoint to pour sand into the gas tank of the car. You will be relunctant to do this, because you have NO IDEA what will happen if you put sand into the gas tank of the car. Perhaps doing so will magically cause the death of billions of innocent people, including your family and loved one. There really is no information that you can derive from the consequences of pouring sand in there. Perhaps pouring the sand in there will somehow magically cause your own death, in addition to the death of others, in which case you should refuse, since either way you'll die, but by refusing to comply with the terrorist, you'll be saving billions of lives. Now suppose you have a car, and it has a POST condition saying "if sand is used as fuel, this will result in the malfunctioning of the car." Then the terrorist holds a gun to your head and tells you to pour sand in the fuel tank. You will immediately comply, because you know what the results of your actions are, and you consider the car to be less valuable than your life. It's better to have accurate information than no information. > > An "exception" raised by the car as a response > to sand is not useful a behavior out of a car, > it is simply a way to signal a wrong use of it > (in the case of a precondition failure). I claim that there exists situations where this IS useful. > > As a programmer I try to put as much preconditions > if my modules as possible. Many times I choose to > not weaken them, because that makes the implementation > of the module easier (and more efficient), and nothing is > lost since the client can always choose the best module to > do the work required. I wear two hats. One is the "contract-writer", where I balance the interest of both parties: the client, and the implementor. What does the client want out of this contract? What pre-conditions does the implementor want in exchange for implementing this contract? Does the client find this acceptable? Great. Now I switch hats, and am the implementor. I see what pre-conditions I can work with, and what post-conditions I must fufill, and work with them. The problem with "putting as much preconditions as possible to make implementation easy" is that with that, you can do anything (including the impossible). Look, here's a method which solves the halting problem, a problem famous because it is known to be impossible to solve. /* pre-condition: false post-condition: solves halting problem. */ void solveHaltingProblem() { } Writing contracts really has to be a negotation between the two parties, even if the two parties are both you (i.e. you're writing both the implementation of the method, and the client code which will call the method). > What would be profoundly wrong > was not to make perfectly clear the contract, with > a clear separation of obligations and benefits (for > both sides). Agreed. > >> and thus the more robust that method is. > > Our goal should be to make methods correct > and programs reliable (correct+robust). > > The problem of robustness is beyond the scope > of individual methods (it is a systemic property). > It's part of the solution of the problem is simply > to ensure exception in any assertion failure > (simple, isn't it?). The fewer pre-conditions, the more robust that one particular method is. The more robust each individual method in a system, the more robust that system is as a whole, etc. [...] >> >> Preconditions are bad, but specifying pre-conditions are good. > > Preconditions are "bad" for *clients*, and good for *suppliers*. > > Postconditions (and invariants) are good for clients, > and "bad" for suppliers. Yes, sorry, I should have made it clear that I was talking about the client's point of view here. And client-satisfaction is important for the suppliers. Again, look at my halting-problem solver, posted above. This has the best possible pre-condition, and yet it's BAD for the supplier. Why? Because no client will ever use the method. So the effort I spent writing that method was wasted. >> <analogy> >> If I break my girlfriend's expensive vase, that's bad. If I admit to >> her >> that I did it, and apolozie, that's good. >> </analogy> > > Surely breaking the vase was not the precondition! The precondition > would be probably *not* to break the vase (a good thing, if we > don't want her to be in a very bad mood). > > The bad thing here was not to verify the precondition. I'm not saying that "the vase must be broken" is a precondition. I'm saying that preconditions are bad (for the client), just like breaking vases are bad (for the owners of the vase). The above analogy is not about contracts or programming, so pre-conditions aren't within that analogy. They are what the analogy is trying to compare itself too. <meta-analogy> It's like when someone makes an analogy between software and cars, and someone says "but my webbrowser doesn't have tires." Yes, I know it doesn't have tires. It was just an analogy. </meta-analogy> ;) > > The idea of "eliminating" preconditions out of modules > is named defensive programming (and it fails, because > no matter what, not only a car will not be able to use > sand as fuel, but also its clients will be very confused > not being sure if that is the case). Well, to me, the names of things aren't so important as the things themselves, but if you want to call it defensive programming, fine. Using your definitions, I claim that DbC is good, but it's better when used in combination with "defensive programming". - Oliver
From: Miguel Oliveira e Silva on 2 Mar 2006 15:13 Oliver Wong wrote: > "Miguel Oliveira e Silva" <mos(a)det.ua.pt> wrote in message > news:440712A1.1B61BC5C(a)det.ua.pt... > > Oliver Wong wrote: > > > >> /* > >> 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. > >> > >> However, if the filename does not refer to a legal file name; for > >> example if it is equal to "!!*$*!(*", then the behaviour is NOT specified > >> by the contract. > > > > The normal behavior isn't (by definition). But if the method has the > > runnable precondition: file_exists(filename), then an exception > > will be raised and no harm was done to the object's state > > (the routine was not executed). > > I think there was a miscommunication about the pre-condition here. The > string called filename must be a valid filename. Sorry, I assumed that it was a filename referring to an existing readable file. If that was not the idea, perhaps it should have been, or else the name of the function should be: tryToOpenFile. > There may or may not exist > a file with such a name. The pre-condition only asks that the filename is > valid for the given OS (e.g. "*" is not a valid filename in Windows). Ok. > If the filename is valid, but there does not exist such a file, then the > pre-condition is NOT violated. Correct. > Can you re-respond to my previous example with this new understanding in > mind? The inexistence of a readable(/writable?) file attached to "filename" is a very possible normal behavior when handling with files. OpenFile should require the existence of the filename it is supposed to open. Your DbC file module should be something like (Eiffel syntax): class FILE feature valid_name(name: STRING): BOOLEAN require name /= Void exists(filename: STRING): BOOLEAN require filename /= Void; valid_filename(filename) readable(filename: STRING): BOOLEAN require filename /= Void; valid_name(filename); exists(filename) open_to_read(filename: STRING) require filename /= Void; valid_name(filename); exists(filename); readable(filename) ensure opened_to_read read_integer: INTEGER require opened_to_read (...) end -- FILE This way you'll be certain that the clients of this module can (and must) check file existence and readability, before an attempt to open the file. It is true that in file handling there can exist various race conditions due to external noxious concurrent accesses to files (which can happen anytime during file operations). So the precondition "file_exists" will not give 100% assurance that a millisecond latter the file still exists! However, the programmer can (and should) assume that this race conditions are very rare situations, and include them inside the realm of program errors, making them to trigger exceptions (false precondition). You may ask: If such a thing might happen, why not then implement "OpenFile" without all those preconditions? Well, the reason is (again) program correctness. If you expect that, in normal program behavior, exceptions will arise, then to reason and assert about the correctness of the program will be much harder. You'll also be forced to consider goto-like exceptions as normal program behavior. That is a very messy world (perhaps then, it would be useful a meta-exception mechanism to signal errors in the exception world). If a program is logically correct, it is expected that before a call to "OpenFile" somewhere in the (normal) program there was a verification that the file exists and is readable. Such as reading a file should only occur after an OpenFile (and before a CloseFile). Of course it doesn't have to be the way I'm saying, and you are free to express more permissive contracts. In my experience it pays off to leave normal behavior out of exceptions. There is a kids tail about a shepherd who was constantly joking about a coming wolf. The first and second time he did that, everyone believed him, and measures were taken to protect his herd. However, realizing that it was a recurrent joke, they cease to believe in anything he said. One day the wolf really came, and his herd was killed. Exceptions should not be abused. They have a very clear goal: signal incorrect behavior. Programs are much more readable, clean and simple if we keep things that way. (I've seen too many Java/C++ programs in which exceptions are cached just to print an error message, and let the program to go on as if nothing has happened. Clearly the same problem as the shepherd. Isn't it?) > [...] > >> > >> So Daniel, I think, is saying that runnable pre-conditions can always > >> be > >> converted to post-conditions. > > > > That's where he is wrong. You see, in your workaround runnable assertion > > the precondition is correctly putted *before* the routine starts the > > execution > > of its hopefully useful algorithm. If I call "foo" in (C++/Java) without a > > try/catch block the exception will pass by me (without I'm being aware of > > it). > > Hence: > > > > // somewhere in a program > > do_x; > > r = foo(s); > > do_y(r); > > > > before "do_y" the assertion that this code ensures is the postcondition > > of "foo", which -in order for the program not to be incorrect- should > > ensure do_y's precondition. > > It is completely absurd and useless to thing that "do_y" will receive > > a possible exception raised by foo (see my point?). The exception > > is there not necessarily for the direct client of the function, but > > to the program as whole to signal that an error has occurred. > > I'm not sure of what programming language you're thinking of, None. I was showing the absurdity of putting exceptions inside the postcondition contract (argument by absurd). > but in the > ones I'm aware of, if foo(s) throws an exception, this exception will not be > "received" by do_y(). Precisely my point. It is a signal to the program, not necessarly to the direct client of the function. > The exception moves up the call stack, and execution > stops unless there's an apporpriate catch statement. Right. > [...] > > >> They are not THEMSELVES pre-conditions; > > > > Yes they are (regardless of the fact that > > they can't always express the whole intended > > precondition). > > I think we're getting into philosophy here. It's like me saying "red" is > not actually the colour red, but a string whose semantical contents refer to > that colour. > > (IMHO) Eiffel require-clauses aren't themselves pre-conditions, but > rather Eiffel's particular implementation of allowing you to encode the > pre-conditions of a method into the source code representation of that > method. Again, to me it's just a minor philosophical point, so I'm happy to > agree to disagree on this one. If you say that they do implement preconditions, we agree. > > > > What you say is that, since exceptions have > > a known behavior (the program does something), > > then if they are expressed in a "contract" then > > the contract is not broken. A very confusing way > > to look at programs and contracts. In DbC > > exceptions are simply the program's behavior > > as a response to a detected broken contract > > (period). > > If this is true, then DbC is useless in all cases where the > contract-designer explicitly wants exceptions thrown under certain > conditions. (I'm not sure to what you are saying.) DbC simply requires that exceptions be raised everytime a runnable assertion fails (nothing more, nothing less). In DbC the goal is to build programs that behave correctly without exceptions. > [snip] > > > >> The fewer pre-conditions your methods have, the fewer situations in > >> which the method will behave in an undefined manner, > > > > The *useful* work expected from a method is not to raise > > an exception. Preconditions are essential to make clear > > what is the usable work a method can produce. > > But raising the exception might be part of the useful work! For example, > let's say you're invoking an old library that uses return-codes to signal > error conditions. Well if you use modules that are not based in DbC, then yes all kind of dirt things can happen (as C's "errno" and error code as return values). If possible, wrap them inside appropriate DbC based modules, and use them only through those modules (to protect the remaining program from undetected propagation of errors). > You might write a function which takes as input an error code, and as a > result, raises the corresponding exception. > > void convertToException(int i, Object errInfoObject) { > if (i == 1) { > throw new DivideByZeroException(); > } > if (i == 2) { > String filename = (String)errInfoObject; > throw new FilNotFoundException(filename); > } > //etc. > } > You seem to be neglecting that exceptions may be a very useful part of > program behaviour, and not just for ensuring the correct of the program. I am not neglecting it. I am saying that they should never be used for normal correct program behavior (that's DbC philosophy). > > > > Should my car accept sand as fuel? > > (It would be a weaker precondition.) > > If it's possible, the engineers should specify the behaviour of the car > when sand is used as fuel. No. The engineers simply put in the contract that the car has a precondition of fuel = gasoline (diesel, GPL, hydrogen, electricity, or whatever). No need to worry about absurd car usages (or else, they would have to write an endless and mostly useless contract). > This isn't a very realistic example, but perhaps > the principle ideas will be clear anyway. > > Suppose that you have a car, and there's a pre-condition on it saying > that "sand shall not be used as fuel". Preconditions assert what the module expects and requires. Not what the module does not expect or requires (or else, it would be a very, very long list of conditions). > Now say you are being held hostage by > a terrorist, and he tells you at gunpoint to pour sand into the gas tank of > the car. You will be relunctant to do this, because you have NO IDEA what > will happen if you put sand into the gas tank of the car. Perhaps doing so > will magically cause the death of billions of innocent people, including > your family and loved one. There really is no information that you can > derive from the consequences of pouring sand in there. If the car has a runnable precondition (whatever that might be in a car!) of (fuel = gasoline), nothing will happen, if the car behavior is not fault tolerant it will simply cease to work. > Perhaps pouring the > sand in there will somehow magically cause your own death, in addition to > the death of others, in which case you should refuse, since either way > you'll die, but by refusing to comply with the terrorist, you'll be saving > billions of lives. > > Now suppose you have a car, and it has a POST condition saying "if sand > is used as fuel, this will result in the malfunctioning of the car." Then > the terrorist holds a gun to your head and tells you to pour sand in the > fuel tank. You will immediately comply, because you know what the results of > your actions are, and you consider the car to be less valuable than your > life. It is not necessary to put it in the postcondition. If the precondition if (fuel = gasoline), that is enough to ensure that nothing bad will happen (if the car software were allowed to control car behavior, it wouldn't even start, preventing an attempt to burn sand). > It's better to have accurate information than no information. Preconditions are accurate information. > > > > An "exception" raised by the car as a response > > to sand is not useful a behavior out of a car, > > it is simply a way to signal a wrong use of it > > (in the case of a precondition failure). > > I claim that there exists situations where this IS useful. (Aren't you taking this absurd example a little too far away?) > > > > As a programmer I try to put as much preconditions > > if my modules as possible. Many times I choose to > > not weaken them, because that makes the implementation > > of the module easier (and more efficient), and nothing is > > lost since the client can always choose the best module to > > do the work required. > > I wear two hats. One is the "contract-writer", where I balance the > interest of both parties: the client, and the implementor. What does the > client want out of this contract? What pre-conditions does the implementor > want in exchange for implementing this contract? Does the client find this > acceptable? Great. > > Now I switch hats, and am the implementor. I see what pre-conditions I > can work with, and what post-conditions I must fufill, and work with them. > > The problem with "putting as much preconditions as possible to make > implementation easy" is that with that, you can do anything (including the > impossible). That's absurd. Preconditions don't *do* anything. They simply assert the client's obligations when using the routine. > Look, here's a method which solves the halting problem, a > problem famous because it is known to be impossible to solve. > > /* > pre-condition: false > post-condition: solves halting problem. > */ > void solveHaltingProblem() { > } Simply because you wrote in the postcondition (and in the function name) that the function solves the halting problem does not mean that it does. That function is, by definition, unusable. It can't do nothing (you can call "nothing" the halting problem, black coffee, a nuclear plant; it makes absolutely no difference). Any program that attempts to use solveHaltingProblem is simply incorrect (incorrect is not equal to solve the halting problem). > Writing contracts really has to be a negotation between the two parties, > even if the two parties are both you Correct. > (i.e. you're writing both the > implementation of the method, and the client code which will call the > method). > > > What would be profoundly wrong > > was not to make perfectly clear the contract, with > > a clear separation of obligations and benefits (for > > both sides). > > Agreed. > > > > >> and thus the more robust that method is. > > > > Our goal should be to make methods correct > > and programs reliable (correct+robust). > > > > The problem of robustness is beyond the scope > > of individual methods (it is a systemic property). > > It's part of the solution of the problem is simply > > to ensure exception in any assertion failure > > (simple, isn't it?). > > The fewer pre-conditions, the more robust that one particular method is. At the expense of a less robust program/system. (Robustness is a systemic property.) > The more robust each individual method in a system, the more robust that > system is as a whole, etc. Correct. But robustness is achieved by more preconditions/postcondition/invariants, not less. > [...] > > >> > >> Preconditions are bad, but specifying pre-conditions are good. > > > > Preconditions are "bad" for *clients*, and good for *suppliers*. > > > > Postconditions (and invariants) are good for clients, > > and "bad" for suppliers. > > Yes, sorry, I should have made it clear that I was talking about the > client's point of view here. And client-satisfaction is important for the > suppliers. (Absolutely essential.) > Again, look at my halting-problem solver, posted above. This has the > best possible pre-condition, and yet it's BAD for the supplier. Not at all. In fact, it is the dream of a (lazy) supplier (whatever he does or does not, the contract is not broken due to his responsibility). Don't forget that preconditions *precede* postconditions. That's why there is no paradox there. > Why? Because > no client will ever use the method. So the effort I spent writing that > method was wasted. (Well, it was your method, not mine ;), so I don't think that's my problem. You could have done nothing, and still you would have keep your part of the contract.) > >> <analogy> > >> If I break my girlfriend's expensive vase, that's bad. If I admit to > >> her > >> that I did it, and apolozie, that's good. > >> </analogy> > > > > Surely breaking the vase was not the precondition! The precondition > > would be probably *not* to break the vase (a good thing, if we > > don't want her to be in a very bad mood). > > > > The bad thing here was not to verify the precondition. > > I'm not saying that "the vase must be broken" is a precondition. I'm > saying that preconditions are bad (for the client), But they aren't (In case you haven't noted I wrote "bad", not bad). Preconditions are good also to the clients, because they won't be fooled by expecting impossible things out of routines. > just like breaking vases > are bad (for the owners of the vase). No, it is not "just like". Breaking vases is definitely bad (program error). But the problem was not the precondition (it's the way things work). > The above analogy is not about > contracts or programming, so pre-conditions aren't within that analogy. They > are what the analogy is trying to compare itself too. (If you look more carefully you will see many preconditions hanging there, and everywhere [like in a "nightmare": there is no escape, no matter how deeper we put our head inside the sand]). > <meta-analogy> > It's like when someone makes an analogy between software and cars, and > someone says "but my webbrowser doesn't have tires." > Yes, I know it doesn't have tires. It was just an analogy. > </meta-analogy> > ;) ;) > > > > The idea of "eliminating" preconditions out of modules > > is named defensive programming (and it fails, because > > no matter what, not only a car will not be able to use > > sand as fuel, but also its clients will be very confused > > not being sure if that is the case). > > Well, to me, the names of things aren't so important as the things > themselves, How can you be sure about things themseves, when communicating with someone else, if names have fuzzy definitions? Names are essential for a noise-free communication. Hence to be able to understand, to teach, and to learn. Preconditions are conditions imposed *before* something happens, and postconditions *after* that thing has happened. If the thing did not happen due to a precondition failure, it is absurd to express that as a postcondition. If a client wants to know his part of the contract, he need only to look at the preconditions. Postcondition are the supplier's part of the contract. To hide preconditions inside postconditions is asking for trouble. > but if you want to call it defensive programming, fine. Using > your definitions, I claim that DbC is good, but it's better when used in > combination with "defensive programming". They are opposing methodologies (it will be hard to combine them). (They prescribe opposing medicines.) Defensive programming is a good methodology to handle conditions that are completely outside the program's responsibility, such as when communicating with the external world. > - Oliver Best regards, -miguel -- Miguel Oliveira e Silva
From: Dmitry A. Kazakov on 2 Mar 2006 15:27 On Thu, 02 Mar 2006 15:43:30 +0000, Miguel Oliveira e Silva wrote: > That's where he is wrong. You see, in your workaround runnable assertion > the precondition is correctly putted *before* the routine starts the execution > of its hopefully useful algorithm. If I call "foo" in (C++/Java) without a > try/catch block the exception will pass by me (without I'm being aware of it). > Hence: > > // somewhere in a program > do_x; > r = foo(s); > do_y(r); > > before "do_y" the assertion that this code ensures is the postcondition > of "foo", which -in order for the program not to be incorrect- should > ensure do_y's precondition. Not quite. r = foo(s) ensures a conjuction of preconditions. One part is the precondition of do_y, another is a conjuction of the preconditions of all enclosing catch-blocks. Consider: try { do_x; r = foo(s); post: r /= null V NullStringException = P1 pre: r /= null = P2 do_y(r); post ... } post: ... V NullStringException catch (NullStringException) pre: NullStringException = P3 { ... } P1 => P2 V P3. It's fine. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de
From: Oliver Wong on 2 Mar 2006 16:56 "Miguel Oliveira e Silva" <mos(a)det.ua.pt> wrote in message news:440751F4.9EF8D419(a)det.ua.pt... > Oliver Wong wrote: > >> >> If this is true, then DbC is useless in all cases where the >> contract-designer explicitly wants exceptions thrown under certain >> conditions. > > (I'm not sure to what you are saying.) > > DbC simply requires that exceptions be raised everytime > a runnable assertion fails (nothing more, nothing less). > > In DbC the goal is to build programs that behave > correctly without exceptions. What I was saying is that somethings the behaviour of a correct program is to throw exceptions. If DbC forbids throwing exceptions, then DbC cannot support programs whose purpose is to throw an exception. [...] >> But raising the exception might be part of the useful work! For >> example, >> let's say you're invoking an old library that uses return-codes to signal >> error conditions. > > Well if you use modules that are not based in DbC, then > yes all kind of dirt things can happen (as C's "errno" > and error code as return values). > > If possible, wrap them inside appropriate DbC based modules, > and use them only through those modules (to protect the > remaining program from undetected propagation of errors). Yes, but those "wrapper" modules will not actually be fully-DbC compliant, under your definition, because of the "dirt" leaking in. This is why I'm saying there exists a lot of practical situation where DbC as you define it cannot be used, and a more relaxed-version of DbC is nescessary. (e.g. one where exceptions are allowed as part of the normal behaviour of the module). >> > >> > Should my car accept sand as fuel? >> > (It would be a weaker precondition.) >> >> If it's possible, the engineers should specify the behaviour of the >> car >> when sand is used as fuel. > > No. The engineers simply put in the contract that > the car has a precondition of fuel = gasoline (diesel, > GPL, hydrogen, electricity, or whatever). No need to > worry about absurd car usages (or else, they would have > to write an endless and mostly useless contract). [...] > >> It's better to have accurate information than no information. > > Preconditions are accurate information. Yes, but the equivalent post-condition gives MORE information. If you have a pre-condition "A", then the information you have is "If A is false, I don't know what happens." If you have a post-condition "if A is false, car fails", then you now have more information than before, and that's better. That is why I say that if the engineers could have put the "no sand" pre-condition without any trouble on their part, they should have. The problem with the "no sand" precondition is that if, some day in the future, they invent a way to allow cars to run on sand, then they cannot update the underlying implementation of their car to take advantage of this new feature without changing the interface (and the contract). But if the engineers are confident that they can guarantee that a car will ALWAYS fail to run when given sand as fuel, AND/OR the client specifically wishes for the behaviour of the car to be it failing when given fuel, then this behaviour should be specified as a post-condition. [...] >> >> The problem with "putting as much preconditions as possible to make >> implementation easy" is that with that, you can do anything (including >> the >> impossible). > > That's absurd. Preconditions don't *do* anything. They simply > assert the client's obligations when using the routine. I didn't say the pre-conditions "do" something; I'm saying that with enough pre-conditions, you (the implementor) are free to do anything. > >> Look, here's a method which solves the halting problem, a >> problem famous because it is known to be impossible to solve. >> >> /* >> pre-condition: false >> post-condition: solves halting problem. >> */ >> void solveHaltingProblem() { >> } > > Simply because you wrote in the postcondition > (and in the function name) that the function > solves the halting problem does not mean that > it does. Yes, but in this case, the function DOES solve the halting problem as long as the pre-conditions are met. > > That function is, by definition, unusable. > It can't do nothing (you can call "nothing" > the halting problem, black coffee, a nuclear > plant; it makes absolutely no difference). I'm assuming you mean "It can't do anything" or "it can do nothing". Well, no, actually, what it does is solves the halting problem precisely when the pre-conditions are true (which incidentally in this case happens to be "never", but that's besides the point). Just because something is "unusable" doesn't mean it "does nothing". Consider this alternate implementation of the same contract: /* pre-condition: false post-condition: solves halting problem. */ void solveHaltingProblem() { print "Hello world!"; } Now if you ignore the documentation and just look at the implementation (glass-box analysis), even a beginner in programming will tell you that the function does *something*. > > Any program that attempts to use > solveHaltingProblem is simply > incorrect (incorrect is not equal > to solve the halting problem). I'm not claiming that "incorrect" is equivalent to solving the halting problem. Here's a different contract, for example: /* pre-condition: false post-condition: prints "Hello World" to standard out. */ void printHelloWorld() { print "Goodbye."; } This method also satisfies its part of the contract, and the contract says that whenever the pre-conditions are satisfied, it must print "Hello World" to standard out. The implementation of this function is not incorrect; I claim it is completely free of bugs. It does exactly what it is intended to do! The exact same thing is happening with my "solve halting problem" function above. It does exactly what it intends to do, which is to solve the halting problem, given certain assumptions and pre-conditions. > >> The more robust each individual method in a system, the more robust that >> system is as a whole, etc. > > Correct. > > But robustness is achieved by more preconditions/postcondition/invariants, > not less. No, pre-conditions are assumptions the implementor can make when implementing the method. The fewer assumptions the implementor makes, the more robust the implementation. Therefore, the LESS pre-conditions, the more robust the program. If you have lots of pre-conditions, it's easier to make a change somewhere in the program which inadvertently violates one of those pre-conditions, thus rendering the program as a whole incorrect. If you have ZERO pre-conditions (a noble theoretical goal, but almost impossible to obtain in practice), then it doesn't matter what changes you make, elsewhere, you will NEVER break the module which has zero pre-conditions, because it does not depend on any outside assumptions. >> Again, look at my halting-problem solver, posted above. This has the >> best possible pre-condition, and yet it's BAD for the supplier. > > Not at all. In fact, it is the dream of a (lazy) supplier > (whatever he does or does not, the contract is not broken > due to his responsibility). It's bad for the supplier because he cannot get any clients to use it. What's the point of creating a method if it will never be used? It's wasted effort on the supplier's part. But I guess it's useless to proceed further with this until we define metrics for "good" and "bad" for the supplier and client. It is GOOD for the client if he can find an implementation of a contract which solves his problem without imposing too many pre-conditions. It is GOOD for the supplier if he can find a client who will agree to a contract with the supplier. I say the "halting problem" contract is bad for the supplier, because he will not be able to find any clients who will agree to that contract. >> >> Well, to me, the names of things aren't so important as the things >> themselves, > > How can you be sure about things themseves, when > communicating with someone else, if names have > fuzzy definitions? I've long ago learned that every person has their own "version" of English, where words mean slightly different things to them. For example, for some people, the word "clown" carries a connotation of fear, while it doesn't for others. English isn't as rigorous as one would like, so you will often find people disagreeing about the meanings or definitions of terms. That's fine. I don't try to enforce my definitions on other people. When it's not too much trouble for me, I try to use the same definitions as the person I'm speaking to, but when I'm concurrently speaking to multiple people, it occasionally becomes difficult to keep track of what terms mean what to different people. It's a minor problem, and in practice an insoluble one, so I just tolerate it. As for "How can you be sure about things themselves", I reply with the philosophical "How can you be sure of anything?" > Preconditions are conditions imposed *before* something > happens, and postconditions *after* that thing has happened. > If the thing did not happen due to a precondition > failure, it is absurd to express that as a postcondition. > If a client wants to know his part of the contract, > he need only to look at the preconditions. Postcondition > are the supplier's part of the contract. To hide preconditions > inside postconditions is asking for trouble. 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. - Oliver
From: ggroups on 3 Mar 2006 06:16
Oliver Wong wrote: [ Design By Contract saga snipped, but acknowledged ] > It's bad for the supplier because he cannot get any clients to use it. > What's the point of creating a method if it will never be used? It's wasted > effort on the supplier's part. > But I guess it's useless to proceed further with this until we define > metrics for "good" and "bad" for the supplier and client. > It is GOOD for the client if he can find an implementation of a contract > which solves his problem without imposing too many pre-conditions. > It is GOOD for the supplier if he can find a client who will agree to a > contract with the supplier. The best components from the users' viewpoint are those that have the weakest preconditions and strongest post/invariant conditions. The suppliers' viewpoint is the converse. Within those extremes we have a dynamic where components arrive at contracts that are amenable to both (usage, implementation effort etc) .. Hence the term *contract* . Contracts are negotiated are they not ... Regards, Steven Perryman |