Before writing the function, I'd enlist all the conditions that must be met for a contract to be valid. Something along the lines of:
1. There must be a valid offer made by an offerer; 2. There must be an unconditional, voluntary acceptance of the offer; 3. The offerer and accepter must be above the age of minority; 4. There must be a valid consideration (quid pro quo).
Though there are many more conditions, for the sake of simplicity, let us stick to only the above four conditions.
Going from the above four, I'd translate them into code precondition checks as follows:
These four would be the basis of unit tests after writing code.
In the implementation, I'd use Design By Contract (DBC) style assertion:
if (objOfferer == null) return false;
if (SomeOtherCondition Not Met) return false;
Or I might use the straight-forward-one-return-path style design like,
if (objOfferer) if (SomeOtherCondition Met) return true;
After writing the implementation of the above function, I'd write unit test scripts to test each of the above conditions. These would be seperate functions that would call the function IsValidContract with invalid and valid values against each of the above four conditions we outlined.
Now, let me in on some terminology. Which of the above are test cases and what is the unit tests here?
Water Cooler v2 27 February 2006 17:47:22 [ permanent link ]
Thanks for your diligent reply, Laurent. It has been very helpful and interesting.
I think I follow you and believe that my understanding has been correct so far. What still intrigues me is:
1. What then, are pre-conditions?
My initial assessment showed they were conditions to test inside the function's implementation, before the function returned valid input. If they're not that, what are they?
2. I understood the unit-test script you wrote called testIsValidContract. You tested it for one condition/validation, and as it concurs with my understanding, you imply that it must extend for all such validations. Fine. But what about test-cases. You said, they are a specification of what input the function gets and what it spits out. My question precisely is, "what is the format in which they are written?" Could you please produce a list of test-cases for this example we've taken? That would give me an outline.
In article <1141051642.754991.221520@u72g2000cwu.googlegroups.com>, "Water Cooler v2" <wtr_clr@yahoo.com> wrote:
Thanks for your diligent reply, Laurent. It has been very helpful and> interesting.>
I think I follow you and believe that my understanding has been correct> so far. What still intrigues me is:>
1. What then, are pre-conditions?>
My initial assessment showed they were conditions to test inside the> function's implementation, before the function returned valid input. If> they're not that, what are they?
A pre-condition is something about the input such that, if the condition doesn't hold, the function's result is undefined. In other words, if the precondition doesn't hold, the function has no way of knowing what to return. There may be no preconditions in your function.
To put it another way, preconditions are conditions that your function doesn't have to test, because they must hold *before* the function can be called.
2. I understood the unit-test script you wrote called> testIsValidContract. You tested it for one condition/validation, and as> it concurs with my understanding, you imply that it must extend for all> such validations. Fine. But what about test-cases. You said, they are a> specification of what input the function gets and what it spits out. My> question precisely is, "what is the format in which they are written?"> Could you please produce a list of test-cases for this example we've> taken? That would give me an outline.
The format for the unit tests depends on the language and test harness used, but they are written *outside* the function. A test case should create some input, call the function with that input, then test the output to see if it is correct.
The tests can also be used by implementers of code that use your function as examples of proper use.
-- Magic depends on tradition and belief. It does not welcome observation, nor does it profit by experiment. On the other hand, science is based on experience; it is open to correction by observation and experiment.
Laurent Bossavit 27 February 2006 18:51:17 [ permanent link ]
My initial assessment showed they were conditions to test inside the> function's implementation, before the function returned valid input.
That's right, as long as you're careful with the implications of that definition. Preconditions are conditions that must prevail for the function to have a meaningful result. If preconditions are not met upon entry, that means someone is using the function wrongly. In other words, someone wrote a bug, and the bug isn't inside the function.
A major benefit of DbC in my opinion is that it makes it easier, in just that way, to reason about "where the bug is". A precondition violation implies a bug in the caller; a postcondition violation imples a bug in the function called.
Thus, objOfferer != null is a precondition. It is incorrect to call the function with a null offerer.
However, objTermsAndConditions.Consideration.Value > 0 cannot be a precondition, since you *expect* that the function will sometimes be called with terms that do not comply with this criterion. The point of writing the function is that it should tell you when that is the case, by returning FALSE.
But what about test-cases. You said, they are a> specification of what input the function gets and what it spits out. My> question precisely is, "what is the format in which they are written?"
It depends.
I'm a practitioner of Test-Driven Development. I write down test cases in the format of unit tests.
So, the testIsValidContract function I sketched is both things: - a test case (it specifies inputs and outputs) - an executable unit test
You can also write down test cases in some other format. On paper, or on the whiteboard (I do that on occasion), in Excel tables... The precise format depends on what it is you're coding.
Oliver Wong 27 February 2006 20:09:04 [ permanent link ]
"Water Cooler v2" <wtr_clr@yahoo.com> wrote in message news:1141051642.754991.221520@u72g2000cwu.googlegroups.com...>
1. What then, are pre-conditions?>
My initial assessment showed they were conditions to test inside the> function's implementation, before the function returned valid input. If> they're not that, what are they?
There was an argument about this earlier in the newsgroup, so I'm going to qualify my answer with "this is my opinion; not everyone agrees with it":
A pre-condition is a condition which must be true for your function to behave correctly. If the pre-condition is not true, then there is no guarantee as to how the function will behave.
When you're implementing a function, the pre-conditions are things which you can assume to be true. When calling a function, the pre-conditions are things which you must ensure are true in order for the function you're calling to actually be useful.
Here's an example with trivial (perhaps satirical) pre-conditions:
/* Pre-conditions: (*) God will not change the laws of logic. (*) a gamma ray will not strike the computer, flipping bits randomly. (*) the compiler will not mess up the translation of the function into machine code. */ public boolean getFalse() { return false; }
Here's a somewhat more practical example:
/* Pre-conditions: (*) x is not zero */ public double getMultiplicativeInverse(double x) { return 1.0 / x; }
Miguel Oliveira e Silva 27 February 2006 22:20:55 [ permanent link ]
Water Cooler v2 wrote:
Thanks for your diligent reply, Laurent. It has been very helpful and> interesting.>
I think I follow you and believe that my understanding has been correct> so far. What still intrigues me is:>
1. What then, are pre-conditions?>
My initial assessment showed they were conditions to test inside the> function's implementation, before the function returned valid input. If> they're not that, what are they?
They are the client's part of the routine's contract.
It is up to the client's to ensure a routine precondition *before* a call is attempted (however, a wise supplier should protect itself from erroneous preconditions, as explained below).
In return, the client will be ensured *after* its execution - if the routine is correct - that the routine's postconditions will hold.
Another very important assertion in the OO world are class invariants. Invariants are conditions required to hold immediately before and immediately after any use of an object (those periods are named: object stable times).
Together these three assertions, if used correctly, allow the programmer to express the (approximate) semantics of the object's Abstract Data Type.
Ideally all those assertions should be ensured statically (at compile time), situation in which the program would be correct (regarding those assertions).
However, such ideal and desirable goal is not possible for most practical programs. One is then faced to what to do when there is the possibility (hopefully, very remote) of a run-time assertion failure.
We can identify four possible approaches to this problematic run-time situation:
1. Ostrich approach: Ignore the problem.
2. Defensive programming approach: convert the problem to the normal program control flow (if's).
3. "All-or-nothing" approach: decorate the program with C like "asserts" in the place of runnable assertions, ensuring that either the program meets all of its assertions at run-time, or else terminates with an appropriate error message.
4. DbC approach: make all those assertions part of the programming language and relate them with the exception mechanism. A failure of an assertion raises an exception.
Only the last two approaches are appropriate to properly handle run-time contracts.
BTW, it makes no practical sense to discuss what postcondition should a routine "ensure" when a precondition fails. When a precondition fails we are in the presence of a program error, of the responsibility of the caller. The routine should never be executed in those cases.
Nevertheless, the routine *implementation* should always assume the the precondition holds (and, after the routine execution, the caller's code should always assume the routine's postcondition holds).
Class assertions are not part of the class implementation, they are part of its interface (and it is there that they should be tested, whenever it is possible and there is no static guarantee that they are always verified).
(Being part of the class interface they are required to be properly inherited by descendant classes: a very difficult behavior to ensure in C++ or in standard Java).
When a run-time assertion fails, the (wise) programmer(s) should only take one of two paths:
1. The program error should be identified and corrected to ensure its inexistence next time the program is to be executed ("all-or-nothing", or DbC approaches);
2. In critical applications, an appropriate safe fault tolerant technique should be devised and implemented within the program itself (DbC approach).
In article <44035117.A32AC86E@det.ua.pt>, Miguel Oliveira e Silva <mos@det.ua.pt> wrote:
Water Cooler v2 wrote:>
Thanks for your diligent reply, Laurent. It has been very helpful and> > interesting.> >
I think I follow you and believe that my understanding has been correct> > so far. What still intrigues me is:> >
1. What then, are pre-conditions?> >
My initial assessment showed they were conditions to test inside the> > function's implementation, before the function returned valid input. If> > they're not that, what are they?>
They are the client's part of the routine's contract.>
It is up to the client's to ensure a routine precondition> *before* a call is attempted (however, a wise supplier> should protect itself from erroneous preconditions, as> explained below).
A nice write up except for this one problem. If the supplier is able to protect itself from erroneous inputs, then it can define proper outputs for those inputs, thus making them no longer erroneous.
Someone else's example:
double inverse( double x ) { return 1/x; }
The precondition of course is that 'x != 0' however, thats an easy thing to check, and as such one can define a particular output for that condition:
// returns NaN if x == 0 double inverse( double x ) { return 1/x; }
On the other hand, some functions have preconditions that can't be checked. For example C++'s std::copy takes two input iterators that must be in a range (ie incrementing the first iterator must eventually make it equal to the second.) This cannot be checked from inside the routine, even theoretically. Now that's a *real* precondition.
In return, the client will be ensured *after* its> execution - if the routine is correct - that> the routine's postconditions will hold.>
Another very important assertion in the OO world> are class invariants. Invariants are conditions required> to hold immediately before and immediately after any> use of an object (those periods are named: object> stable times).>
Together these three assertions, if used correctly,> allow the programmer to express the (approximate)> semantics of the object's Abstract Data Type.>
Ideally all those assertions should be ensured> statically (at compile time), situation in which> the program would be correct (regarding> those assertions).>
However, such ideal and desirable goal is not> possible for most practical programs.> One is then faced to what to do when there> is the possibility (hopefully, very remote)> of a run-time assertion failure.>
We can identify four possible approaches to this> problematic run-time situation:>
1. Ostrich approach: Ignore the problem.>
2. Defensive programming approach: convert the> problem to the normal program control flow (if's).>
3. "All-or-nothing" approach: decorate the program> with C like "asserts" in the place of runnable assertions,> ensuring that either the program meets all of its> assertions at run-time, or else terminates with> an appropriate error message.>
4. DbC approach: make all those assertions part> of the programming language and relate them> with the exception mechanism. A failure of> an assertion raises an exception.>
Only the last two approaches are appropriate> to properly handle run-time contracts.
For real preconditions, none of the above approaches will work. You can't ignore the problem, and you can't write code to protect yourself from it.
-- Magic depends on tradition and belief. It does not welcome observation, nor does it profit by experiment. On the other hand, science is based on experience; it is open to correction by observation and experiment.
Oliver Wong 28 February 2006 17:34:13 [ permanent link ]
"Daniel T." <postmaster@earthlink.net> wrote in message newsostmaster-B27730.19331527022006@news.east.earthlink.net...> In article <44035117.A32AC86E@det.ua.pt>,> Miguel Oliveira e Silva <mos@det.ua.pt> wrote:>
Water Cooler v2 wrote:>>
Thanks for your diligent reply, Laurent. It has been very helpful and>> > interesting.>> >
I think I follow you and believe that my understanding has been correct>> > so far. What still intrigues me is:>> >
1. What then, are pre-conditions?>> >
My initial assessment showed they were conditions to test inside the>> > function's implementation, before the function returned valid input. If>> > they're not that, what are they?>>
They are the client's part of the routine's contract.>>
It is up to the client's to ensure a routine precondition>> *before* a call is attempted (however, a wise supplier>> should protect itself from erroneous preconditions, as>> explained below).>
A nice write up except for this one problem. If the supplier is able to> protect itself from erroneous inputs, then it can define proper outputs> for those inputs, thus making them no longer erroneous.
Right, except keep in mind that sometimes the person designing the contract and the person implementing the code are two different people. That latter person may not have the authority to change the contract.
One reason you might not want to make certain inputs no longer erroneous is to give you flexibility to change your implementation later on.
Someone else's example:>
double inverse( double x ) {> return 1/x;> }>
The precondition of course is that 'x != 0' however, thats an easy thing> to check, and as such one can define a particular output for that> condition:>
// returns NaN if x == 0> double inverse( double x ) {> return 1/x;> }
In another thread, someone mentioned that in some situations (e.g. games) it might be worth treating 0 instead as a tiny positive non-zero value; so the implementation might become:
double inverse(double x) { if (x == 0) { x = 0.000001; } return 1/x; }
By not prematurely tightening your contract, you leave yourself open to make changes like these, whereas if you enforced the behaviour that if x = 0, NaN is returned, then you wouldn't have this freedom.
Of course, if the desired behaviour is to have NaN returned, then by all means, specify it in the contract! But don't make the contract overly restrictive if the calling client doesn't care about those corner cases, or can ensure that those corner cases will never happen, or else you're just making the implementor's job more difficult for no reason.
On the other hand, some functions have preconditions that can't be> checked. For example C++'s std::copy takes two input iterators that must> be in a range (ie incrementing the first iterator must eventually make> it equal to the second.) This cannot be checked from inside the routine,> even theoretically. Now that's a *real* precondition.>
Agreed.
[snip: various approaches to dealing with pre-conditions which do not hold]
For real preconditions, none of the above approaches will work. You> can't ignore the problem, and you can't write code to protect yourself> from it.
I'd phrase it as "you (the implementor) are forced to ignore the problem", while the calling client is forced to cross their fingers and hope the problem never comes up. This is often the case when there are uncomputable pre-conditions on the input, but the input comes from the user via the command-line or a GUI or something similar, e.g.:
/* pre-condition: String describes valid C source code for a program that eventually halts. */ String void determineOutput(String programSourceCodeInC) { /*Implementation of a C interpreter goes here*/ }
-- > Magic depends on tradition and belief. It does not welcome observation,> nor does it profit by experiment. On the other hand, science is based> on experience; it is open to correction by observation and experiment.
OT, but I thought that magic is a lot closer to science than to religion. That is, if you do something using magic (drink a potion, chant an incantation, wave a wand, etc.) exactly the same way, you will get exactly the same results.
If you do something using religion (pray, sacrifice an animal, etc.) exactly the same way, you will get different results, depending on the moods of the deities involved.
That is, science is all about independently repeatable experiments, and if magic were real, you could perform independently repeatable experiments in magic as well.
As a magician, you may or may not want to share the results of your magical experiments with others, but that would be a characteristic of each particular magician, and not of magic as a whole (similarly, some scientists don't share their results, and some do).
Miguel Oliveira e Silva 28 February 2006 19:38:41 [ permanent link ]
"Daniel T." wrote:
In article <44035117.A32AC86E@det.ua.pt>,> Miguel Oliveira e Silva <mos@det.ua.pt> wrote:>
Water Cooler v2 wrote:> >
Thanks for your diligent reply, Laurent. It has been very helpful and> > > interesting.> > >
I think I follow you and believe that my understanding has been correct> > > so far. What still intrigues me is:> > >
1. What then, are pre-conditions?> > >
My initial assessment showed they were conditions to test inside the> > > function's implementation, before the function returned valid input. If> > > they're not that, what are they?> >
They are the client's part of the routine's contract.> >
It is up to the client's to ensure a routine precondition> > *before* a call is attempted (however, a wise supplier> > should protect itself from erroneous preconditions, as> > explained below).>
A nice write up except for this one problem. If the supplier is able to> protect itself from erroneous inputs, then it can define proper outputs> for those inputs, thus making them no longer erroneous.
That is not correct.
Most electronic circuits - for example the components used to build computers - protected themselves against incorrect connections (through appropriate interface physical shapes) and even against out of bound voltage signals (through appropriate protecting circuits). That protection is *not part* of the normal component behavior, neither is part of the component's contract to the outside world: it is simply a way for the component to protect itself against *incorrect* uses (thus increasing its fault tolerance and life span). Nothing - other than malfunction - is expected from an electronic circuit when it is incorrectly used (thus one cannot "define proper outputs for those inputs" as you said). Nevertheless, a wise electronic engineer (eager to keep its job) protects, as much as reasonable, its circuits against incorrect external uses.
Same thing with assertions.
It seems to me, that you are incorrectly mixing the world of normal program behavior, and the world of exceptions and exceptional behavior. Contracts aim at correctness in the normal program behavior. Exceptions exist as a mean to deal with (detectable) incorrect programs (in DbC it is as simply as this).
Someone else's example:>
double inverse( double x ) {> return 1/x;> }>
The precondition of course is that 'x != 0'
It should be part of the function interface as in Eiffel:
inverse(x: DOUBLE): DOUBLE is require -- precondition x /= 0 do Result := 1/x ensure -- postcondition (Result * x - 1).abs <= Maximum_acceptable_near_zero_value end;
however, thats an easy thing> to check,
Indeed it is.
and as such one can define a particular output for that> condition:>
// returns NaN if x == 0> double inverse( double x ) {> return 1/x;> }
You surely can, but you surely shouldn't (or else you will be doing defensive programming and not DbC).
That said, of course the programmer is free to establish the contracts we wants to its classes and routines.
Of course, in this particular (mathematical) case it would not be wise to remove the precondition (and extend the postcondition with a NaN result value when x equals zero).
I don't see the interest of dividing anything by zero. There are some very important mathematical and engineering uses for "extracting" square roots out of negative values, but none to my knowledge out of x/0.
99.99...99% of 1/0 uses are programming errors (and should be treated as such).
On the other hand, some functions have preconditions that can't be> checked.
Yes, that situation can occur (the world is not perfect). Manytimes times we can only make a practical runnable approximation of the required precondition.
Why do you think that, somehow, this practical limitation contradicts anything that I have written (the "all-or-nothing" and the DbC's exceptional behavior was clearly identified as applying to *runnable* assertions)?
Are you, by any chance, defending that a program should not have runnable preconditions?
If you are, then that is not DbC: it is defensive programming.
For example C++'s std::copy takes two input iterators that must> be in a range (ie incrementing the first iterator must eventually make> it equal to the second.) This cannot be checked from inside the routine,> even theoretically.
So?
Are you saying that we should not use iterators unwisely or in critical code? If you are, I agree (at least the C++'s iterators you refer).
Of course there can exist situations in which we cannot express runnable assertions. However, as in the example you give, many times the programmer is free to choose the library that better fills its needs (why should he use a blind copy of unsafe iterators?).
Now that's a *real* precondition.
What do you mean by "real"?
(Surely you are not stating that runnable preconditions are not real!)
A better name would be hard or difficult.
In return, the client will be ensured *after* its> > execution - if the routine is correct - that> > the routine's postconditions will hold.> >
Another very important assertion in the OO world> > are class invariants. Invariants are conditions required> > to hold immediately before and immediately after any> > use of an object (those periods are named: object> > stable times).> >
Together these three assertions, if used correctly,> > allow the programmer to express the (approximate)> > semantics of the object's Abstract Data Type.> >
Ideally all those assertions should be ensured> > statically (at compile time), situation in which> > the program would be correct (regarding> > those assertions).> >
However, such ideal and desirable goal is not> > possible for most practical programs.> > One is then faced to what to do when there> > is the possibility (hopefully, very remote)> > of a run-time assertion failure.> >
We can identify four possible approaches to this> > problematic run-time situation:> >
1. Ostrich approach: Ignore the problem.> >
2. Defensive programming approach: convert the> > problem to the normal program control flow (if's).> >
3. "All-or-nothing" approach: decorate the program> > with C like "asserts" in the place of runnable assertions,> > ensuring that either the program meets all of its> > assertions at run-time, or else terminates with> > an appropriate error message.> >
4. DbC approach: make all those assertions part> > of the programming language and relate them> > with the exception mechanism. A failure of> > an assertion raises an exception.> >
Only the last two approaches are appropriate> > to properly handle run-time contracts.>
For real preconditions, none of the above approaches will work.
("Real"?)
There are three kinds of assertions:
1. Those that can be ensured statically (the ideal goal);
2. Those that can be checked at run-time (the practical compromise);
3. All the remaining (the "comment" ones).
A wise programmer attempts to put assertions, as much as is possible (and reasonable), in the group 1; then he attempts to put the remaining in the group 2 (even if only some practical approximations); only then, he should use comments to informally express the remaining ones (this last group of assertions will only serve as a documentation aid).
You can't ignore the problem, and you can't write code to protect yourself> from it.
Perhaps not completely (sometimes), but some protection is surely possible. Even if only *after* the problem as manifest itself. (The world of runnable assertions is not bound only to preconditions.)
Of course it will be much harder to establish clear responsibilities between the caller and the callee and to bring objects to stable usable states (verifying its invariants) if the (precondition) error is found after the routine starts its internal execution.
All the problems you identify do exist in the practical use of DbC, but I fail to understand in what way such problems contradicts anything that I have stated about DbC.
--> Magic depends on tradition and belief. It does not welcome observation,> nor does it profit by experiment. On the other hand, science is based> on experience; it is open to correction by observation and experiment.
Martin Brown 28 February 2006 21:34:07 [ permanent link ]
Daniel T. wrote:
In article <44035117.A32AC86E@det.ua.pt>,> Miguel Oliveira e Silva <mos@det.ua.pt> wrote:>
Water Cooler v2 wrote:>>
Thanks for your diligent reply, Laurent. It has been very helpful and>>>interesting.>>>
I think I follow you and believe that my understanding has been correct>>>so far. What still intrigues me is:>>>
1. What then, are pre-conditions?>>>
My initial assessment showed they were conditions to test inside the>>>function's implementation, before the function returned valid input. If>>>they're not that, what are they?>>
They are the client's part of the routine's contract.>>
It is up to the client's to ensure a routine precondition>>*before* a call is attempted (however, a wise supplier>>should protect itself from erroneous preconditions, as>>explained below).>
A nice write up except for this one problem. If the supplier is able to > protect itself from erroneous inputs, then it can define proper outputs > for those inputs, thus making them no longer erroneous.>
Someone else's example:>
double inverse( double x ) {> return 1/x;> }>
The precondition of course is that 'x != 0' however, thats an easy thing > to check, and as such one can define a particular output for that > condition:>
// returns NaN if x == 0> double inverse( double x ) {> return 1/x;> }
Although you could specify it to return a silent NaN that merely delays detecting a hard error and contaminates other possibly meaningful future calculations that use this NaN result.
Generating a trap in most cases would be preferable since it usually signifies a serious programming error when division by zero occurs.
Your improved definition potentially breaks some code that might be expected to work algebraically since you can no longer satisfy the invariant property for the function inverse for all valid inputs.
x == inverse( inverse(x) )
whereas 0 != inverse( inverse(0)) under your definition.
And at present inverse(Nan) is also undefined.
Returning a suitably signed infinity might just be defensible under some circumstances if the calculation must proceed and cannot be aborted.
In article <du2530$jog$1@newsg4.svr.pol.co.uk>, Martin Brown <|||newspam|||@nezumi.demon.co.uk> wrote:
Daniel T. wrote:>
In article <44035117.A32AC86E@det.ua.pt>,> > Miguel Oliveira e Silva <mos@det.ua.pt> wrote:> >
Water Cooler v2 wrote:> >>
Thanks for your diligent reply, Laurent. It has been very helpful and> >>>interesting.> >>>
I think I follow you and believe that my understanding has been correct> >>>so far. What still intrigues me is:> >>>
1. What then, are pre-conditions?> >>>
My initial assessment showed they were conditions to test inside the> >>>function's implementation, before the function returned valid input. If> >>>they're not that, what are they?> >>
They are the client's part of the routine's contract.> >>
It is up to the client's to ensure a routine precondition> >>*before* a call is attempted (however, a wise supplier> >>should protect itself from erroneous preconditions, as> >>explained below).> >
A nice write up except for this one problem. If the supplier is able to > > protect itself from erroneous inputs, then it can define proper outputs > > for those inputs, thus making them no longer erroneous.> >
The precondition of course is that 'x != 0' however, thats an easy thing > > to check, and as such one can define a particular output for that > > condition:> >
// returns NaN if x == 0> > double inverse( double x ) {> > return 1/x;> > }>
Although you could specify it to return a silent NaN that merely delays > detecting a hard error and contaminates other possibly meaningful future > calculations that use this NaN result.>
Generating a trap in most cases would be preferable since it usually > signifies a serious programming error when division by zero occurs.
I have no problem with that, it's still a defined result.
Your improved definition potentially breaks some code that might be > expected to work algebraically since you can no longer satisfy the > invariant property for the function inverse for all valid inputs.>
x == inverse( inverse(x) )>
whereas 0 != inverse( inverse(0)) under your definition.>
And at present inverse(Nan) is also undefined.
We'd have to define that as well I guess. "ensure inverse(NaN) == 0"
-- Magic depends on tradition and belief. It does not welcome observation, nor does it profit by experiment. On the other hand, science is based on experience; it is open to correction by observation and experiment.
Dmitry A. Kazakov 28 February 2006 22:00:23 [ permanent link ]
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:
"Daniel T." wrote:
It seems to me, that you are incorrectly mixing the world> of normal program behavior, and the world of exceptions> and exceptional behavior.
A behavior is either correct or not. A program exposing incorrect behavior is incorrect. [That correctness might be undecidable changes here nothing.]
Exceptions exist as a> mean to deal with (detectable) incorrect programs> (in DbC it is as simply as this).
Exceptions are used for control flow in correct programs. Exceptions in incorrect programs are presumably incorrect.
and as such one can define a particular output for that>> condition:>>
// returns NaN if x == 0>> double inverse( double x ) {>> return 1/x;>> }>
You surely can, but you surely shouldn't (or else> you will be doing defensive programming and not> DbC).
Extended numeric sets are proven to be mathematically correct and deliver far safer and efficient computations than ones defined on bound numeric subsets. NaN is a *legal* value of IEEE float. It is a valid outcome of division a finite number by exact zero. Better could be only full-scaled interval arithmetic with infinity ideals.
Now that's a *real* precondition.>
What do you mean by "real"?
= used to determine correctness of the program.
(Surely you are not stating that runnable> preconditions are not real!)
That is the only possibility, as I have shown in our previous discussion. I think there is no need to repeat it.
Miguel Oliveira e Silva 28 February 2006 22:59:19 [ permanent link ]
"Dmitry A. Kazakov" wrote:
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:>
"Daniel T." wrote:>
It seems to me, that you are incorrectly mixing the world> > of normal program behavior, and the world of exceptions> > and exceptional behavior.>
A behavior is either correct or not. A program exposing incorrect behavior> is incorrect. [That correctness might be undecidable changes here nothing.]
Agreed.
Exceptions exist as a> > mean to deal with (detectable) incorrect programs> > (in DbC it is as simply as this).>
Exceptions are used for control flow in correct programs.
Not in DbC. That would be an unacceptable abuse of exceptions.
A correct program should never raise exceptions.
In DbC, exceptions are used to deal with incorrect programs (broken contracts).
Exceptions in incorrect programs are presumably incorrect.
Nope. They *correctly* signal an *incorrect* program (as in physical sciences when an experience whose results contradicts what was expected from a theoretical law, proves *correctly* that the law is *incorrect*).
and as such one can define a particular output for that> >> condition:> >>
// returns NaN if x == 0> >> double inverse( double x ) {> >> return 1/x;> >> }> >
You surely can, but you surely shouldn't (or else> > you will be doing defensive programming and not> > DbC).>
Extended numeric sets are proven to be mathematically correct and deliver> far safer and efficient computations than ones defined on bound numeric> subsets. NaN is a *legal* value of IEEE float.
(So what?)
I fail to see suitable uses of a NaN number ("Not-a-Number" number) other than to express an error within the number representation, but - by all means - be free to use that defensive approach to inverse [inverse(0) = NaN].
Just don't forget that the inverse function (as all functions) are is not an end in it selves, but a mean to reach somewhere. I'll bet that in 99.99...99% if the times the client of inverse is not expecting to divide by zero, neither such result makes any sense to whatever calculation required the use of this function.
The class/routine implementor is free to set up whatever contract he wants. However, once defined, their clients are bound to observe them. So if the inverse function defines (as it should) the precondition of a non-zero argument, then we are in the presence of an incorrect program when x equals zero (regardless of the routine's postcondition).
It is a valid outcome of> division a finite number by exact zero. Better could be only full-scaled
interval arithmetic with infinity ideals.>
Now that's a *real* precondition.> >
What do you mean by "real"?>
= used to determine correctness of the program.
What?
Are you saying that the non-zero x precondition of inverse is not useful to determine the correctness of a program?
(Surely you are not stating that runnable> > preconditions are not real!)>
That is the only possibility, as I have shown in our previous discussion.
(I missed that demonstration.)
If that is your opinion, then don't call it DbC, or else you will be confusing everyone who does not know what is this methodology.
I think there is no need to repeat it.
No it is not. It is only necessary a little effort to understand what I'm saying, and what DbC is. [B. Meyer, Object-Oriented Software Construction, 2ed, pages 331-438]
Oliver Wong 28 February 2006 23:47:02 [ permanent link ]
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:4404AB97.37B38F68@det.ua.pt...> "Dmitry A. Kazakov" wrote:>
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:>>
"Daniel T." wrote:>>
Exceptions are used for control flow in correct programs.>
Not in DbC. That would be an unacceptable abuse of exceptions.>
A correct program should never raise exceptions.
What if the contract specifically states that the behaviour of a given method is to raise an exception? E.g.
/* Pre-conditions: None. Post-conditions: NullPointerException must be raised. */ void raiseNPE() { /*Implementation goes here*/ }
In DbC, exceptions are used to deal with incorrect programs> (broken contracts).>
Exceptions in incorrect programs are presumably incorrect.>
Nope. They *correctly* signal an *incorrect* program> (as in physical sciences when an experience whose results> contradicts what was expected from a theoretical law,> proves *correctly* that the law is *incorrect*).
Not nescessarily, as shown above (i.e. the exception might correctly be part of the behaviour of a correctly implemented program). However, if a program is incorrectly implemented, then perhaps the exception raising routines are also incorrectly implemented, so raising the exception might not be a correct signal for anything!
Another example:
/* Pre-condition: none. Post-condition: "Hello World!" is printed to the standard out, no exception will be raised. */ void printHelloWorld() { print "Hello World!" throw new DivideByZeroException(); }
This program is incorrectly implemented, and coincidentally an exception is raised. But the exception being raised is *INCORRECTLY* signalling the the incorrectness of the program; it claims that the problem had something to do with a division by zero, when that was not the nature of the incorrectness at all. Actually, the incorrectness was simply the presence of the exception itself!
In article <4404AB97.37B38F68@det.ua.pt>, Miguel Oliveira e Silva <mos@det.ua.pt> wrote:
No it is not. It is only necessary a little effort to understand> what I'm saying, and what DbC is.> [B. Meyer, Object-Oriented Software Construction, 2ed,> pages 331-438]
I understand DbC, but DbC is not what I've been discussing here. Preconditions existed long before Mr. Meyer. Maybe it would avoid confusion if you used 'require' instead.
As in, "a require clause will cause an exception if its condition is false." This is very different from what happens if a precondition is not met.
pre?con?di?tion: n : A condition that must exist or be established before something can occur or be considered; a prerequisite.
If a precondition is not met, the behavior of the code cannot even be considered...
With DbC we can wonder what would happen if I sent 0 to the inverse function, because there is a defined result (an exception will be generated.) Here, I've been talking about real preconditions, ones that require being met or we cannot determine the outcome.
In article <aF2Nf.7647$Cp4.7169@edtnps90>, "Oliver Wong" <owong@castortech.com> wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message > news:4404AB97.37B38F68@det.ua.pt...> > "Dmitry A. Kazakov" wrote:> >
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:> >>
"Daniel T." wrote:> >>
Exceptions are used for control flow in correct programs.> >
Not in DbC. That would be an unacceptable abuse of exceptions.> >
A correct program should never raise exceptions.>
What if the contract specifically states that the behaviour of a given > method is to raise an exception?
Good catch! Exceptions are defined behaviors that correct programs use to signal problems. Incorrect programs can do literally anything.
In DbC, exceptions are used to deal with incorrect programs> > (broken contracts).> >
Exceptions in incorrect programs are presumably incorrect.> >
Nope. They *correctly* signal an *incorrect* program> > (as in physical sciences when an experience whose results> > contradicts what was expected from a theoretical law,> > proves *correctly* that the law is *incorrect*).>
Not nescessarily, as shown above (i.e. the exception might correctly be > part of the behaviour of a correctly implemented program). However, if a > program is incorrectly implemented, then perhaps the exception raising > routines are also incorrectly implemented, so raising the exception might > not be a correct signal for anything!
Not in DbC, and that is where the confusion lies. Miguel is talking about DbC and Eiffel's require clause, Dmitry and I are talking about preconditions.
Another example:>
/*> Pre-condition: none.> Post-condition: "Hello World!" is printed to the standard out, no exception > will be raised.> */> void printHelloWorld() {> print "Hello World!"> throw new DivideByZeroException();> }>
This program is incorrectly implemented, and coincidentally an exception > is raised. But the exception being raised is *INCORRECTLY* signalling the > the incorrectness of the program; it claims that the problem had something > to do with a division by zero, when that was not the nature of the > incorrectness at all. Actually, the incorrectness was simply the presence of > the exception itself!
Yes! And why on earth should we be passing control back to the client (by raising an exception) when we already know that it contains incorrect code, we know that because it gave us data that didn't meet our pre-condition?
-- Magic depends on tradition and belief. It does not welcome observation, nor does it profit by experiment. On the other hand, science is based on experience; it is open to correction by observation and experiment.
Dmitry A. Kazakov 1 March 2006 00:48:31 [ permanent link ]
On Tue, 28 Feb 2006 19:59:19 +0000, Miguel Oliveira e Silva wrote:
"Dmitry A. Kazakov" wrote:>
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:>>
Exceptions exist as a>>> mean to deal with (detectable) incorrect programs>>> (in DbC it is as simply as this).>>
Exceptions are used for control flow in correct programs.>
Not in DbC. That would be an unacceptable abuse of exceptions.>
A correct program should never raise exceptions.
???
Exceptions in incorrect programs are presumably incorrect.>
Nope. They *correctly* signal an *incorrect* program> (as in physical sciences when an experience whose results> contradicts what was expected from a theoretical law,> proves *correctly* that the law is *incorrect*).
Whom they signal to? To the incorrect program? What could an *incorrect* program *correctly* do about anything? Why would you trust to what an incorrect program says?
Program: "I am a liar."
Is it?
Extended numeric sets are proven to be mathematically correct and deliver>> far safer and efficient computations than ones defined on bound numeric>> subsets. NaN is a *legal* value of IEEE float.>
(So what?)>
I fail to see suitable uses of a NaN number ("Not-a-Number" number)> other than to express an error within the number representation,
It is the same misunderstanding as above. NaN is not an error, it is not a number. It is like i=sqrt(-1), which is also not a [real] number, but an ideal from another set [of complex numbers.]
but - by all means - be free to use that defensive approach to> inverse [inverse(0) = NaN].
IEEE floats don't form a ring. So the theorem above does not hold. So what?
Just don't forget that the inverse function (as all functions) are> is not an end in it selves, but a mean to reach somewhere.> I'll bet that in 99.99...99% if the times the client of inverse> is not expecting to divide by zero, neither such result makes> any sense to whatever calculation required the use of this> function.
That does not influence program correctness. A nice thing about ideals, like NaN, is that they don't leak. If an algorithm is correct in R without ideals it stays in R with ideals. So it is 100% safe. You can continue with NaN to the point where R is needed. Note, not as a precondition, but as a type conversion. This conversion will raise an exception for NaN. That would be a correct behavior.
Now that's a *real* precondition.>>>
What do you mean by "real"?>>
= used to determine correctness of the program.>
What?>
Are you saying that the non-zero x precondition> of inverse is not useful to determine the correctness> of a program?
No. I claim that only one of the following two statements is true for a given program:
1. A predicate is a "real" precondition <=> determines correctness => its value is not used (handled) by the program
2. A predicate is not a precondition => its value can be handled.
(Surely you are not stating that runnable>>> preconditions are not real!)>>
That is the only possibility, as I have shown in our previous discussion.>
(I missed that demonstration.)
pre: true function Liar return Boolean is begin return not Correct (Liar); end Liar; post : Liar
If things determining the program correctness were allowed for evaluation in the program, that would allow construction of the liar paradox.
So if a precondition determines correctness, then it cannot be checked (=it is "real"). If it is checked [and the system is not self-contradictory], then it does not determine the correctness => it is not a precondition.
If that is your opinion, then don't call it DbC, or else you> will be confusing everyone who does not know what is> this methodology.
So in your opinion the "real" DbC is self-contradictory? Cool!
Miguel Oliveira e Silva 1 March 2006 01:46:17 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:4404AB97.37B38F68@det.ua.pt...> > "Dmitry A. Kazakov" wrote:> >
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:> >>
"Daniel T." wrote:> >>
Exceptions are used for control flow in correct programs.> >
Not in DbC. That would be an unacceptable abuse of exceptions.> >
A correct program should never raise exceptions.>
What if the contract specifically states that the behaviour of a given> method is to raise an exception?
I understand what you Daniel and Dmitry are trying to argue in this point. But as I several times attempted to call up to your attention, DbC(tm) has a much simpler view of contracts, normal program behavior and exceptions. In DbC exceptions are simply the response of a program to an incorrect behavior due to a broken contract.
There is no need to put exceptions in contracts, because in DbC when a runnable assertions is false an exception is required to be raised.
You may argue that exceptions can be used in normal program control flow (in Eiffel its much harder to do that, because, unlike Ada/C++/Java its exception mechanism is tightly bound to DbC). However, I must insist that such use is inappropriate (even in those languages). Not only normal conditional instructions are much more safe, expressive and efficient, but also it mixes up two different programming worlds: the one bound to the expected normal behavior of programs, and the one used to get along with exceptional incorrect behavior.
Also using exceptions for normal program control, is little more than the disguise use of the unstructured goto instruction (no more single point of entry and single point of exit for program components).
The structured property of programs is extremely important because it is one that allows Hoare/Floyd formal view of attaching meaning to programs through preconditions and postconditions (which was one of the most important earlier work in which Meyer based his DbC methodology).
E.g.>
/*> Pre-conditions: None.> Post-conditions: NullPointerException must be raised.> */> void raiseNPE() {> /*Implementation goes here*/> }>
In DbC, exceptions are used to deal with incorrect programs> > (broken contracts).> >
Exceptions in incorrect programs are presumably incorrect.> >
Nope. They *correctly* signal an *incorrect* program> > (as in physical sciences when an experience whose results> > contradicts what was expected from a theoretical law,> > proves *correctly* that the law is *incorrect*).>
Not nescessarily, as shown above (i.e. the exception might correctly be> part of the behaviour of a correctly implemented program).
I was referring to DbC(tm) in which, as I have mentioned several times, things are much simpler and responsibilities are much clear.
However, if a> program is incorrectly implemented, then perhaps the exception raising> routines are also incorrectly implemented, so raising the exception might> not be a correct signal for anything!
If the exception is raised as a result of a false assertion, then it correctly signals a broken contract, even if that contract is not the intended one.
Another example:>
/*> Pre-condition: none.> Post-condition: "Hello World!" is printed to the standard out, no exception> will be raised.> */> void printHelloWorld() {> print "Hello World!"> throw new DivideByZeroException();> }>
This program is incorrectly implemented, and coincidentally an exception> is raised. But the exception being raised is *INCORRECTLY* signalling the> the incorrectness of the program;
Of course the use of the "throw" instruction in programs can incorrectly raise exceptions (there is no such instruction in Eiffel). If you take a closer look to my message you'll see that I was referring to exceptions raised as a result of false assertions (situation in which they are always correctly raised, even if the assertion was not intended).
it claims that the problem had something> to do with a division by zero, when that was not the nature of the> incorrectness at all.
Indeed.
Actually, the incorrectness was simply the presence of> the exception itself!
Just because it was not bound to assertions (and DbC).
Miguel Oliveira e Silva 1 March 2006 02:09:10 [ permanent link ]
"Daniel T." wrote:
In article <aF2Nf.7647$Cp4.7169@edtnps90>,> "Oliver Wong" <owong@castortech.com> wrote:>
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> > news:4404AB97.37B38F68@det.ua.pt...> > > "Dmitry A. Kazakov" wrote:> > >
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:> > >>
"Daniel T." wrote:> > >>
Exceptions are used for control flow in correct programs.> > >
Not in DbC. That would be an unacceptable abuse of exceptions.> > >
A correct program should never raise exceptions.> >
What if the contract specifically states that the behaviour of a given> > method is to raise an exception?>
Good catch! Exceptions are defined behaviors that correct programs use> to signal problems. Incorrect programs can do literally anything.>
In DbC, exceptions are used to deal with incorrect programs> > > (broken contracts).> > >
Exceptions in incorrect programs are presumably incorrect.> > >
Nope. They *correctly* signal an *incorrect* program> > > (as in physical sciences when an experience whose results> > > contradicts what was expected from a theoretical law,> > > proves *correctly* that the law is *incorrect*).> >
Not nescessarily, as shown above (i.e. the exception might correctly be> > part of the behaviour of a correctly implemented program). However, if a> > program is incorrectly implemented, then perhaps the exception raising> > routines are also incorrectly implemented, so raising the exception might> > not be a correct signal for anything!
Not in DbC, and that is where the confusion lies.
All my messages clearly state that I'm writing about DbC(tm).
In fact, the first message I sent (In a thread named: "Design by contract and unit tests") was a response to an incorrect behavior attributed explicitly to DbC.
Miguel is talking about DbC and Eiffel's require clause,
I'm not referring specifically to Eiffel's require clause.
DbC can (and should) be used in languages other than Eiffel (I use it in all the languages I use and teach). Of course it is be much more difficult to properly use DbC in languages such as Ada/C++/Java. For example, in Eiffel class assertions (invariants, preconditions and postconditions) are part of the class interface (not its implementation).
Dmitry and I are talking about preconditions.
So am I, and how they relate to class contracts. Of course I'm not only referring to Hoare/Floyd's mathematical concept but to the practical DbC preconditions.
If you take a closer look to my messages you'll see we don't disagree that much regarding the theoretical mathematical concept.
Miguel Oliveira e Silva 1 March 2006 02:47:16 [ permanent link ]
"Daniel T." wrote:
In article <4404AB97.37B38F68@det.ua.pt>,> Miguel Oliveira e Silva <mos@det.ua.pt> wrote:>
No it is not. It is only necessary a little effort to understand> > what I'm saying, and what DbC is.> > [B. Meyer, Object-Oriented Software Construction, 2ed,> > pages 331-438]>
I understand DbC, but DbC is not what I've been discussing here.
Ok. But "Water Cooler v2"'s initial message was clearly referring to DbC.
Preconditions existed long before Mr. Meyer.
Maybe it would avoid confusion if you used 'require' instead.
DbC is a very useful tool in any language (not just Eiffel).
As in, "a require clause will cause an exception if its condition is> false." This is very different from what happens if a precondition is> not met.
What do you propose that should happen in such situations?
In my opinion either:
1. The error is detected statically and program is simply not allowed to run (the ideal solution);
2. The errors cannot be detected statically, but there is the possibility to trace them at runtime through runnable assertions. Regardless of this dynamic typed alike approach, the program is incorrect and it should behave appropriately (terminate immediately or raise an exception to allow a fault tolerant alternative path);
3. The error cannot be detected neither statically nor within runnable assertions. In this case since I'm an atheist there are not much things I could do... Nevertheless, sooner or later (hopefully, sooner) a runnable assertion will most likely fail (although not where it should).
Do you have alternative proposals?
pre?con?di?tion: n : A condition that must exist or be established> before something can occur or be considered; a prerequisite.
Exactly.
If a precondition is not met, the behavior of the code cannot even be> considered...
Agreed. The program is incorrect (the error is somewhere within the caller's code).
However the less the program is allowed to normally proceed after the error detection, the better (the error localization is much more precise, and devising fault tolerant algorithms are much easier and safe).
With DbC we can wonder what would happen if I sent 0 to the inverse> function, because there is a defined result (an exception will be> generated.)
In DbC that's an exceptional behavior (due to a programming error), not a normal behavior.
Here, I've been talking about real preconditions, ones that> require being met or we cannot determine the outcome.
All preconditions (postconditions, invariants and other assertions) are required to be met in correct programs. So I think I've been talking about the same things.
DbC prescribes a very simple and precise behavior. If you have alternative practical proposals -based on the mathematical foundations in which most likely we all agree- I'm all ears.
Miguel Oliveira e Silva 1 March 2006 03:40:11 [ permanent link ]
"Dmitry A. Kazakov" wrote:
On Tue, 28 Feb 2006 19:59:19 +0000, Miguel Oliveira e Silva wrote:>
"Dmitry A. Kazakov" wrote:> >
On Tue, 28 Feb 2006 16:38:41 +0000, Miguel Oliveira e Silva wrote:> >>
Exceptions exist as a> >>> mean to deal with (detectable) incorrect programs> >>> (in DbC it is as simply as this).> >>
Exceptions are used for control flow in correct programs.> >
Not in DbC. That would be an unacceptable abuse of exceptions.> >
A correct program should never raise exceptions.>
???>
Exceptions in incorrect programs are presumably incorrect.> >
Nope. They *correctly* signal an *incorrect* program> > (as in physical sciences when an experience whose results> > contradicts what was expected from a theoretical law,> > proves *correctly* that the law is *incorrect*).>
Whom they signal to?
In the case of a false precondition, to the routine's client.
To the incorrect program?
Exactly.
What could an *incorrect*> program *correctly* do about anything?
Terminate execution (identifying the error); or propagate the exception upwards in the call stack until a point in which it would be safe to retry an alternative (hopefully, correct) algorithm (fault tolerance).
Why would you trust to what an> incorrect program says?
It is precisely because we can't trust an incorrect program, that an exception should be raised (otherwise, there would be no reason for not allowing the program to proceed normally).
Program: "I am a liar.">
Is it?
(To be or not to be, that's the question!)
Extended numeric sets are proven to be mathematically correct and deliver> >> far safer and efficient computations than ones defined on bound numeric> >> subsets. NaN is a *legal* value of IEEE float.> >
(So what?)> >
I fail to see suitable uses of a NaN number ("Not-a-Number" number)> > other than to express an error within the number representation,>
It is the same misunderstanding as above. NaN is not an error, it is not a> number.
As I said before, in 99.9...9% of the uses of the inverse(0) function it will be the result of a programming error (regardless of NaN).
It is like i=sqrt(-1), which is also not a [real] number, but an> ideal from another set [of complex numbers.]
It is not like complex numbers, because to my knowledge there is no practical use for NaN (other than error signaling, for which there is a much better programming tool: exceptions).
In R, I agree with you that the problem is the same in both situations: a programming error (located in the caller).
but - by all means - be free to use that defensive approach to> > inverse [inverse(0) = NaN].>
IEEE floats don't form a ring. So the theorem above does not hold. So what?>
Just don't forget that the inverse function (as all functions) are> > is not an end in it selves, but a mean to reach somewhere.> > I'll bet that in 99.99...99% if the times the client of inverse> > is not expecting to divide by zero, neither such result makes> > any sense to whatever calculation required the use of this> > function.>
That does not influence program correctness. A nice thing about ideals,> like NaN, is that they don't leak. If an algorithm is correct in R without> ideals it stays in R with ideals. So it is 100% safe. You can continue with> NaN to the point where R is needed. Note, not as a precondition, but as a> type conversion. This conversion will raise an exception for NaN. That> would be a correct behavior.
So you agree with me that the appropriate (exceptional) behavior as a result to a use of NaN would be to raise an exception?
(We are getting somewhere.)
Of course in my opinion, the error source is not due to the use of NaN. It is due to the part of program that was responsible for its existence (but that just my opinion).
Now that's a *real* precondition.> >>>
What do you mean by "real"?> >>
= used to determine correctness of the program.> >
What?> >
Are you saying that the non-zero x precondition> > of inverse is not useful to determine the correctness> > of a program?>
No. I claim that only one of the following two statements is true for a> given program:>
1. A predicate is a "real" precondition <=> determines correctness => its> value is not used (handled) by the program>
2. A predicate is not a precondition => its value can be handled.
So you prescribe a defensive programming technique when your compile time tools are unable to prove the correctness of preconditions (and what about other assertions?), even when it would be trivial to use them at runtime as in DbC?
(Fine, just don't call it DbC.)
(Surely you are not stating that runnable> >>> preconditions are not real!)> >>
That is the only possibility, as I have shown in our previous discussion.> >
(I missed that demonstration.)>
pre: true> function Liar return Boolean is> begin> return not Correct (Liar);> end Liar;> post : Liar
If things determining the program correctness were allowed for evaluation> in the program, that would allow construction of the liar paradox.
That's the problem of searching for total correctness (or the "truth" in science)
The much more practical and non-paradoxal search for incorrect programs is much easier (as it is to search for incorrect scientific theories).
So if a precondition determines correctness,
A precondition (or any other assertion) states a correctness condition. It does not prove, by itself, the correctness of a program (it is a mean, not an end).
then it cannot be checked (=it> is "real"). If it is checked [and the system is not self-contradictory],> then it does not determine the correctness => it is not a precondition.
Runnable assertions cannot fully assert program correctness (for the nth+2 times). They allow the detection of *incorrect* programs (if, and only if, the assertion happens to be false somewhere during program execution).
A runnable precondition is a practical approximation of the formal mathematical precondition expected to be observed. It cannot prove program correctness. It can, nevertheless, detect program incorrectness (why is this so difficult to understand?).
If that is your opinion, then don't call it DbC, or else you> > will be confusing everyone who does not know what is> > this methodology.>
So in your opinion the "real" DbC is self-contradictory?
No.
(A reference for your real "DbC" would be most welcomed, so that we may communicate with each other.)
Daniel T. wrote:> In article <du2530$jog$1@newsg4.svr.pol.co.uk>,> Martin Brown <|||newspam|||@nezumi.demon.co.uk> wrote:>
Daniel T. wrote:
The precondition of course is that 'x != 0' however, thats an easy thing >>>to check, and as such one can define a particular output for that >>>condition:>>>
// returns NaN if x == 0>>>double inverse( double x ) {>>> return 1/x;>>>}>>
Although you could specify it to return a silent NaN that merely delays >>detecting a hard error and contaminates other possibly meaningful future >>calculations that use this NaN result.>>
Generating a trap in most cases would be preferable since it usually >>signifies a serious programming error when division by zero occurs.>
I have no problem with that, it's still a defined result.
More importantly it isolates the failing code and broken contract as soon as is reasonably possible. There isn't much point in continuing a calculation with a runtime lash up flying on a wing and a prayer.
Anything that uses the Nan becomes contaminated wasting time and money.
Your improved definition potentially breaks some code that might be >>expected to work algebraically since you can no longer satisfy the >>invariant property for the function inverse for all valid inputs.>>
x == inverse( inverse(x) )>>
whereas 0 != inverse( inverse(0)) under your definition.>>
And at present inverse(Nan) is also undefined.>
We'd have to define that as well I guess. "ensure inverse(NaN) == 0"
Why guess or use Nan at all when the hardware provides signed infinites?
It is a very bad idea. One perfectly reasonable use for signalling NaNs in testing is to ensure that loading floating point numbers from uninitiallised variables will generate an immediate error interrupt.
If you must allow inverse(0) it should return a signed Infinity (and IEEE floating point defines them) and that is what most FP hardware does by default but it also requests a zero divide interrupt unless masked.
Dmitry A. Kazakov 1 March 2006 14:22:06 [ permanent link ]
On Wed, 01 Mar 2006 00:40:11 +0000, Miguel Oliveira e Silva wrote:
"Dmitry A. Kazakov" wrote:>
Why would you trust to what an>> incorrect program says?>
It is precisely because we can't trust an> incorrect program, that an exception should> be raised (otherwise, there would be no reason> for not allowing the program to proceed> normally).
But an exception is a way to convey something. How can you trust it?
Program: "I am a liar.">>
Is it?>
(To be or not to be, that's the question!)
Don't trust in what a program tells, look what it does!
Extended numeric sets are proven to be mathematically correct and deliver>>>> far safer and efficient computations than ones defined on bound numeric>>>> subsets. NaN is a *legal* value of IEEE float.>>>
(So what?)>>>
I fail to see suitable uses of a NaN number ("Not-a-Number" number)>>> other than to express an error within the number representation,>>
It is the same misunderstanding as above. NaN is not an error, it is not a>> number.>
As I said before, in 99.9...9% of the uses of the inverse(0) function> it will be the result of a programming error (regardless of NaN).
Where you get this statistics? In practically any correct numeric application you are unable to ensure that numeric underflows will not lead to a zero. Zero is a reachable state. So inverse(0) is quite legal, and the result NaN is also legal. It is not an error. Neither it indicates an error. If the algorithm is correct, NaN denotes a result outside the interval of finite values representable by double. That is a *correct* answer! You cannot depict any given value as damned. In the floating-point model each algorithm has some accuracy Eps (maybe dependant on the input.) It is a part of the contract. If the theoretically known result x deviates from the evaluated result y by less than Eps>0, then the algorithm is correct for the given input. So if x is the maximal finite double, then for any Eps NaN is a correct output.
It is like i=sqrt(-1), which is also not a [real] number, but an>> ideal from another set [of complex numbers.]>
It is not like complex numbers, because to my knowledge> there is no practical use for NaN (other than error> signaling, for which there is a much better programming> tool: exceptions).
No, exceptions were inappropriate here.
If we consider closed operation 1/x defined on a numeric type. Then its definition depends on that type.
Note that double (assuming IEEE float) and, say, DEC floating-point type, are different types with different sets of values! It is *incorrect* to raise an exception for double 1/x because 1/0 is *defined* to return NaN for 1/0. Defined here, is in strict mathematical sense. If you take another type, with the domain set limited to only real numbers (like DEC floating-point is), then exception would be appropriate.
The above analysis considers closed 1/x, i.e. one, which result is of the same type as the argument. There could be cross variants.
That does not influence program correctness. A nice thing about ideals,>> like NaN, is that they don't leak. If an algorithm is correct in R without>> ideals it stays in R with ideals. So it is 100% safe. You can continue with>> NaN to the point where R is needed. Note, not as a precondition, but as a>> type conversion. This conversion will raise an exception for NaN. That>> would be a correct behavior.>
So you agree with me that the appropriate (exceptional) behavior> as a result to a use of NaN would be to raise an exception?
Not at all! You have to state the contract first. If "using" applies to the type double, then NaN is a legal value. If it is not of double type but of some other type, maybe, based on double, which does not contain NaN as a value, then you have to convert NaN to that type. (It is a typed world.) This would raise an exception. And again, not because of a contract violation. The contract states: double in, finite number or exception out. If you *excluded* exception from the postcondition, and let it propagate, then that would be a contract violation.
Now that's a *real* precondition.>>>>>
What do you mean by "real"?>>>>
= used to determine correctness of the program.>>>
What?>>>
Are you saying that the non-zero x precondition>>> of inverse is not useful to determine the correctness>>> of a program?>>
No. I claim that only one of the following two statements is true for a>> given program:>>
1. A predicate is a "real" precondition <=> determines correctness => its>> value is not used (handled) by the program>>
2. A predicate is not a precondition => its value can be handled.>
So you prescribe a defensive programming technique when> your compile time tools are unable to prove the correctness> of preconditions (and what about other assertions?),
No. You can prove correctness, you can do it at run-time, anything you want and have to, but *not* in the same program. A fault tolerant design assumes that there is a hierarchy of programs. Higher levels of the hierarchy can monitor lower levels. It is absolutely OK. Preconditions cannot be evaluated by the program, but they can be by another program. That program may run at the same computer (though better in a compiler.) When that program detects a contract violation, that would not raise an exception in a program under check. That would be wrong. It will abort the incorrect program. Technically it might look as an unhandled exception, but that is an implementation issue. The point is: whatever correctness checks are made they are outside. Another important point is, that the computing system as a whole, cannot check correctness of itself. The hierarchy must stop somewhere.
pre: true>> function Liar return Boolean is>> begin>> return not Correct (Liar);>> end Liar;>> post : Liar>
If things determining the program correctness were allowed for evaluation>> in the program, that would allow construction of the liar paradox.>
That's the problem of searching for total correctness> (or the "truth" in science)>
The much more practical and non-paradoxal search for incorrect> programs is much easier (as it is to search for incorrect scientific> theories).
You can't gain anything practical from an incorrect theory.
Note also that the correct theory gives you much more, than you think. It tells that run-time checks are the behavior [of the thing doing the checks.] This is a very valuable knowledge, which instructs developers to publish the checks in the contract. It also gives a rationale for layered fault-tolerant software design with layers insulated against faults underneath. Exactly because you cannot do much for correctness within one layer. This is IMO *true* Design by Contract!
In article <4404E104.CA725428@det.ua.pt>, Miguel Oliveira e Silva <mos@det.ua.pt> wrote:
"Daniel T." wrote:>
In article <4404AB97.37B38F68@det.ua.pt>,> > Miguel Oliveira e Silva <mos@det.ua.pt> wrote:> >
As in, "a require clause will cause an exception if its condition is> > false." This is very different from what happens if a precondition is> > not met.>
What do you propose that should happen in such situations?
When a require clause's condition is false, it should throw an exception, because that is what require clauses are defined to do. I would never propose that a well defined construct do anything other than what it is defined to do.
When a precondition is not met, we cannot propose anything because (as you agree to below,) we cannot even consider the behavior of the code.
If a precondition is not met, the behavior of the code cannot even be> > considered...>
Agreed. The program is incorrect (the error is somewhere within> the caller's code).
Funny, you agree that "if a precondition is not met, the behavior of the code cannot even be considered", yet you spend a lot of bandwidth considering what the behavior of the code should be when a precondition is not met...
That's what I mean when I talk about "real" preconditions, as opposed to Eiffel's pseudo-preconditions that really aren't because they have defined behavior. Please understand, I'm not deriding Eiffel here, I think it is good to limit preconditions precisely because we can't reason about the behavior of programs when they are not met (and we need to be able to reason about the behavior of our programs.)
Here, I've been talking about real preconditions, ones that> > require being met or we cannot determine the outcome.>
All preconditions (postconditions, invariants and other assertions)> are required to be met in correct programs. So I think I've been> talking about the same things.>
DbC prescribes a very simple and precise behavior.> If you have alternative practical proposals -based on> the mathematical foundations in which most likely> we all agree- I'm all ears.
And therein lies the rub. "the behavior cannot even be considered" (you know, that part you agreed to above?) means there is no solution, nor can we even consider one.
As such we must do our best to remove preconditions in our code and provide well defined results for anything detectable. Eiffel does this by creating a postcondition, "if a parameter is a certain value an exception will be raised." I'm fine with that, it's a great idea, but it isn't a precondition (which is a good thing, because preconditions are bad.)
-- Magic depends on tradition and belief. It does not welcome observation, nor does it profit by experiment. On the other hand, science is based on experience; it is open to correction by observation and experiment.
In article <7fyp5pt4hnq7.1jec4c7748d0u$.dlg@40tude.net>, "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote:
Note also that the correct theory gives you much more, than you think. It> tells that run-time checks are the behavior [of the thing doing the> checks.] This is a very valuable knowledge, which instructs developers to> publish the checks in the contract. It also gives a rationale for layered> fault-tolerant software design with layers insulated against faults> underneath. Exactly because you cannot do much for correctness within one> layer. This is IMO *true* Design by Contract!
Careful there, "Design by Contract" is trademarked, there is only One True DbC.
-- Magic depends on tradition and belief. It does not welcome observation, nor does it profit by experiment. On the other hand, science is based on experience; it is open to correction by observation and experiment.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:4404D2B9.176463A5@det.ua.pt...> Oliver Wong wrote:>
What if the contract specifically states that the behaviour of a >> given>> method is to raise an exception?>
I understand what you Daniel and Dmitry are trying to> argue in this point. But as I several times attempted to call> up to your attention, DbC(tm) has a much simpler view of> contracts, normal program behavior and exceptions.> In DbC exceptions are simply the response of a program> to an incorrect behavior due to a broken contract.>
There is no need to put exceptions in contracts, because> in DbC when a runnable assertions is false an exception> is required to be raised.>
You may argue that exceptions can be used in normal> program control flow (in Eiffel its much harder to do that,> because, unlike Ada/C++/Java its exception mechanism> is tightly bound to DbC). However, I must insist that> such use is inappropriate (even in those languages).> Not only normal conditional instructions are much> more safe, expressive and efficient, but also it mixes> up two different programming worlds: the one bound> to the expected normal behavior of programs, and the> one used to get along with exceptional incorrect behavior.>
Also using exceptions for normal program control,> is little more than the disguise use of the unstructured> goto instruction (no more single point of entry and> single point of exit for program components).
From my perspective, you have a view of "this is good enough for me, so it should be good enough for everyone". When I mentioned that testing the pre-conditions could break runtime performance requirements of a method, you said something along the lines of 99.99999% of programs are not real-time systems, and runtime performance isn't important except for real-time systems, for example. So I'm going to provide a few examples, but I fear you'll simply dismiss them as "well, these don't apply 99.99999% of the time."
I think sometimes raising exceptions is a very legitimate part of the behaviour of a program, and not merely constrained to indicating incorrect programs. For example, let's say I'm writing a modular program which can accept plugins written by 3rd parties. I want to ensure that if the plugins screw up and throw exceptions, this doesn't bring down my whole application.
Let's say I'm writing a unit testing framework (e.g. jUnit), I might create a method whose sole purpose is to throw an exception (e.g. assert[anything]()), so the exception is very much part of the normal behaviour of the program as well. Example:
/* Pre-conditions: objects A and B implement an appropriate isEqual method. Post-condition: Throws an exception if A.equals(B) is false, or if exactly one of the objects is null. */ assertEquals(Object A, Object B) { if (A == null) { if (B == null) { return; } throw new AssertionException(); } if (B == null) { throw new AssertionException(); } if (A.equals(B)) { return; } throw new AssertionException(); }
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:7fyp5pt4hnq7.1jec4c7748d0u$.dlg@40tude.net...
Note that double (assuming IEEE float) and, say, DEC floating-point type,> are different types with different sets of values! It is *incorrect* to> raise an exception for double 1/x because 1/0 is *defined* to return NaN> for 1/0. Defined here, is in strict mathematical sense. If you take > another> type, with the domain set limited to only real numbers (like DEC> floating-point is), then exception would be appropriate.
I didn't bring this up earlier, 'cause it didn't seem like an important point, but since this example is being quoted, paraphrased, repeated, etc. all over this thread, I figure I should say it now.
I think 1/0 is defined by IEEE to be equal to positive infinity, not NaN.
(negative) infinity divided by (negative) infinity is equal to NaN. Maybe infinity minus infinity is also NaN, but I'm not sure about this last one.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:4404ED6B.CFB9753E@det.ua.pt...> "Dmitry A. Kazakov" wrote:
What could an *incorrect*>> program *correctly* do about anything?>
Terminate execution (identifying the error);> or propagate the exception upwards in the> call stack until a point in which it would be> safe to retry an alternative (hopefully, correct)> algorithm (fault tolerance).
You seem to be missing the point here. The program is incorrect. So you look at the source code, and somewhere, it contains bugs, but you're not sure where yet. You look at the source code, and you *THINK* what it will do is terminate execution or propagate the exception upwards in the call stack until a point in which it would be safe to retry an alternative algorithm... but maybe it won't! Why not? Because it's incorrect!
This is what is meant by "You cannot say anything about the behaviour of an incorrect program." You might WANT it to terminate; but it won't nescessarily do so!
Miguel Oliveira e Silva 1 March 2006 18:32:12 [ permanent link ]
"Daniel T." wrote:
(...)
If a precondition is not met, the behavior of the code cannot even be> > > considered...> >
Agreed. The program is incorrect (the error is somewhere within> > the caller's code).>
Funny, you agree that "if a precondition is not met, the behavior of the> code cannot even be considered",
When a precondition fails, it is useless to think about the normal behavior of programs (that was what I though you were referring to). As I said too many times, the program is incorrect, hence it should not run, or else appropriate exceptional behavior is desirable and even required if the faulty precondition was detected at runtime.
(You seem to be concerned about a pure theoretical concept of preconditions with little practical use when our static verification tools are unable to prove their correctness. I am concerned about how we can approximate them in practical programming to help us build more reliable programs.)
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 ...)
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?
They are not (obviously) proved preconditions. Nevertheless they are a practical approximation of the desirable behavior, and sometimes they even express the expected formal condition (equal to its pure mathematical counterpart). Of course they cannot prove program correctness at runtime, they can, nevertheless, correctly detect some incorrect programs (which is a huge contribution to approximate correctness and to produce quality reliable software).
Also you seem to consider, for some reason, that exceptions are part of normal program behavior (reason why you insist in not fully understanding what I'm saying). That, in my opinion (and in DbC's), is a basic misunderstanding of this mechanism. Its existence is exactly to allow us to think and use two separated worlds: normal program behavior and exceptional behavior. In the former our primary objective is to reach correctness. The latter aims at robustness, either for a faster detection of errors and their correction, or for implementing fault tolerant techniques.
(...)
Eiffel's pseudo-preconditions that really aren't because they have> defined behavior.
A precondition is a condition expected to be observed before the routine starts its execution. Eiffel require clauses *are* preconditions (such as ensure clauses are postconditions and invariant clauses are invariants).
Please understand, I'm not deriding Eiffel here, I> think it is good to limit preconditions precisely because we can't> reason about the behavior of programs when they are not met (and we need> to be able to reason about the behavior of our programs.)>
Here, I've been talking about real preconditions, ones that> > > require being met or we cannot determine the outcome.> >
All preconditions (postconditions, invariants and other assertions)> > are required to be met in correct programs. So I think I've been> > talking about the same things.> >
DbC prescribes a very simple and precise behavior.> > If you have alternative practical proposals -based on> > the mathematical foundations in which most likely> > we all agree- I'm all ears.>
And therein lies the rub. "the behavior cannot even be considered" (you> know, that part you agreed to above?) means there is no solution, nor> can we even consider one.>
As such we must do our best to remove preconditions in our code and> provide well defined results for anything detectable.
No.
Eiffel does this by creating a postcondition,
No it doesn't.
"if a parameter is a certain value an> exception will be raised."
That is not part of the postcondition (how could it be if the routine did not start its execution!). It is the exceptional behavior as a result of a failed precondition.
I'm fine with that, it's a great idea, but it> isn't a precondition
Of course it is. It was a condition expected to be verified by the client *before* (as in *pre*) the routine starts its execution. In this respect, it is irrelevant if the error was found statically or at runtime.
(References please.)
(which is a good thing, because preconditions are bad.)
?
Preconditions, postconditions, invariants and other assertions are good. They make it quite clear what are the contracts expected, and they give us an idea of the meaning of programs (reusing Floyd's article title).
--> Magic depends on tradition and belief. It does not welcome observation,> nor does it profit by experiment. On the other hand, science is based> on experience; it is open to correction by observation and experiment.
Dmitry A. Kazakov 1 March 2006 19:15:52 [ permanent link ]
On Wed, 01 Mar 2006 15:04:41 GMT, Oliver Wong wrote:
I didn't bring this up earlier, 'cause it didn't seem like an important > point, but since this example is being quoted, paraphrased, repeated, etc. > all over this thread, I figure I should say it now.>
I think 1/0 is defined by IEEE to be equal to positive infinity, not > NaN.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:4405BE7C.41AD05C9@det.ua.pt...>
When a precondition fails, it is useless to think about the normal> behavior of programs (that was what I though you were referring to).> As I said too many times, the program is incorrect, hence it should> not run, or else appropriate exceptional behavior is desirable and> even required if the faulty precondition was detected at runtime.
I think if a precondition fails, it is useless to think about the behaviour of a program, period. Not merely the "normal" behaviour, but ALL behaviour is unpredictable.
Perhaps an incorrect program should not run, but once it's running, it's too late! The program is controlling its own control-flow, and there isn't anything you can really do, unless the OS itself can detect incorrectness and shut down the process associated with the program (but detecting incorrectness is theoretically impossible anyway).
[...]>
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.
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. Any behaviour is possible. An exception *MIGHT* be thrown, or null might be returned, or any of several other possibilities.
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.
(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. For example, consider the following two methods:
/* pre-condition: s is not null. post-condition: returns s. */ String foo(String s) { return s; }
Both of these methods satisfy their contracts. But notice that the implementation for both methods is exactly the same. And yet, one of the contracts has a pre-condition, and the other one doesn't. Therefore, I don't think one should determine what the pre-conditions of a method are based on the implementation of that method.
Therefore, with a program like this:
/* pre-condition: s is not null. post-condition: returns s. */ String foo(String s) { assert s != null; return s; }
"s != null" is a runnable pre-condition. But it's also a "real" pre-condition, in the sense that yes, this contract really does have a pre-condition. The contract didn't have to be designed this way; but that's the way it was designed. The fact that the pre-condition is runnable does not make it any less real.
However, note that the following method also satisfies its contract:
/* pre-condition: none. post-condition: returns s if it is not null, otherwise throws an AssertionException. */ String foo(String s) { assert s != null; return s; }
So Daniel, I think, is saying that runnable pre-conditions can always be converted to post-conditions. That part I agree with. But I don't agree with the idea that this nescessarily means runnable pre-conditions are less real.
Also you seem to consider, for some reason, that exceptions are> part of normal program behavior (reason why you insist in not> fully understanding what I'm saying). That, in my opinion (and in> DbC's), is a basic misunderstanding of this mechanism.> Its existence is exactly to allow us to think and use two> separated worlds: normal program behavior and exceptional> behavior. In the former our primary objective is to reach> correctness. The latter aims at robustness, either for a> faster detection of errors and their correction, or for> implementing fault tolerant techniques.
See above. There's 3 worlds. Handling routine normal situations, handling exceptional situations, and being incorrectly implemented. Violating the pre-condition puts you in the "incorrectl implemented" world, not the "handling exceptional situation" world.
(...)>
Eiffel's pseudo-preconditions that really aren't because they have>> defined behavior.>
A precondition is a condition expected to be observed> before the routine starts its execution. Eiffel require clauses> *are* preconditions (such as ensure clauses are postconditions> and invariant clauses are invariants).
I think Daniel is saying that Eiffel require clauses are implementation detail which allows the programmer to cause the program to behave in certain ways. They can be used to check pre-conditions, or they can be used to enforce post-conditions. They are not THEMSELVES pre-conditions; they are tools for checking pre-conditions.
Please understand, I'm not deriding Eiffel here, I>> think it is good to limit preconditions precisely because we can't>> reason about the behavior of programs when they are not met (and we need>> to be able to reason about the behavior of our programs.)>>
Here, I've been talking about real preconditions, ones that>> > > require being met or we cannot determine the outcome.>> >
All preconditions (postconditions, invariants and other assertions)>> > are required to be met in correct programs. So I think I've been>> > talking about the same things.>> >
DbC prescribes a very simple and precise behavior.>> > If you have alternative practical proposals -based on>> > the mathematical foundations in which most likely>> > we all agree- I'm all ears.>>
And therein lies the rub. "the behavior cannot even be considered" (you>> know, that part you agreed to above?) means there is no solution, nor>> can we even consider one.>>
As such we must do our best to remove preconditions in our code and>> provide well defined results for anything detectable.>
No.
Yes. The fewer pre-conditions your methods have, the fewer situations in which the method will behave in an undefined manner, and thus the more robust that method is.
Eiffel does this by creating a postcondition,>
No it doesn't.
I won't get into this, since I think it's just a matter of choice of words. You're both right.
"if a parameter is a certain value an>> exception will be raised.">
That is not part of the postcondition (how could it> be if the routine did not start its execution!).> It is the exceptional behavior as a result of a failed> precondition.>
I'm fine with that, it's a great idea, but it>> isn't a precondition>
Of course it is. It was a condition expected> to be verified by the client *before* (as in *pre*)> the routine starts its execution. In this respect,> it is irrelevant if the error was found statically> or at runtime.>
(References please.)>
(which is a good thing, because preconditions are bad.)>
?>
Preconditions, postconditions, invariants and other> assertions are good. They make it quite clear> what are the contracts expected, and they give> us an idea of the meaning of programs (reusing> Floyd's article title).
Preconditions are bad, but specifying pre-conditions are good.
<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>
We should have as few preconditions as reasonably possible (we should break as few vases as possible), but any pre-conditions we are unable to eliminate, we should should clearly specify (we should admit whenever we break a vase instead of lying about it).
Miguel Oliveira e Silva 2 March 2006 18:43:30 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:4405BE7C.41AD05C9@det.ua.pt...> >
When a precondition fails, it is useless to think about the normal> > behavior of programs (that was what I though you were referring to).> > As I said too many times, the program is incorrect, hence it should> > not run, or else appropriate exceptional behavior is desirable and> > even required if the faulty precondition was detected at runtime.>
I think if a precondition fails, it is useless to think about the> behaviour of a program, period. Not merely the "normal" behaviour, but ALL> behaviour is unpredictable.
Yes and no ;).
Precisely one of the positive side-effects of runnable preconditions (and other assertions) is to prevent programs to "shoot themselves in the foot" (by stopping at that point normal program execution, giving the possibility for fault tolerant alternative execution paths).
(Preconditions, can not only exist in executable programs, but they are also part of the good guys.)
In theory, unless we are able to prove statically the correctness of programs, the behavior of *any* program is unpredictable. After all we can't be entirely sure that there could be a false untested assertion somewhere in the program. In practice, our best defense within a program, is to ensure -with every means possible- that all assertion are correct, and immediately take preventive measures when the first one fails. We won't be sure that the program state will be completely stable (we almost never are), but we are guaranteed that the runnable invariants of all objects -except probably the one responsible for the failure (in the case of preconditions that would be the caller)- are observed (a very important property deal with failures).
Perhaps an incorrect program should not run, but once it's running, it's> too late!
Yes and no.
Afterall if a got a flu, the problem was not the fact that I'm alive! Surely to shoot me down would be a solution for it, but not quite a solution for me.
Nevertheless the best solution if programming would be -if possible- to terminate the program, correct the error, and try again.
The program is controlling its own control-flow,
Yes.
But in DbC we take a rather extreme view of program control flow: there is the normal program flow (desirable OO and structured); and there are exceptions as a safety belt if anything detectable goes wrong. This non-negotiable separation, helps us to think clearly about program correctness, and provides a powerful methodology for preventing programs to hurt themselves, hence increasing their possibilities to recover from faulty situations.
and there isn't> anything you can really do, unless the OS itself can detect incorrectness> and shut down the process associated with the program (but detecting> incorrectness is theoretically impossible anyway).>
[...]> >
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)
(Fault tolerance as a result of a detected failure of a runtime assertion.)
and can be part of the well defined behaviour of a correctly> implemented program.
I understand your point, but that is not DbC approach. In there, a correctly implemented program never raises exceptions.
On the other hand a fault tolerant program, by providing redundant execution paths, may be shielded against some incorrect sub-programs inside itself, and -at the end- might also behave correctly (as you are arguing).
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.
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.
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).
Any behaviour is possible.
"Any behavior" is not acceptable. (DbC does not allow "any behavior" to occur).
An exception *MIGHT* be thrown, or> null might be returned, or any of several other possibilities.
Not in DbC (sorry, but such a thing won't ever happen there). A failure in a runnable assertion *always* raises exceptions in DbC.
In a defensive approach, anything can happen in a program. In a DbC approach, a false assertion raises exceptions.
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.
I know he is. But I completely disagree with that messy view of contracts and program behavior. Preconditions are simply *pre*conditions: they are not part of *post*conditions. Exceptions are not part of the normal program behavior (hence they can be used to signal incorrectness).
It's not something I agree with though.
Neither do I.
(IMHO,) whether something is a pre-condition or not depends on the> person writing the contract,
Correct.
and NOT on the implementation of the method> that is supposedly fufilling that contract.
Correct (and that is DbC approach).
For example, consider the following two methods:>
/*> pre-condition: s is not null.> post-condition: returns s.> */> String foo(String s) {> return s;> }>
Both methods satisfy their part in the contract: the postconditions. By definition, there is no possibility for a method to satisfy its precondition (that is up to the clients).
But notice that the> implementation for both methods is exactly the same.
Of course. The routine need only to be concerned to ensure its postconditions (always assuming its preconditions).
And yet, one of the> contracts has a pre-condition, and the other one doesn't.
That is why preconditions and postcondition should be part of the routine's interface, not its implementation.
Unfortunately most OO languages missed that essential point, and attach only prototypes to routines in interfaces (even in Ada, in which from the very beginning there was a lot of concern in separation implementation from interface, it is not possible to put these assertions in a module interface).
Therefore, I don't> think one should determine what the pre-conditions of a method are based on> the implementation of that method.
The implementation will assume valid preconditions. Nowhere it should test them.
Therefore, with a program like this:>
/*> pre-condition: s is not null.> post-condition: returns s.> */> String foo(String s) {> assert s != null;> return s;> }>
"s != null" is a runnable pre-condition. But it's also a "real"> pre-condition, in the sense that yes, this contract really does have a> pre-condition.
Correct.
The contract didn't have to be designed this way; but that's> the way it was designed. The fact that the pre-condition is runnable does> not make it any less real.
Exactly.
In standard C++/Java/Ada we are forced to put assertion testing inside the routine implementation (a very unfortunate fact). (No such problem in Eiffel.)
The problem is much (much) harder to express class invariants in those languages!
However, note that the following method also satisfies its contract:>
/*> pre-condition: none.> post-condition: returns s if it is not null, otherwise throws an> AssertionException.> */> String foo(String s) {> assert s != null;> return s;> }>
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.
So a logical possible precondition for "do_y" can be a non-NULL String argument (never an exception as an entry "argument").
That is why it is so important to separate normal behavior from exceptional behavior. Our reasoning about the correctness of programs is much (much) easier and without ambiguities.
That part I agree with.
I don't (and it is not DbC, which has been my point since the very beginning of this discussion).
But I don't agree with> the idea that this nescessarily means runnable pre-conditions are less real.>
Also you seem to consider, for some reason, that exceptions are> > part of normal program behavior (reason why you insist in not> > fully understanding what I'm saying). That, in my opinion (and in> > DbC's), is a basic misunderstanding of this mechanism.> > Its existence is exactly to allow us to think and use two> > separated worlds: normal program behavior and exceptional> > behavior. In the former our primary objective is to reach> > correctness. The latter aims at robustness, either for a> > faster detection of errors and their correction, or for> > implementing fault tolerant techniques.>
See above. There's 3 worlds. Handling routine normal situations,> handling exceptional situations, and being incorrectly implemented.> Violating the pre-condition puts you in the "incorrectl implemented" world,> not the "handling exceptional situation" world.
In DbC, an runnable assertion violation puts you in the exceptional world (in fact that is the exact justification for its existence).
(...)> >
Eiffel's pseudo-preconditions that really aren't because they have> >> defined behavior.> >
A precondition is a condition expected to be observed> > before the routine starts its execution. Eiffel require clauses> > *are* preconditions (such as ensure clauses are postconditions> > and invariant clauses are invariants).>
I think Daniel is saying that Eiffel require clauses are implementation> detail which allows the programmer to cause the program to behave in certain
ways. They can be used to check pre-conditions,
require clauses in Eiffel can only be used to check preconditions.
or they can be used to enforce post-conditions.
No they can't.
They are not THEMSELVES pre-conditions;
Yes they are (regardless of the fact that they can't always express the whole intended precondition).
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).
they are tools for checking pre-conditions.
Correct.
Please understand, I'm not deriding Eiffel here, I> >> think it is good to limit preconditions precisely because we can't> >> reason about the behavior of programs when they are not met (and we need> >> to be able to reason about the behavior of our programs.)> >>
Here, I've been talking about real preconditions, ones that> >> > > require being met or we cannot determine the outcome.> >> >
All preconditions (postconditions, invariants and other assertions)> >> > are required to be met in correct programs. So I think I've been> >> > talking about the same things.> >> >
DbC prescribes a very simple and precise behavior.> >> > If you have alternative practical proposals -based on> >> > the mathematical foundations in which most likely> >> > we all agree- I'm all ears.> >>
And therein lies the rub. "the behavior cannot even be considered" (you> >> know, that part you agreed to above?) means there is no solution, nor> >> can we even consider one.> >>
As such we must do our best to remove preconditions in our code and> >> provide well defined results for anything detectable.> >
No.>
Yes.
No.
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.
So, if you take that argument as a reason to weaken preconditions beyond reason, then you are in the defensive programming domain.
Should my car accept sand as fuel? (It would be a weaker precondition.)
Is the use of sand as fuel a defined behavior (just because someone wrongly choosed to include such a possibility in an incorrect precondition)?
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).
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. What would be profoundly wrong was not to make perfectly clear the contract, with a clear separation of obligations and benefits (for both sides).
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?).
Eiffel does this by creating a postcondition,> >
No it doesn't.>
I won't get into this, since I think it's just a matter of choice of> words. You're both right.
Preconditions and postcondition apply to different program parts (client and supplier), so to move conditions from one place to the other, is not simply a question of words (how can can a bad thing that happens in a program point be propagated ahead to another place which is after an alleged execution that will never occur, precisely because that bad thing has happened?).
"if a parameter is a certain value an> >> exception will be raised."> >
That is not part of the postcondition (how could it> > be if the routine did not start its execution!).> > It is the exceptional behavior as a result of a failed> > precondition.> >
I'm fine with that, it's a great idea, but it> >> isn't a precondition> >
Of course it is. It was a condition expected> > to be verified by the client *before* (as in *pre*)> > the routine starts its execution. In this respect,> > it is irrelevant if the error was found statically> > or at runtime.> >
(References please.)> >
(which is a good thing, because preconditions are bad.)> >
?> >
Preconditions, postconditions, invariants and other> > assertions are good. They make it quite clear> > what are the contracts expected, and they give> > us an idea of the meaning of programs (reusing> > Floyd's article title).>
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.
It is not because we would like black boxes out of which we could get anything we want for any input (precondition: true), that makes preconditions bad (without the "'s).
Preconditions are everywhere. Without them no one would know its responsibilities when using something.
It is impossible to build machines without preconditions. It is impossible to use machines without postconditions.
In programming we are constantly being clients *and* suppliers.
<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.
We should have as few preconditions as reasonably possible (we should> break as few vases as possible),
If you didn't have the "do not break de vase" precondition, you could easily do the bad thing (break it) without being aware of the terrible mistake.
but any pre-conditions we are unable to> eliminate, we should should clearly specify (we should admit whenever we> break a vase instead of lying about it).
Yes, preconditions should be explicit and clear.
Anyway, it seems to me that you are, perhaps, confusing programs as a whole (which should be as bullet proof as possible) with their internal modules (which should be build to do useful work correctly).
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).
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:440712A1.1B61BC5C@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.>
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.
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".
Miguel Oliveira e Silva 2 March 2006 23:13:40 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:440712A1.1B61BC5C@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.> >
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
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.>
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.
Dmitry A. Kazakov 2 March 2006 23:27:57 [ permanent link ]
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 { ... }
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:440751F4.9EF8D419@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.>>
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:
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.
[ 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 ...
Miguel Oliveira e Silva 3 March 2006 19:47:36 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:440751F4.9EF8D419@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.
Oliver, DbC prescribes a very simple (but not simplistic) view of programming. Modules (classes) should be built with clear and explicit contracts. Contracts express the module correctness assertions. Hence a broken contract is always the manifestation of an incorrect program (we most likely agree to this point).
So what DbC imposes is that a false runnable assertion *always* raises an exception (ok?). It does not strictly forbids you from using exceptions for other objectives. No language, even Eiffel, cannot prevent such a thing to happen (although in Eiffel, due to its disciplined exception mechanism, the programmer is forced to do that explicitly on purpose avoiding same nasty program errors).
However, a wise programmer, should not use exceptions to deal with situations that can easily be dealt with normal language instructions, or else sooner or later he no longer will know if exceptions are exceptional, or simply another normal control structure (= goto).
We can identify two important situations where exceptions are the appropriate tool: (internal) broken contracts, and abnormal behavior due to external unpredictable erroneous situations (not completely controllable by the program). This last case can be thought as been an external broken contract (for example, after testing the existence of a file inside a program one expect the postcondition that the file exists!). In the latter case, a reliable program will need to use the exception mechanism to ensure the correctness of the program (my mistake for not remembering, in previous messages, this rare but, nevertheless, important situation).
[...]> >> 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,
Not at all.
They can be fully DbC compliant (why shouldn't they?).
because of the "dirt" leaking in.
But it does not "leak out" (that's the point), so external clients will use a 100% compliant DbC module.
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.
I'm not sure you completely understand "my" definition of DbC. DbC is a sufficient methodology to build programs. You are free to put, or remove, conditions from contracts.
My advise is that contracts should be reasonable, and assertions should express only correctness program conditions (not exceptional behavior as a result of a program error). If you do want to put exceptional behavior inside contracts (which may not go against DbC), DbC just requires that you don't mix different assertions, and that a false assertions always raises exceptions. Preconditions cannot be expressed as postconditions.
(e.g. one where exceptions are allowed as part of the normal behaviour of> the module).
You mean: when an exceptional behavior is a normal behavior?
(Why call it exceptional then?)
Please don't think I'm playing with words. Exceptions exist for a reason. And that reason is not to replace goto's.
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.
No it doesn't.
(See my response to that in the end of this message, when I compare your contracts A and B).
If you> have a pre-condition "A", then the information you have is "If A is false, I> don't know what happens."
If it is runnable, an exception is raised (that is not undefined behavior: it is simply an *exceptional* behavior).
If you have a post-condition "if A is false, car> fails", then you now have more information than before, and that's better.
Oliver, it is impossible to express all the conditions that should not happen (sand/water/rocks/.../garbage as fuel), so your suggestion is absurd. In contracts, one is bound to express what is required at the start (precondition) and what will arise in the end (postcondition).
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.
Take a little time to think about it. You'll surely see that's absurd.
The (runnable) precondition (fuel = gasoline) is more than enough to completely protect the car against other absurd types of fuel.
The problem with the "no sand" precondition
There is not a "no sand" precondition (where did you saw such a thing?). The precondition is (fuel = gasoline).
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).
Of course. In the gasoline car abstraction, sand will never be an acceptable fuel.
You can, however, create ADTs for more abstract cars. In fact we can simply use the static type system to solve the fuel problem with generic classes:
class CAR[F -> FUEL_TYPE]
feature
fuel_capacity: DOUBLE; -- maximum amount of fuel possible
fuel_capacity > 0; fuel_level >= 0 and fuel_level <= fuel_capacity;
end -- CAR
The class FUEL_TYPE would be an abstract class from which all types of fuels would be required to inherit in order for a CAR to be used.
BTW, in Eiffel CAR[F -> FUEL_TYPE] means that CAR is a generic (template) class which only accepts as type parameters types descendant from FUEL_TYPE (hence it is ensured statically that no SAND will ever be used as fuel, unless in the future such a propelling fuel is invented).
But if the engineers are confident that they can guarantee that a car> will ALWAYS fail to run when given sand as fuel,
Of course, in my initial analogy, the precondition was (fuel = gasoline).
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.
(Not again, please!)
The condition *precedes* the usage of the car, hence it is a *pre*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.
The supplier is only required to do things *iff* preconditions are met. With a false precondition he can do whatever he likes, including nothing. The contract will fail due to the client's responsibility.
Look, here's a method which solves the halting problem, a> >> problem famous because it is known to be impossible to solve.> >>
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.
(Who is playing with words now?)
A false precondition (as you recognize below) cannot be met. And that fact is not *besides the point*. It is *the* point. (That is why there is a "pre" in preconditions and a "post" in postconditions.)
(Perhaps the way you view this problem results from that messy way of viewing contracts, in which you insist in putting broken preconditions as defined behavior in postconditions.)
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).
(Only in your apparently confused mind.)
The supplier knows that the precondition is never met, so he can do anything he wants inside the routine. He also knows that nobody will never try to see if the postcondition is met (so he's off the hook).
In case you haven't notice, postconditions are not evaluated alone. They are an implication from preconditions. This basic causal relation is not "besides the point".
Just because something is "unusable" doesn't mean it "does nothing".> Consider this alternate implementation of the same contract:>
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*.
*Only* a beginner programmer could say that. Preconditions cannot be ignored by clients. A client will never know if the supplier mets the postcondition.
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.
When you put a false precondition in a solveHaltingProblem routine, saying that it solves the problem, then you are indeed saying that incorrect program = 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.";> }
(Same thing.)
This method also satisfies its part of the contract,
The contract is, by definition, broken if any client attempts to use this function. The responsibility for this very unfortunate fact belongs to the client. It is absolutely irrelevant the conditions expressed in *post*conditions.
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;
By definition, with a false precondition, we will never know if the supplier is incorrect (before that, the client has already be proved guilty).
I claim it is completely free of bugs.
Really! A false precondition = free of bugs. (I'm always learning new extraordinary things.)
It does exactly what it is intended to do!
Of course: a program error (which is *not* to print "Hello World", then halting problem, or whatever you might like to put *after* a false assertion).
The exact same thing is happening with my "solve halting problem"> function above.
Exactly.
It does exactly what it intends to do, which is to solve the> halting problem, given certain assumptions and pre-conditions.
(Think again.)
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.
Correct.
The fewer assumptions the implementor makes, the> more robust the implementation.
Not quite.
If we can weaken preconditions and still do useful work (which rules out: raising exceptions), then the routine will be able to serve correctly wider requests from clients (which is good, because it will be a more general routine).
However, the problem is that almost always, a routine cannot do useful work out of every request from clients. The robustness problem arises precisely from this problem (to request impossible things to routines). Reliability is ensured when *everyone* mets their part of the contract (not just the clients, but also suppliers).
So a reliable program is one in which all assertions are either met (correctness), or, if tested to be false, raises an exception (DbC view of broken contracts).
What you don't seem to fully understand is that, no matter what, when one wants to do useful work inside a routine there is almost always a precondition involved, and the problem is not the existence of the precondition, but the limitations we have to put things to work correctly. That was the case of "sqrt", "inverse", the car analogy, and almost any other problem you might think off.
For same reason that escapes me (but I keep an opened mind[*]) apparently you seem convinced that because you put in a postcondition an exceptional behavior, there is no precondition involved. The precondition is there, it is simply hidden in the wrong place, with nasty consequences (see the end of the message).
[*] but not so open that my brain fall out. (to cite Carl Sagan)
Therefore, the LESS pre-conditions, the more> robust the program.
The better contracts are met by everyone the more correct (so also more reliable) the program is (which is not the same as less preconditions).
The more you allow false assertions to not raise exceptions, the less robust your program is.
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,
It is not.
The contract is part of the public ADT of a module. Once established, the clients know their part of the deal and are also ensured on the module's responsibilities.
That is why contracts are required to be inherited by descendant classes (Eiffel does that by default, in other languages it required much more handmade bookkeeping work by the programmer to approach that essential behavior).
A contract cannot be changed lightly, neither by clients nor by suppliers (or else correctness problems will arise).
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.
It was not me who putted as an (absurd) example a routine with a false precondition, so it is not up to me to answer that question.
But I guess it's useless to proceed further with this until we define> metrics for "good" and "bad" for the supplier and client.
They are very easy to define. All true assertions are good, and all false assertions are bad.
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.
Correct.
It is GOOD for the supplier if he can find a client who will agree to a> contract with the supplier.
Correct.
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.
That's irrelevant.
Since the false assertion is a precondition, that routine is simply bad for clients (that's why they shouldn't use it).
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.
True.
English isn't as rigorous as one would like, so you will often find> people disagreeing about the meanings or definitions of terms.
Agreed.
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.
Same with me.
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.
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).
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.
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)?
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.)
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?)
Again, DbC approach is much simpler and safe: any detected false assertion raises an exception.
<ggroups@bigfoot.com> wrote in message news:1141384594.168474.257720@z34g2000cwc.googlegroups.com...> 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 ...
The problem with this is that if it were true that it's best for the supplier to implement components with the strongest pre-conditions and the weakest post-conditions, then no one would ever implement any component except for this one:
But why isn't that what we observe in real life? Because there's an ADDED factor that a supplier will not want to waste his/her time implementing a component that no one wants to use! That's the point I was trying to make.
Dmitry A. Kazakov 3 March 2006 21:07:35 [ permanent link ]
On 3 Mar 2006 03:16:34 -0800, ggroups@bigfoot.com wrote:
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 ...
Maybe, but there is also somebody who assigns suppliers and consumers, responsible for the problem decomposition. Now the question is, when the negotiation takes place. Before or after the roles have been identified?
Miguel Oliveira e Silva 3 March 2006 21:13:15 [ permanent link ]
"Dmitry A. Kazakov" wrote:
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.
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?).
One part is> the precondition of do_y, another is a conjuction of the preconditions of> all enclosing catch-blocks.>
Consider:>
{> 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.
Correct.
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).
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.
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.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:44087328.68DFA1A7@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.
A does not specify what will happen if s is null. 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 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.
That is why I say any client who accepts contract A will also accept contract B.
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. 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. If this is a problem for the client, the client should negotiate a new contract where the desired behaviour is made explicit.
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.
Exceptions do not indicate that class invariants might have been violated. This fear is unjustified.
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. 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.
The reverse is not nescessarily true.
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.
Dmitry A. Kazakov 3 March 2006 21:46:03 [ permanent link ]
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?
Miguel Oliveira e Silva 3 March 2006 23:42:08 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:44087328.68DFA1A7@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.)
In article <16nNf.9996$dg.1352@clgrps13>, "Oliver Wong" <owong@castortech.com> wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message > news:4405BE7C.41AD05C9@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.
Miguel Oliveira e Silva 4 March 2006 19:17:03 [ permanent link ]
"Daniel T." wrote:
In article <16nNf.9996$dg.1352@clgrps13>,> "Oliver Wong" <owong@castortech.com> wrote:>
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> > news:4405BE7C.41AD05C9@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.
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.
Miguel Oliveira e Silva 4 March 2006 20:17:10 [ permanent link ]
"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").
Dmitry A. Kazakov 4 March 2006 23:26:36 [ permanent link ]
On Sat, 04 Mar 2006 17:17:10 +0000, Miguel Oliveira e Silva wrote:
"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).
Yes, this is the crucial point of disagreement. A program cannot be correct in one of its states and incorrect in another. Correctness applies to all possible states of a given program. So any shared states don't count.
That is why we should never use exceptions to do normal useful> work in programs (using exceptions as goto's).
We should. The use case of exception is when the implementation logic sufficiently deviates for some program states. These states are usually called exceptional, but this does not attribute the program correctness, but merely the states. A typical example is, when decomposition prevents handling of some situations in definite contexts.
For instance, when Read determines file end it raises an exception. Read cannot and should not handle file end. It does not have the information necessary for it. To give this information to Read, would be a very fragile design. So the design decision is to handle this state outside Read. This does not make Read or finite files incorrect. Note that a return code for Read would do exactly same, but much less safer and efficient. It is not an alternative.
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.
Exactly this could happen if the transfer is synchronous. Exception propagation has side effects which change the state. Asynchronous transfer does not have side effects. Which also implies that there is no reasonable way to continue the aborted thing.
On the other hand, to detect false assertions one> is required to observe the programs internal state.> That is what assertions do.
Here we go again. To observe state /= to observe correctness. State observation is a contracted behavior. Any program is just a FSM. And what a FSM does? It observes its state and goes to another one.
That assertions indeed observe states is the point Daniel, Oliver and I are defending. They don't check correctness.
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?
Technically it breaks at the first point where it can.
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).
It only appears so. You can use anything you want for correctness checks, if you can be sure that the program being checked does not influence one that checks. Exceptions as they are implemented in all languages I know, are unsuitable for this. ATCs, system traps, calls to supervisor are much better. Physically separated systems are even more better.
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!).
No. I only claim that a program cannot know anything about its correctness. About itself, a program knows its state.
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!
The definition of structured programming isn't that formal. You cannot point at a program and say whether it is well structured or not. There are cases where gotos are better structured than if-then-else.
Still exceptional behavior is a behavior. Program error is not.>
Exceptional behavior is the behavior a incorrect program> should take.
[ Don't you see any problems with such statements? ]
A correct behavior of an incorrect program? Would an incorrect program become correct by raising an exception? Was it incorrect before it raised the exception? But it is definitely correct after doing that! Ah, maybe, it was always correct except for a negligible period of time required to raise an exception? Wouldn't be any program correct if we implemented it as:
procedure Foo is begin raise Oh_My_God; end Foo;
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);
Isn't is so, that any program is correct as long as it keeps on coping with?
Kenneth P. Turvey 5 March 2006 03:23:35 [ permanent link ]
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Fri, 03 Mar 2006 18:32:06 +0000, Oliver Wong wrote:
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.
I should note that this isn't strictly true. Client B must do some work to check to see if the constraint is violated. This may be a small amount of work or a large amount of work, but it will take time. Client A will get the same answer on every case in which it is defined and will perform better than client B.
- -- Kenneth P. Turvey <kt-usenet@squeakydolphin.com> Phone : (314) 255-2199
Miguel Oliveira e Silva 5 March 2006 22:04:39 [ permanent link ]
"Dmitry A. Kazakov" wrote:
On Sat, 04 Mar 2006 17:17:10 +0000, Miguel Oliveira e Silva wrote:>
"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).>
Yes, this is the crucial point of disagreement. A program cannot be correct> in one of its states and incorrect in another.
The program as whole no (by definition). But *parts* of it may (and most likely are if the program is modular).
A flat tyre in my car does not affect its engine!
Correctness applies to all> possible states of a given program.
The total correctness of program, of course.
So any shared states don't count.
(Think again.)
That is why we should never use exceptions to do normal useful> > work in programs (using exceptions as goto's).>
We should. The use case of exception is when the implementation logic> sufficiently deviates for some program states. These states are usually> called exceptional, but this does not attribute the program correctness,
In DbC's programmers *choose* to use them (mainly) for that purpose.
but merely the states.
And the *programmer* knows that some states show, without doubt, program incorrectness (those in which assertions are false). Hence he uses (in DbC) exceptions to deal with that situation.
A typical example is, when decomposition prevents> handling of some situations in definite contexts.>
For instance, when Read determines file end it raises an exception.
I've already mentioned these group of problems.
A program should be ruthless in its internal contracts. Since it it its own responsibility to ensure them. Hence whenever an internal contract is broken (false assertions) we are in the presence of a program error.
However, when dealing with external entities (whose behavior cannot be controlled by the program), one should use a defensive programming approach (just because it may be normal that a program expecting to read something out of a file, receives a end-of-file. That is not a program error, it is simply an external broken contract).
To implement this defensive behavior, one can, mainly, use one of two approaches:
1. Normal control instructions.
Using your example, instead of "read" services, one uses "tryToRead" services. A command-query separated interface might look something like these:
class IO
feature
try_to_read_integer is require file_opened; file_opened_to_read;
last_read_successful: BOOLEAN
last_read_was_an_integer: BOOLEAN
last_integer: INTEGER is require last_read_successful; last_read_was_an_integer
end_of_file: BOOLEAN
(...)
end -- IO
(As you can see, exceptions are not required for normal behavior)
) is to take an expected behavior from outside entities as an external contract being broken.
This type of broken contracts, are much easier to rectify because they are not the expression of a program error.
--
All these being said, I still cannot understand your point in this discussion.
Read cannot and should not handle file end. It does not have the information> necessary for it. To give this information to Read, would be a very fragile> design. So the design decision is to handle this state outside Read. This> does not make Read or finite files incorrect. Note that a return code for> Read would do exactly same, but much less safer and efficient. It is not an> alternative.
Agreed (but my num. 1 proposal is).
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.>
Exactly this could happen if the transfer is synchronous.
What?
Only if the programmer chooses to do a very bad thing in catch blocks (to use Ada/C++/Java terminology).
Exception> propagation has side effects which change the state. Asynchronous transfer> does not have side effects. Which also implies that there is no reasonable> way to continue the aborted thing.
Really?
So in your opinion a new big-bang is *always* required.
(If I've used that strategy to my sixteen year old car, I would have bought quite a few hundreds of cars by now.)
On the other hand, to detect false assertions one> > is required to observe the programs internal state.> > That is what assertions do.>
Here we go again. To observe state /= to observe correctness.
(Here we go again.) To observe state = to observe incorrectness if the programmer correctly uses DbC (I've already showed you a few examples, so I don't know why you keep insisting at this point).
State observation is a contracted behavior. Any program is just a FSM.
As a result of a program constructed and defined by programmers.
A DbC programmer puts *redundant* assertions in the code to detect incorrect states (beats me why this is so difficult to understand by you).
And what a> FSM does? It observes its state and goes to another one.
So?
What is your point?
Are you saying that a FSM machine cannot be built to protect itself from some nasty, non intended behaviors?
(That's simply absurd.)
That assertions indeed observe states is the point Daniel, Oliver and I are> defending.
That they, doing precisely that, can detect incorrect states has been my point (and DbC's approach).
They don't check correctness.
They can detect incorrect behaviors, hence they check the correctness of different parts of programs. They just are unable to ensure total program correctness.
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?>
Technically it breaks at the first point where it can.
So, technically, it could be after launching the nuclear missile?
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).>
It only appears so. You can use anything you want for correctness checks,> if you can be sure that the program being checked does not influence one> that checks.
That's why *pre*conditions (and invariants) are so important (and not expressible as postconditions). Any runtime checked precondition *precedes* nasty behaviors.
Exceptions as they are implemented in all languages I know,> are unsuitable for this.
They are not, if exceptions are throwned *before* the program begins an erratic behavior.
ATCs, system traps, calls to supervisor are much> better. Physically separated systems are even more better.
We agree in this last point.
I just don't see what that was to do with the runtime use of DbC inside programs, and why runtime preconditions are not tested preconditions.
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!).>
No. I only claim that a program cannot know anything about its correctness.
(Really?)
So a module, that is expected -for example- to solve in R a second degree equation, cannot ever know if the values of "a", "b" and "c" have real roots. I would say that a simple (run time) calculation of the sign of (b^2-4ac) was more than enough.
So, in my book, if another part of the program is attempting to use that module expecting that such a solution exists, it is making a program error. You apparently don't think the same way (or you are simply playing with words).
On the other hand, if you are claiming that a program *by itself* lacks intelligence to be aware, for example, of its own existence or its correctness. That's obvious (and quite beside the point). The *programmer* knows what are the expected correct behaviors and enforces them by the use of contracts. Run time verifiable contracts, when broken, ensure the programmer that the program is incorrect, and so he can chose to build its programs to respond to such errors, or simply to let the program die gracefully.
About itself, a program knows its state.
Exactly. That is more than enough for runtime assertions to detect some incorrect programs.
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!>
The definition of structured programming isn't that formal.
I'm using Dijkstra's approach ("Structured Programming", page 19, 1972, Academic Press). Do you have a better suggestion?
(Yes, I'm also know Knuth's articles on the subject.)
You cannot> point at a program and say whether it is well structured or not. There are> cases where gotos are better structured than if-then-else.>
Still exceptional behavior is a behavior. Program error is not.> >
Exceptional behavior is the behavior a incorrect program> > should take.>
[ Don't you see any problems with such statements? ]
No.
A correct behavior of an incorrect program?
Exactly.
Would an incorrect program> become correct by raising an exception?
No (obviously).
(Where did I said that?)
Read more carefully, and stop playing with words. The appropriate (->correct) behavior a program should take as a response to detected incorrect state, is to raise an exception. If, resulting from that exception, the program is able to recover -correcting itself by the use of redundant code- or not (case in which it should terminate its execution), is a problem up to the programmer.
Was it incorrect before it raised> the exception?
If the exception was a result of a false assertion, yes.
But it is definitely correct after doing that! Ah, maybe, it> was always correct except for a negligible period of time required to raise> an exception? Wouldn't be any program correct if we implemented it as:>
procedure Foo is> begin> raise Oh_My_God;> end Foo;
I'll take all these last absurd comments as a (funny) joke on your part.
Take a little time to understand the other "side"'s arguments, before writing fallacious absurd things.
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);>
Isn't is so, that any program is correct as long as it keeps on coping> with?
A program is correct if all its runnable and intended contracts are always observed.
"Oliver Wong" <owong@castortech.com> wrote in message news:CX_Nf.8680$Ui.8021@edtnps84...
<ggroups@bigfoot.com> wrote in message > news:1141384594.168474.257720@z34g2000cwc.googlegroups.com...
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 ...
The problem with this is that if it were true that it's best for the > supplier to implement components with the strongest pre-conditions and the > weakest post-conditions, then no one would ever implement any component > except for this one:
Because the component *does nothing* . Therefore it is of no use. It is not real life, regardless of its contract.
<quote> The art is to find the weakest possible [precondition] and the strongest possible [postcondition] that characterize your intent as to what the module is supposed to do. </quote>
The quote captures the essence of the problem.
Because there's an ADDED factor that a supplier will not want to waste > his/her time implementing a component that no one wants to use! That's the > point I was trying to make.
But you have not made that point at all IMHO. A supplier generally builds components that is intended for some use.
What determines whether a potential user will use that component ??
a) preconditions too strong (usage too constrained) b) postconditions too weak (no observable measures of correctness)
Component contracts evolve through usage and horse-trading. Not in a vacuum.
"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message news:5966u7leuxqk$.nuv7gvvewc93.dlg@40tude.net...
On 3 Mar 2006 03:16:34 -0800, ggroups@bigfoot.com wrote:
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 ...
Maybe, but there is also somebody who assigns suppliers and consumers,> responsible for the problem decomposition. Now the question is, when the> negotiation takes place. Before or after the roles have been identified?
Negotiation should be ongoing, based on the realities. Which means before/during/after identification of such "roles" .
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message newsan.2006.03.05.00.23.32.448064@squeakydolphin.com...> On Fri, 03 Mar 2006 18:32:06 +0000, Oliver Wong wrote:>
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.>
I should note that this isn't strictly true. Client B must do some work> to check to see if the constraint is violated. This may be a small amount> of work or a large amount of work, but it will take time. Client A will> get the same answer on every case in which it is defined and will perform> better than client B.
I'm not sure I understand what you're saying. Which constraint are you referring to when you say "client B must [...] check to see if *THE* constraint is violated" (emphasis added)?
AFAICT, the set of all cases which contract A defines is a subset of the set of all cases which contract B defines. If a client is willing to accept contract A, then that client is only interested in those cases, and should thus receive similar performance with both implementations A and B.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:4408AA20.637854AC@det.ua.pt...> Oliver Wong wrote:>
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message>> news:44087328.68DFA1A7@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 don't dispute the above, nor do I concede to it for now, because you may be making extra inferences from the phrase "using a disguised goto" that I'm not aware of yet.
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.
I dispute this claim, however. First of all, I mentioned there are 3 behaviours earlier in the thread (though they were called 3 "worlds" at the time). There's the normal path through the program, and then there's the error handling path through the program, and then there's the situation where the program is incorrect, and so the runtime, OS, framework, or whatever it is that can detect this error, intervenes and takes appropriate action (by halting the program, or triggering an exception within the program, etc.)
Second of all, I already gave examples of where throwing exceptions are desired parts of the contract, earlier in this thread, where you can not handle them in a "better" way. Writing a robust plugin framework, where the host application won't die because one of the plugins was incorrectly implemented is one example. Implementing a unit-testing framework (e.g. jUnit) was another one.
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).
Yes, I know that that is what you are arguing. And yes, it'd be nice if the world were such that it's possible to restrict exceptions to ONLY signal program error on the responsibility of the client. That would eliminate this argument, and make our lives simpler. However, I don't think that's the way the world is. So we can either deal with it, or we can pretend it doesn't exist (or just say "99.9999999% of the time, it won't happen, so don't worry about it").
B's contract does not guarantee the object's> invariant when the exception is throwned. A does.
I believe B's contract DOES guarantee the object's invariant (because the object's invariants always hold before a given method invocation, and after the end of that invocation). But if you don't think so, then let's just fix the contract so that it's clear to both of us.
/* Contract B:
pre-condition: none. post-condition: if s is null, throws NullPointerException; otherwise, returns s. In either cases, will not violate this object's class invariants, will not cause your car to explode, will not kill innocent children, etc. */
Thinking better, A ensures even more than the> invariant. It guarantees that *nothing* happens> inside the object when (s==NULL).
I don't know what you mean for "nothing" to happen "inside" the object. I think with contract A, the pre-condition test (might) happen "inside" of the object. I'm guessing maybe you mean that the apparent state of the object will not change with A. Well, if that's the desired behaviour, we can add that to the contract too.
/* Contract B:
pre-condition: none. post-condition: (*) if s is null, throws NullPointerException; also ensures that the apparent state of the object does not change. (*) otherwise, returns s. (*) In either cases, will not violate this object's class invariants, will not cause your car to explode, will not kill innocent children, etc. */
A does not specify what will happen if s is null.>
If it is DbC and a runnable assertion: an exception> will be raised.
That's not true, because it's possible to turn off the pre-condition checking, right? That's what I thought I read from you elsewhere in this thread. So you are not sure whether or not exceptions will be raised. It depends (I guess) on your compiler or VM settings.
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).
My interpretation of pre-conditions were that there was NO guarantee that the object's state won't be modified just because the pre-condition did not hold. However, if that's your definition of pre-condition, that's fine. My original claim was that it's always possible to create a contract B from contract A, and so if you claim that information is missing from contract B: fine, I'll just add it (and have done so already, above).
That is why I say any client who accepts contract A will also accept>> contract B.>
Wrong.>
(Unmodified object.)
I've corrected it now.
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.
If you feel this way, why do you think it's not absurd to express the non-behaviour of "don't modify object state"?
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.
Er... Didn't you just say earlier that it's absurd to not express non-behaviour?
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).
No, that's not true. If the contract says "nothing nasty has been done", then if we assume that the implementor correctly fufilled the contract, then you can assume that "nothing nasty has been done".
If the implementor is NOT correctly fufilling the contract, then you've got other, much bigger, problems.
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?).
Let me call the version of A which specifies that NPE will be raised A'. Then this is what results:
/* A: pre: s != null. post: returns s.
B: pre: none. post: (*) when s == null, throws NPE. (*) otherwise, returns s. (*) Doesn't violate object constraint, kill children, etc.
A': pre: s != null. post: (*) when s == null, throw NPE. (?!) (*) otherwise, returns s. (*) Doesn't violate object constraint, kill children, etc. */
Notice that the implementor of A' doesn't actually have to throw NPE, because he can assume (based on the pre-condition) that s != null. In other words, A' is a buggy contract. You must fix the contract before you can begin implementing a method that fufills it.
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.
The post-conditions are not nescessarily "tested after the fact". They are not nescessarily "tested" at all. A post-condition simply tells the client what they can assume about the behaviour of the method. "testing" and "contracts" are two different concept, contracts often facilitate testing.
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).
I don't know what programming languages you're thinking of, but in most programming languages I work with, exceptions do not randomly change the state of objects or anything like that. So if your method doesn't change the state of an object, and your method throws an exception, then also the state of the object hasn't changed. That's the case with the start implementation of contract B:
public String foo(String s) { if (s == null) { throw new NullPointerException(); } return s; }
This method will NOT change the state of the object it's associated with, regardless of whether or not an exception is raised. Of course, if you do something silly like:
public String sillyFoo(String s) { if (s == null) { this.state = changed; throw new NullPointerException(); } return s; }
Then yes, the state will have changed. But that's because the method was coded incorrectly.
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?
Mentioned earlier in the thread. Robust plugin-framework, and unit-testing-testing.
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?
If the client wants useful work (whatever that might be), why doesn't he just implement it all himself? Fine, let's say he does that at first. So now he has one big monolithic function. Then he decides to refactor the code to break the function down into smaller methods, and encapsulate state together with behaviour into classes and objects, etc. Suddenly, now the piece of code that raises the exception is contain in a different method that the original one.
I claim that if there is no useful work out of> that exception (other than error handling)> then *it is* a disguised precondition.
My definition of pre-condition is a condition which must be true to ensure the correct behaviour of the module on which the pre-condition applies. Let's look at the implementation of contract B again:
/* B: pre: none. post: (*) when s == null, throws NPE. (*) otherwise, returns s. (*) Doesn't violate object constraint, kill children, etc. */ public String foo(String s) { if (s == null) { throw new NullPointerException(); } return s; }
Now, does this method "foo" behave incorrect when s == null? No, it CORRECTLY throws an NPE when s == null (I say "correctly" because that is the desired behaviour from the client). Since it behaves correctly when "s = null", that shows that "s != null" is NOT a pre-condition of the method.
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.
Not nescessarily. I've repeated the examples several times now (plugin framework, unit-testing framework)
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).
It's only wrong in that you and I had a different understanding of what pre-conditions mean. I've thus shown that even using YOUR definition of pre-condition, the claim is still true.
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.
When you say "A client expecting B will easily accept A, if it is ensured that a precondition failure raises that very same exception.", that is equivalent to saying "A client expecting B will easily accept A, if false.", which is logically (but trivially) true. However, it doesn't detract from my argument. Recall that in logic "F -> T" is true, and "F -> F" is true, where "->" is the "implies" operator.
Miguel Oliveira e Silva 6 March 2006 19:20:27 [ permanent link ]
Oliver Wong wrote:
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message> newsan.2006.03.05.00.23.32.448064@squeakydolphin.com...> > On Fri, 03 Mar 2006 18:32:06 +0000, Oliver Wong wrote:> >
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.> >
I should note that this isn't strictly true. Client B must do some work> > to check to see if the constraint is violated. This may be a small amount> > of work or a large amount of work, but it will take time. Client A will> > get the same answer on every case in which it is defined and will perform> > better than client B.>
I'm not sure I understand what you're saying. Which constraint are you> referring to when you say "client B must [...] check to see if *THE*> constraint is violated" (emphasis added)?
Because the exceptional behavior is part of its postcondition (instead of being part of the precondition). Hence the routine is obligated to *always* implement that (defensive) behavior (since the precondition is true, he cannot assume that sane clients will not use the routine simple to raise an exception).
AFAICT, the set of all cases which contract A defines is a subset of the> set of all cases which contract B defines. If a client is willing to accept> contract A, then that client is only interested in those cases, and should> thus receive similar performance with both implementations A and B.
If, in contract A, the runtime checking of the (runnable) precondition is enable, yes: both performances can be similar. However, in A, the programmer may disable the precondition runtime check. In those cases, the routine will perform better (no redundant defensive check in the routine's implementation).
The decision to disable runtime assertions is up to the programmer. He knows that in DbC assertions are redundant tests in *correct* programs. So if he feels confident that all uses of contract A are correct, he can safely disable them.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:440C614B.D7963519@det.ua.pt...> Oliver Wong wrote:>
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message>> newsan.2006.03.05.00.23.32.448064@squeakydolphin.com...>> > On Fri, 03 Mar 2006 18:32:06 +0000, Oliver Wong wrote:>> >
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.>> >
I should note that this isn't strictly true. Client B must do some >> > work>> > to check to see if the constraint is violated. This may be a small >> > amount>> > of work or a large amount of work, but it will take time. Client A >> > will>> > get the same answer on every case in which it is defined and will >> > perform>> > better than client B.>>
I'm not sure I understand what you're saying. Which constraint are >> you>> referring to when you say "client B must [...] check to see if *THE*>> constraint is violated" (emphasis added)?>
Because the exceptional behavior is part of its postcondition> (instead of being part of the precondition). Hence the routine> is obligated to *always* implement that (defensive) behavior> (since the precondition is true, he cannot assume that> sane clients will not use the routine simple to raise an> exception).
Yes, but my question was what constraint is being tested?
AFAICT, the set of all cases which contract A defines is a subset of >> the>> set of all cases which contract B defines. If a client is willing to >> accept>> contract A, then that client is only interested in those cases, and >> should>> thus receive similar performance with both implementations A and B.>
If, in contract A, the runtime checking of the (runnable) precondition> is enable, yes: both performances can be similar. However, in A,> the programmer may disable the precondition runtime check.> In those cases, the routine will perform better (no redundant> defensive check in the routine's implementation).>
The decision to disable runtime assertions is up to the> programmer. He knows that in DbC assertions are> redundant tests in *correct* programs. So if he feels> confident that all uses of contract A are correct,> he can safely disable them.
Indeed, method A does slightly less than method B, so method A may be slightly faster. In this example, it's faster by a single if-statement. I don't dispute that (note that I phrased it as "Similar performance", not "exactly the same").
Kenneth seem to be implying that there may be some "expensive" constraint testing involved with B. By expensive, I assumed he meant something like a different class of asymptotic performance (e.g. turning an O(1) algorithm into an O(n) one, or something like that). That's why I was asking which constraint is it that's being tested.
"Daniel T." <postmaster@earthlink.net> wrote in message newsostmaster-982AB6.19083303032006@news.east.earthlink.net...> In article <16nNf.9996$dg.1352@clgrps13>,> "Oliver Wong" <owong@castortech.com> wrote:>
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.
From what I understand (I've never worked with Eiffel), you can turn off the checking of require-clauses in Eiffel, sort of like you can turn on or off the "assert" statements in Java. As such, I wouldn't say that it's perfectly defined, just from the source code, what an Eiffel program does if it's require-clauses are non-empty.
MAYBE those require-clauses will execute, or maybe they won't. This is why I propose it's useful (in Eiffel at least) to distinguish between runnable pre-conditions and post-conditions.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:4409BD7F.3A2ED9FA@det.ua.pt...>
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).
Yes, fine, that's how YOU feel about trying to open non-existent files. But there may exist people (clients) out there who feel differently, and WANT to be able to open non-existing files, and WANT specific exceptions to be thrown under those conditions, etc. So you negotiate a contract which they agree with.
For example, the client might say "I want to be able to open a non-existing file, only to have an exception thrown when such an event happens", or the client might say "I want to be able to open a non-existing file, and I want you to create the file if it doesn't already exist."
You can't dictate the contract for the client. You have to negotiate (even when you and the client are the same person).
[...]>
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.
If the client wants to have absolute certainty that the exception was raised before "anything" happened inside the supplier object, that can be negotiated into the contract. It's not a big problem.
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).
False, as I've mentioned in another branch of this thread.
So it is an exceptional behavior resulting> from something the client did: Hence it is a> precondition.
This conclusion is not nescessarily true since it was derived from a false premise.
Why don't you put the exceptional behavior> within the precondition (if s == NULL then> exception NullStringPointer is raised)?
Okay, let's create the contract that you are describing.
/* pre: if s == null then exception NSP is raised. post: [something] */
Now the client must, before calling this method, ensure that whenever s == null, NSP is raised. How can he do that? Notice that, this means if two clients (Client Alice and Client Bob) are both using this method, Alice must ensure that when Bob sets s == null, then an exception must be raised. Because that's what the pre-condition says. If Alice doesn't raise an exception in Bob's code, then Alice can't guarantee that the method will be have correctly for her. How can she possibly do this? Maybe by using Aspect Oriented Programming and intercepting all calls to this method, checking if s is null, and raising the exception herself.
Anyway, as you can see, there is a BIG semantical difference between:
/* pre: if s == null then exception NSP is raised. post: [something] */
and
/* pre: none. post: (*) if s == null then exception NSP is raised. (*) [something] */
So use the first one, when the desired behaviour is the first one (I can't imagine a situation where the first one is desired; it adds unnescessary burden on the client), and use the second one, when the second one is desired. They mean two different things, and a client who accepts the first one won't accept the second one, and vice versa.
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.
That is NOT what the contract says. Read it again.
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).
False, because the pre-condition checking can be turned off. Therefore, it is NOT defined what will happen when a contract is broken. You need additional information (such as what the compiler settings are).
Why do you keep attaching undefined behavior> to preconditions?
That's what most of us understand when we hear the term "precondition". Maybe you have a different definition in mind. You can say all you want that your definition is the one, true, definition, but the reality is that a lot of people take the understanding that Daniel, Dmitry and myself are taking.
When I try to communicate with you, I try to keep in mind your definition of precondition. But when I communicate with other people, I usually assume they use the same definition as I have until I discover otherwise.
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.
According to my understanding of preconditions, it is indeed a condition, but the condition doesn't "occur". Rather, it is a condition which MAY be evaluated (it is not nescessarily evaluated, depending on compiler settings, etc.) If the condition evaluates to TRUE, then the client can be confident that the program module on which the precondition applies will behave correctly. If it evalutes to FALSE, the client cannot be confident that the program module will behave incorrectly.
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).
You seem to be assuming that the real (useful) routine's intent is never to raise exceptions. I've already claimed otherwise, and now I'm further reminding you that you cannot dictate the contract to the client. Sometimes the client WANTS exceptions to be raised.
Contracts, either expressed as preconditions,> postconditions and invariants, are good> in the construction of reliable quality> programs.
Dmitry A. Kazakov 6 March 2006 21:17:03 [ permanent link ]
On Sun, 05 Mar 2006 19:04:39 +0000, Miguel Oliveira e Silva wrote:
"Dmitry A. Kazakov" wrote:>
On Sat, 04 Mar 2006 17:17:10 +0000, Miguel Oliveira e Silva wrote:>>
"Dmitry A. Kazakov" wrote:>>>
Yes, this is the crucial point of disagreement. A program cannot be correct>> in one of its states and incorrect in another.>
The program as whole no (by definition).> But *parts* of it may (and most likely> are if the program is modular).>
A flat tyre in my car does not affect its engine!
It does, but no matter. Take any part of your choice, my statement stands. Any given part of a program is a program and any given part cannot check the correctness of itself. This directly implies that it cannot check the correctness of any enclosing program was well ("Honor your father and your mother..." -)). Only an independent program can.
but merely the states.>
And the *programmer* knows that some states show,> without doubt, program incorrectness (those in which> assertions are false). Hence he uses (in DbC) exceptions> to deal with that situation.
The programmer may know, but the program cannot. Because the programmer's brain runs a different program.
A typical example is, when decomposition prevents>> handling of some situations in definite contexts.>>
For instance, when Read determines file end it raises an exception.>
I've already mentioned these group of problems.>
A program should be ruthless in its internal contracts.> Since it it its own responsibility to ensure them. Hence> whenever an internal contract is broken (false assertions)> we are in the presence of a program error.>
However, when dealing with external entities (whose> behavior cannot be controlled by the program), one> should use a defensive programming approach> (just because it may be normal that a program> expecting to read something out of a file, receives> a end-of-file. That is not a program error, it is> simply an external broken contract).
Ah, now we have mysterious "external" contracts...
To implement this defensive behavior, one can,> mainly, use one of two approaches:>
1. Normal control instructions.>
Using your example, instead of "read" services,> one uses "tryToRead" services. A command-query> separated interface might look something like these:>
(As you can see, exceptions are not required for> normal behavior)
Turing completeness...
All these being said, I still cannot understand your> point in this discussion.>
Read cannot and should not handle file end. It does not have the information>> necessary for it. To give this information to Read, would be a very fragile>> design. So the design decision is to handle this state outside Read. This>> does not make Read or finite files incorrect. Note that a return code for>> Read would do exactly same, but much less safer and efficient. It is not an>> alternative.>
Agreed (but my num. 1 proposal is).
Which is a fragile design. It is one item look-ahead. You cannot deal this way with anything more or less complex. Especially, when parsing program source code. Then it is a bad design in any case, because it publishes the internals, which are, firstly, of no interest, secondly, do not belong there (to the low-level I/O layer,) and, thirdly, inefficient as any look-ahead inherently is.
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.>>
Exactly this could happen if the transfer is synchronous.>
What?>
Only if the programmer chooses to do a very bad thing> in catch blocks (to use Ada/C++/Java terminology).
No. That may long happen before. Upon exception propagation local objects are finalized. If the program is incorrect, these finalizations *cannot* be performed, because their preconditions might be false. In fact, you don't know them, you cannot evaluate them on the context where incorrectness was detected, everything is rubbish there.
Exception>> propagation has side effects which change the state. Asynchronous transfer>> does not have side effects. Which also implies that there is no reasonable>> way to continue the aborted thing.>
Really?>
So in your opinion a new big-bang is *always* required.>
(If I've used that strategy to my sixteen year old car,> I would have bought quite a few hundreds of> cars by now.)
I'm dying to know how your car would drive without wheels.
BTW, for safety reasons the bus system used in cars is designed exactly this way. When bus controllers detect errors, they stop sending. You need to reset a controller to continue. That kills all messages queues! This behavior is quite boring, when you are monitoring the bus state and need permanently plug participants in and out. Because the monitoring system has *another* contract.
On the other hand, to detect false assertions one>>> is required to observe the programs internal state.>>> That is what assertions do.>>
Here we go again. To observe state /= to observe correctness.>
(Here we go again.) To observe state = to observe incorrectness> if the programmer correctly uses DbC (I've already showed you> a few examples, so I don't know why you keep insisting at this> point).
Because you didn't. You cannot show it, without overthrowing very basics of elementary logic. The rest is just argumentation that when the universe does not obey your theory, so worse for the universe...
State observation is a contracted behavior. Any program is just a FSM.>
As a result of a program constructed and defined by> programmers.>
A DbC programmer puts *redundant* assertions in the code> to detect incorrect states (beats me why this is so difficult> to understand by you).
Because "incorrect states" have nothing to do with "incorrect programs." When one program is incorrect that might (but need not to) be a state of another program. If anybody feels sorrow about it, then not the incorrect program.
And what a>> FSM does? It observes its state and goes to another one.>
So?>
What is your point?>
Are you saying that a FSM machine cannot be built> to protect itself from some nasty, non intended> behaviors?>
(That's simply absurd.)
You are playing with words. "Non-intended behavior" need to be defined. No any FSM machine can protect itself from what it was not programmed for. And anything it was, is the behavior.
They don't check correctness.>
They can detect incorrect behaviors, hence they check the correctness> of different parts of programs. They just are unable to ensure total> program correctness.
But that is the whole point. A program detecting an incorrectness of another program is itself correct [or more precisely its correctness is independent on that.] An exception raised or whatever it does in this *correct*, own state is its contracted behavior. End of story.
Technically it breaks at the first point where it can.>
So, technically, it could be after launching the nuclear missile?
Yes. Depending on the application area correctness might be a quite important thing. Here statical analysis goes.
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).>>
It only appears so. You can use anything you want for correctness checks,>> if you can be sure that the program being checked does not influence one>> that checks.>
That's why *pre*conditions (and invariants) are so important> (and not expressible as postconditions). Any runtime> checked precondition *precedes* nasty behaviors.
No it does not prevent wrong behavior. It already happened. You only have detected that. The consequences are unpredictable as well as their timing. Run-time correctness checks is psychotherapy, if you don't plan something reasonable to do when you discover a problem. And if you indeed do, that immediately becomes a behavior. The difference is when you are in a burning house and see on the wall someone's writing: "There must be a way out!", this would be a failed correctness check. But when you see a shield with an arrow pointing to an emergency exit, that would be a behavior.
ATCs, system traps, calls to supervisor are much>> better. Physically separated systems are even more better.>
We agree in this last point.>
I just don't see what that was to do with the runtime use> of DbC inside programs, and why runtime preconditions> are not tested preconditions.
The word "inside" is the only reason.
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!).>>
No. I only claim that a program cannot know anything about its correctness.>
(Really?)>
So a module, that is expected -for example- to solve in R a second> degree equation, cannot ever know if the values of "a", "b" and> "c" have real roots. I would say that a simple (run time) calculation> of the sign of (b^2-4ac) was more than enough.
This is not a correctness check. If you add it to the program, you also add two new states:
1. Here is R and check was OK 2. Here is R and check failed
Now you have to say what your program will do in the second case. Then I will ask you who and when will check that.
About itself, a program knows its state.>
Exactly. That is more than enough for runtime> assertions to detect some incorrect programs.
See above. This inevitably changes the program's contract and thus the definition of its correctness.
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!>>
The definition of structured programming isn't that formal.>
I'm using Dijkstra's approach ("Structured Programming",> page 19, 1972, Academic Press). Do you have a better suggestion?
Yes. Take the definition you like most. Write a style checker. Take a large source code base and run your checker through. Then take a sample of experienced programmer and let them maintain this code 1-2 years. Then compare what your checker said, what did say the programmers, and what were the maintenance costs. The rest is statistics.
A correct behavior of an incorrect program?>
Exactly.>
Would an incorrect program>> become correct by raising an exception?>
No (obviously).
Thus its behavior was incorrect?
Was it incorrect before it raised>> the exception?>
If the exception was a result of a false assertion, yes.
But then it suddenly became correct! Come on! It is just rubbish.
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);>>
Isn't is so, that any program is correct as long as it keeps on coping>> with?>
A program is correct if all its runnable and intended> contracts are always observed.
What does then mean "to cope with" under the condition that contracts aren't "observed." [ BTW, changing words does not help. ]
Miguel Oliveira e Silva 6 March 2006 22:31:28 [ permanent link ]
I sense, perhaps incorrectly, some animosity in this message. I also feel that I'm also to blame for that. My apologies to Oliver (and others) for that. Despite eventual harsh comments on my part (which may also exist in this message [sorry]), I'm really interested in understanding everybody's arguments (which has not been easy, because lots of different, though related, programming issues have been approached in all these messages), and -most importantly- to explain what DbC is, and the software quality reasons behind it.
To clarify things, the main points that I'm contesting are, not only what DbC really is, but also why preconditions cannot -and should not- be expressed as postconditions. I also strongly object against the idea that preconditions (or any other assertion) are bad. It is these points that I'll attempt (again) to address in this message.
Oliver Wong wrote:
(...)
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).>
Yes, I know that that is what you are arguing. And yes, it'd be nice if> the world were such that it's possible to restrict exceptions to ONLY signal> program error on the responsibility of the client.
Oliver, if you read more carefully the above cited paragraph you'll see that I was referring to that (s!=NULL) condition. That condition should be 100% to assert program correctness.
You will also see, in my last messages, that I accept that there might be other useful (hopefully rare) uses for exceptions. In particular when dealing with external entities (whose behavior cannot be ensured by the program itself).
That would eliminate this> argument, and make our lives simpler. However, I don't think that's the way> the world is. So we can either deal with it, or we can pretend it doesn't> exist (or just say "99.9999999% of the time, it won't happen, so don't worry> about it").>
B's contract does not guarantee the object's> > invariant when the exception is throwned. A does.>
I believe B's contract DOES guarantee the object's invariant (because> the object's invariants always hold before a given method invocation, and> after the end of that invocation).
Agreed. The routine implementor *can* (and should) ensure the object invariants even in the exceptional behavior (as I said, in the message you are replying to, by locating carefully the place in which the exception is throwed).
The difference is that such behavior is *ensured* in the contract A (regardless of the implementation of the routine [remember that a precondition *precedes* it]). In contract B, the programmer is required to take extremely care in the location of the exception.
In languages (such as Java/C++/Ada) in which invariants cannot be declared (only approached, by propagating them to every routine), the problem might be more serious (programmers of those languages, considering the enormous bookkeeping required to express them, usually ignore explicit testable invariants).
But if you don't think so, then let's> just fix the contract so that it's clear to both of us.>
/*> Contract B:>
pre-condition: none.> post-condition: if s is null, throws NullPointerException; otherwise,> returns s. In either cases, will not violate this object's class invariants,> will not cause your car to explode, will not kill innocent children, etc.> */
Thinking better, A ensures even more than the> > invariant. It guarantees that *nothing* happens> > inside the object when (s==NULL).>
I don't know what you mean for "nothing" to happen "inside" the object.
Exactly that: nothing.
I think with contract A, the pre-condition test (might) happen "inside" of> the object.
No. They are located in the object's *interface* (if they are to be implemented in non-DbC languages, the precondition test is required to exist in the beginning of the routine, *before* the routine starts its hopefully useful algorithm).
I'm guessing maybe you mean that the apparent state of the> object will not change with A.
The real state of the object won't change, unless, of course, the assertion is incorrectly implemented and has noxious side-effects to the program's state. Assertions are required to be declarative (no side-effects whatsoever to the programs visible state).
Well, if that's the desired behaviour, we can> add that to the contract too.
You can add the things you like to the contract, that won't change the fundamental difference between preconditions and postconditions.
Do you still think that runnable preconditions are bad and that they should be expressed as postconditions?
/*> Contract B:>
pre-condition: none.> post-condition:> (*) if s is null, throws NullPointerException; also ensures that the> apparent state of the object does not change.> (*) otherwise, returns s.> (*) In either cases, will not violate this object's class invariants, will> not cause your car to explode, will not kill innocent children, etc.> */>
A does not specify what will happen if s is null.> >
If it is DbC and a runnable assertion: an exception> > will be raised.>
That's not true, because it's possible to turn off the pre-condition> checking, right?
Right. That is why I have putted the runnable word in the cited paragraph. Whenever the programmer is not sufficiently sure about the inexistence of incorrect uses of a routine, he should not disable the runtime check of that assertion.
That's what I thought I read from you elsewhere in this> thread.
You read correctly.
So you are not sure whether or not exceptions will be raised. It> depends (I guess) on your compiler or VM settings.
It depends on the programmer (hopefully the programming environment will help him in that task) . If he fells confident that all uses of a routine are correct, he can safely disable runnable preconditions.
(In Eiffel, one can enable/disable assertions in each class.)
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).>
My interpretation of pre-conditions were that there was NO guarantee> that the object's state won't be modified just because the pre-condition did> not hold.
Activated runnable preconditions ensure that behavior.
However, if that's your definition of pre-condition, that's fine.
It is not mine. It's DbC one (I can give you many references to the subject, if you want to correctly learn about it).
My original claim was that it's always possible to create a contract B from> contract A, and so if you claim that information is missing from contract B:> fine, I'll just add it (and have done so already, above).
Your original claim is wrong. Contract B is not equivalent to A's (regardless of the things you add in the postcondition).
That is why I say any client who accepts contract A will also accept> >> contract B.> >
Wrong.> >
(Unmodified object.)>
I've corrected it now.
Sorry but it is still not enough. A routine is required to implement its postcondition. A routine that is correct, and is correctly used within a program, can safely ignore its precondition (no need to implement exceptional behavior).
You are still missing one of the key ideas of Design by Contract. Contracts should only apply to *useful* work. So modules should be constructed with the sufficient preconditions (invariants and postconditions) just to do such useful work (no more, no less). It is really illogical to weakening a routine precondition (I would say that it is unacceptable in internal contracts) to a point, in which its implementation is required to include defensively exceptional code. The correctness of programs is ensured not by true preconditions (that's wishful thinking), but by correct implementations (invariants and postconditions) *and* correct utilization of modules (preconditions).
Anyway, I feel we are repeating arguments too much. I have presented, quite deeply, the DbC methodology, its runtime behavior, and the reasons that support it. If you think that it is not the right methodology, you are, of course, in your own right to think that way (you can persist to use contract B's opposite defensive approach).
However, I must insist that you should not give the wrong names to things, as that will confuse who ever wants to learn about it.
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.>
If you feel this way, why do you think it's not absurd to express the> non-behaviour of "don't modify object state"?
Where did I said that?
You are the one who said that to justify the equivalence of passing a precondition to an exceptional postcondition.
I simply say that (correct) preconditions don't change the behavior of objects (or the remaining program). As they occur before the routine starts its execution, by definition, nothing can happen to the object's state.
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.>
Er... Didn't you just say earlier that it's absurd to not express> non-behaviour?
(If I'm not mistaken, I said it is absurd to express non-behavior, not that it is absurd to not express non-behavior.)
Nevertheless, it is quite frequent to have incomplete postconditions in which not all the behavior of the routine is expressed. So the problem is not to express non-behavior, but when the expressed behavior is incomplete. A precondition failure (in the conditions that I've already mentioned a few hundred times) ensures that the routine did not execute, regardless of possible incomplete postconditions (remember that when a precondition is false, the routine's implementation and its postcondition are irrelevant intangible points).
BTW, this situation is not as critical as incomplete preconditions. Regarding correctness, postconditions (and invariants) are only an internal problem of a module. However, preconditions are everybody's problem as they express what is expected from clients in order for a module to be correctly used.
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).>
No, that's not true.
What?
Real programs are *causal* entities. Preconditions (...) occur before routine execution.
If the contract says "nothing nasty has been done",> then if we assume that the implementor correctly fufilled the contract, then> you can assume that "nothing nasty has been done".
You are forgetting the very important practical situation in which the contract is *incorrectly* implemented.
In those cases only runnable preconditions ensure the inexistence of nasty behaviors inside routines.
If the implementor is NOT correctly fufilling the contract, then you've> got other, much bigger, problems.
That's why preconditions are so important (right?).
That is also why postconditions and invariants are important too. A postcondition/invariant failure are manifestations of incorrectly implemented modules (the appropriate behavior is also to raise an exception).
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?).>
Let me call the version of A which specifies that NPE will be raised A'.> Then this is what results:>
A':> pre: s != null.> post:> (*) when s == null, throw NPE. (?!)> (*) otherwise, returns s.> (*) Doesn't violate object constraint, kill children, etc.> */>
Notice that the implementor of A' doesn't actually have to throw NPE,> because he can assume (based on the pre-condition) that s != null.
Oliver, the (s!= NULL) precondition will ensure that behavior (postconditions are irrelevant when the precondition is false).
In other> words, A' is a buggy contract.
A' is a foolish contract. Its postcondition has something that is ensured by the precondition.
You must fix the contract before you can> begin implementing a method that fufills it.
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.>
The post-conditions are not nescessarily "tested after the fact".
What do you mean?
Are they (pre)postconditions?
They are not nescessarily "tested" at all.
Correct. The programmer may disable them if he fells confident that the module is correctly implemented (a much lesser risky option when compared to disabled preconditions).
A post-condition simply tells the> client what they can assume about the behaviour of the method. "testing" and> "contracts" are two different concept, contracts often facilitate testing.
In DbC, contracts and testing are two related concepts (it is one of its advantages). The programmer should, as much as possible, express testable contracts.
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).>
I don't know what programming languages you're thinking of, but in most> programming languages I work with, exceptions do not randomly change the> state of objects or anything like that.
Of course not (you should tell that to Dmitry, as I already did).
The object/program visible state when an exception is throwed, is the one that exists in that location.
So if your method doesn't change the> state of an object, and your method throws an exception, then also the state> of the object hasn't changed.
Of course.
That's the case with the start implementation> of contract B:>
public String foo(String s) {> if (s == null) {> throw new NullPointerException();> }> return s;> }
It is the case of *this* implementation. However, a postcondition cannot ensure that 100% (only preconditions).
A sceptical client (the wise ones) will be tempted to look at the routine's implementation just to make sure things work as expected, and that -in fact- the exceptional behavior is similar to a broken precondition. But even that is not enough. The implementation may change in the future. Contracts, in the other hand (and preconditions in particular), cannot change as they can invalidate prior uses of the module (DbC plays also an important role in the Meyer's OO principle of "modules should be both open and closed").
This method will NOT change the state of the object it's associated> with, regardless of whether or not an exception is raised. Of course, if you> do something silly like:>
public String sillyFoo(String s) {> if (s == null) {> this.state = changed;> throw new NullPointerException();> }> return s;> }
Trust me. Programmers (me included) at times do the most silly things you can imagine.
Then yes, the state will have changed. But that's because the method was> coded incorrectly.
True. But assuming that the changed state mets the object expressed invariant, this routine would be a valid one regarding B's postcondition.
With runnable preconditions, the objects state would not change.
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?>
Mentioned earlier in the thread. Robust plugin-framework, and> unit-testing-testing.
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?>
If the client wants useful work (whatever that might be), why doesn't he> just implement it all himself? Fine, let's say he does that at first. So now> he has one big monolithic function.
Not at all. The more preconditions you have the smallest (and efficient) the routine will be.
Then he decides to refactor the code to> break the function down into smaller methods, and encapsulate state together> with behaviour into classes and objects, etc. Suddenly, now the piece of> code that raises the exception is contain in a different method that the> original one.
If contracts change, everything changes. (Why does that comes as a surprise to you?)
I claim that if there is no useful work out of> > that exception (other than error handling)> > then *it is* a disguised precondition.>
My definition of pre-condition is a condition which must be true to> ensure the correct behaviour of the module on which the pre-condition> applies.
Mine too.
Let's look at the implementation of contract B again:>
/*> B:> pre: none.> post:> (*) when s == null, throws NPE.> (*) otherwise, returns s.> (*) Doesn't violate object constraint, kill children, etc.> */> public String foo(String s) {> if (s == null) {> throw new NullPointerException();> }> return s;> }>
Now, does this method "foo" behave incorrect when s == null? No, it> CORRECTLY throws an NPE when s == null (I say "correctly" because that is> the desired behaviour from the client).
Correct.
Since it behaves correctly when "s => null", that shows that "s != null" is NOT a pre-condition of the method.
But that is a defensive version of the routine. You cannot disable its exceptional behavior as it is required to be part of its implementation.
Now, if that is something a program must live with, because a module depends on external uncontrollable entities. Fine. We don't disagree completely on the approach taken. But if the contract can be completely ensured inside a program (as is the case of testing for internal NULL references), then we completely disagree (and your approach is *not* DbC, it is defensive programming).
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.>
Not nescessarily. I've repeated the examples several times now (plugin> framework, unit-testing framework)
Do those examples change, in any way, the important DbC points (mentioned in the beginning of this message) I've been trying to argue?
If they don't, you'll see that we do not disagree that much.
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).>
It's only wrong in that you and I had a different understanding of what> pre-conditions mean.
Sorry, but your notion of preconditions is wrong (at least if you insist in hiding them inside postconditions, and in saying that they are bad).
I've thus shown that even using YOUR definition of> pre-condition, the claim is still true.
It is not mine. A precondition is a condition that applies *before* a routine starts its execution. DbC preconditions are quite clear concepts.
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.>
When you say "A client expecting B will easily accept A, if it is> ensured that a precondition failure raises that very same exception.", that> is equivalent to saying "A client expecting B will easily accept A, if> false.", which is logically (but trivially) true.
(The two "A client expecting B will easily accept A" sentences are equal. Was that intended?)
However, it doesn't> detract from my argument. Recall that in logic "F -> T" is true, and "F ->> F" is true, where "->" is the "implies" operator.
So?
Are you still arguing with that absurd false preconditions routines?
Don't forget that the program behavior evolves in time. Postconditions (and the routine implementation) are at the infinite time distance from a false (detected) precondition.
Miguel Oliveira e Silva 6 March 2006 22:49:16 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:440C614B.D7963519@det.ua.pt...> > Oliver Wong wrote:> >
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message> >> newsan.2006.03.05.00.23.32.448064@squeakydolphin.com...> >> > On Fri, 03 Mar 2006 18:32:06 +0000, Oliver Wong wrote:> >> >
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.> >> >
I should note that this isn't strictly true. Client B must do some> >> > work> >> > to check to see if the constraint is violated. This may be a small> >> > amount> >> > of work or a large amount of work, but it will take time. Client A> >> > will> >> > get the same answer on every case in which it is defined and will> >> > perform> >> > better than client B.> >>
I'm not sure I understand what you're saying. Which constraint are> >> you> >> referring to when you say "client B must [...] check to see if *THE*> >> constraint is violated" (emphasis added)?> >
Because the exceptional behavior is part of its postcondition> > (instead of being part of the precondition). Hence the routine> > is obligated to *always* implement that (defensive) behavior> > (since the precondition is true, he cannot assume that> > sane clients will not use the routine simple to raise an> > exception).>
Yes, but my question was what constraint is being tested?
I think it was the (s == NULL) test which selects the exception throw instruction in the routine's implementation.
AFAICT, the set of all cases which contract A defines is a subset of> >> the> >> set of all cases which contract B defines. If a client is willing to> >> accept> >> contract A, then that client is only interested in those cases, and> >> should> >> thus receive similar performance with both implementations A and B.> >
If, in contract A, the runtime checking of the (runnable) precondition> > is enable, yes: both performances can be similar. However, in A,> > the programmer may disable the precondition runtime check.> > In those cases, the routine will perform better (no redundant> > defensive check in the routine's implementation).> >
The decision to disable runtime assertions is up to the> > programmer. He knows that in DbC assertions are> > redundant tests in *correct* programs. So if he feels> > confident that all uses of contract A are correct,> > he can safely disable them.>
Indeed, method A does slightly less than method B, so method A may be> slightly faster. In this example, it's faster by a single if-statement. I> don't dispute that (note that I phrased it as "Similar performance", not> "exactly the same").
Ok. We agree then.
Kenneth seem to be implying that there may be some "expensive"> constraint testing involved with B. By expensive, I assumed he meant> something like a different class of asymptotic performance (e.g. turning an> O(1) algorithm into an O(n) one, or something like that).
I'm quite sure Kenneth was referring to what I said (but, of course, only he can confirm that).
Anyway not all assertions are as simple, and efficient, as testing null references. Many of them can involve quite a long processing time (for example: to search for an existing element in a container). So, yes. It can make a huge difference to run programs with assertions enabled and disabled (the gnu SmartEiffel compiler is one such cases). DbC is also excellent to create efficient programs.
That's why I was> asking which constraint is it that's being tested.>
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:440C923C.BC513785@det.ua.pt...> Oliver Wong wrote:>
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message>> news:440C614B.D7963519@det.ua.pt...>> > Oliver Wong wrote:>> >
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message>> >> newsan.2006.03.05.00.23.32.448064@squeakydolphin.com...>> >> > On Fri, 03 Mar 2006 18:32:06 +0000, Oliver Wong wrote:>> >> >
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.>> >> >
[snip]
Anyway not all assertions are as simple, and efficient,> as testing null references. Many of them can involve> quite a long processing time (for example: to search> for an existing element in a container).> So, yes. It can make a huge difference> to run programs with assertions enabled> and disabled (the gnu SmartEiffel compiler> is one such cases). DbC is also excellent> to create efficient programs.>
Okay, I see my mistake now, and my statement above is incorrect, because it is too strong. So let me fall back on my previous claim (rephrased more carefully):
For any contract "A" with runnable pre-conditions, if there exists a client C who accepts contract A and uses an implementation of that contract in their program, then it is always possible to construct a contract B such that it has fewer (possibly zero) pre-conditions than contract A did, and whose implementation can swapped in for the implementation of contract A without changing the behaviour of the program (where behaviour includes asymptotic runtime).
Miguel Oliveira e Silva 7 March 2006 00:09:17 [ permanent link ]
"Dmitry A. Kazakov" wrote:
On Sun, 05 Mar 2006 19:04:39 +0000, Miguel Oliveira e Silva wrote:>
"Dmitry A. Kazakov" wrote:> >
On Sat, 04 Mar 2006 17:17:10 +0000, Miguel Oliveira e Silva wrote:> >>
"Dmitry A. Kazakov" wrote:> >>>
Yes, this is the crucial point of disagreement. A program cannot be correct> >> in one of its states and incorrect in another.> >
The program as whole no (by definition).> > But *parts* of it may (and most likely> > are if the program is modular).> >
A flat tyre in my car does not affect its engine!>
It does, but no matter.
So if I change the tyre, I won't be sure the engine is still working?
(Assuming, of course, that it was working before.)
You live in a very strange complex world.
Take any part of your choice, my statement stands.
Really!
Any given part of a program is a program and any given part cannot check> the correctness of itself.
Assertions are redundant tests that apply to valid program states to detect incorrect behaviors. The programmer chooses to put those extra tests to assert its runtime correctness. As I said many times, assertions are not part of the routine implementation. They are at a different level (the module interface in the case of preconditions, postconditions and invariants).
So your statement is wrong. The program is not testing its own correctness as a whole (including the correctness of assertions). Assertions are testing the program under them. (Remember my persistent argument about separated worlds? Assertions test the correctness of the normal program.)
This directly implies that it cannot check the> correctness of any enclosing program was well ("Honor your father and your> mother..." -)). Only an independent program can.
How many more examples do you need?
but merely the states.> >
And the *programmer* knows that some states show,> > without doubt, program incorrectness (those in which> > assertions are false). Hence he uses (in DbC) exceptions> > to deal with that situation.>
The programmer may know, but the program cannot. Because the programmer's> brain runs a different program.
And that brain (at least a wise one), in DbC, has putted inside the same running program, two "programs". One, that I've been calling normal, which is expected to do what ever is required. And another, using runnable assertions, in which the former program is tested.
A typical example is, when decomposition prevents> >> handling of some situations in definite contexts.> >>
For instance, when Read determines file end it raises an exception.> >
I've already mentioned these group of problems.> >
A program should be ruthless in its internal contracts.> > Since it it its own responsibility to ensure them. Hence> > whenever an internal contract is broken (false assertions)> > we are in the presence of a program error.> >
However, when dealing with external entities (whose> > behavior cannot be controlled by the program), one> > should use a defensive programming approach> > (just because it may be normal that a program> > expecting to read something out of a file, receives> > a end-of-file. That is not a program error, it is> > simply an external broken contract).>
Ah, now we have mysterious "external" contracts...
You can read about them in my messages. There is nothing mysterious about them. When ever a program cannot ensure some expected behavior (external entities), then the program should be defensive about it.
However, regarding the more frequent *internal* contracts (the ones to which DbC is applicable) it should be ruthless (false assertion is a program error).
To implement this defensive behavior, one can,> > mainly, use one of two approaches:> >
1. Normal control instructions.> >
Using your example, instead of "read" services,> > one uses "tryToRead" services. A command-query> > separated interface might look something like these:> >
(As you can see, exceptions are not required for> > normal behavior)>
Turing completeness...
Sorry, but I don't understand your point here.
All these being said, I still cannot understand your> > point in this discussion.> >
Read cannot and should not handle file end. It does not have the information> >> necessary for it. To give this information to Read, would be a very fragile> >> design. So the design decision is to handle this state outside Read. This> >> does not make Read or finite files incorrect. Note that a return code for> >> Read would do exactly same, but much less safer and efficient. It is not an> >> alternative.> >