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.
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.
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.
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 { ... }
[ 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 ...
<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 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 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 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:14:59 [ permanent link ]
Oliver Wong wrote:
"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).
Agreed.
Such a program is not, however, using a DbC approach (it is a defensive approach).
I'm quite sure DbC approach (when applied to internal contracts) is much (much) better in every respect. Of course I'm willing to continue to argue about that.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:440CA653.61F0D4D4@det.ua.pt...> I'm quite sure DbC approach (when applied to internal> contracts) is much (much) better in every respect. [to defensive programming]> Of course I'm willing to continue to argue about that.
Well, before we get into an argument about this, I'd better understand what I'm arguing about. ;)
What do you mean by "internal contracts"? I.e. what change in meaning does the adjective "internal" cause when applied to "contracts" in this context?
Dmitry A. Kazakov 7 March 2006 13:32:12 [ permanent link ]
On Mon, 06 Mar 2006 21:09:17 +0000, Miguel Oliveira e Silva wrote:
"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:>>>>
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?
There are pressure sensors and all that stuff.
(Assuming, of course, that it was working before.)>
You live in a very strange complex world.
Never buy a new car, it might appear steer-by-wire! -))
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.)
If the assertions aren't a part of the program, then the program does not check the correctness of itself [by means of these assertions.] Neither these assertions check the correctness of their own. Also, my statement stands.
It isn't about where assertions belong to. You are free to choose them being either out or in. Depending on the choice they either will be capable to check the correctness or not. Your argumentation is based on a simple trick. First you present one program, then after making some distracting passes, you substitute the program with another and claim that it is same and its correctnes is checked. No, it isn't.
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.
That's just fine. This is the starting point of your opponents. Once we have agreed on this, we can proceed to the next question. If the assertion evaluation program "A" detects that the acting program "B" is incorrect, what would be the proper behavior and what the word "behavior" addresses here. Our point is that it is meaningless to talk about the behavior of B at this stage. B has failed, it proved to be wrong. So exception propagation in B would be nonsence. Now it is the behavior of A. It is A's responsibility to do something about B. [ BTW, this is how all fault-tolerant systems are built. ]
(As you can see, exceptions are not required for>>> normal behavior)>>
Turing completeness...>
Sorry, but I don't understand your point here.
It is an argumentation to Turing completeness. It is true that I can program anything [Turing-complete] without exceptions (no pun -)), as well as without assertions and without lots of other stuff, people are counting for useful.
If he insists that an integer must be read,> he should take the consequences when> the file/user does not give it that.
Of course it should. That is the postcondition of Read: you either have read it or have not. It is not that you have read it or else the program is wrong.
I would say that reading characters was more than enough> (and in fact is what I do in my handmade parsers.)>
After the characters have been read, all remaining> processing is within program's *internal* (strong)> contracts.
Huh, there is practically no real language for which legality of the source code can be decided for any given program. So the approach you have outlined is wrong, or at least impracticable. Examples vary from endless C++'s /*...*/ comments and source lines to folding constant expressions like 20**9999999/20**9999999 etc. Exceptions are very handy for breaking potentially infinite recursion.
Upon exception propagation local objects>> are finalized.>
In the failed precondition object, there are> no local objects (because the routine did> not started its execution).
That is not the context where incorrectness was detected. It is also not the context where exception propagates. But you have conceded it in the next paragraph.
If the program is incorrect, these finalizations *cannot* be>> performed, because their preconditions might be false.>
The programmer is free to allow exception propagation> to where he wants to. He cannot trust objects which> where interrupted during one of their service executions.
So he must foresee how exceptions will propagate and handle the corresponding side effects. That's the whole point. He can't do it if the program is in a non-state. Continuation is only then possible, when the state is known. [It is a FSM, after all.] If the state is known, then it is a correct state.
If you are able to know the reason why the program is incorrect> (assertions), there is lots of safe things that we can do.
Assertions don't tell "why." Deduction process is on the programmer's part. My position is very simple - if you check something then you should well know what you will do for any possible outcome. Otherwise, it is irresponsible.
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.>
You (again) didn't get the point.
It was your example. You seemed to claim that when something is incorrect, no matter what, one can still rely on the car's behavior...
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.>
I'll get an *incorrect* program> in which its states are *able*,> *sometimes*, to detect its> incorrectness.
That's funny, it will fail even if result would have been correctly evaluated. Real sqrt isn't specified this way. But anyway, read my question above and answer the following: in which state sqrt is, when assert returns false. After doing that, proceed to:
double sqrt (double x) { double result;
result = <some iterative procedure to get square root>;
assert (result == 2*x); // postcondition
return result; }
And anything it was, is the behavior.>
A program, unless if proved statically to be correct,> cannot protect itself from *all possible* incorrect> behaviors. It can, however, protect itself from incorrect> behaviors expressed as runnable assertions (that,> my friend, *is* DbC at work).
Huh, any behavior you can protect from, is not a behavior! Do non-existing things exist?
The word "inside" is the only reason.>
Is it?
Yes, otherwise, you'd inevitably fall into a contradiction.
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.>
Yes it is.>
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>
Of course (wasn't that what I was referring to?).
No. You have added two states. It is another program. Its correctness need to be stated.
Now you have to say what your program will do in the second case.>
An exception was throwned (clients can handle it if they> have enough context information to correct the> detected error.)
So the contract is: either all right or exception. Which makes the program correct when evaluation wasn't correct. Evaluation /= the whole program, which now consists of evaluation and a check. When you add checks, you become questions about false positives and false negatives, which then determine the correctness of the [new] program.
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.>
What is your point?
That "well-structured" program is not a well defined term. People disagree on this issue.
Miguel Oliveira e Silva 7 March 2006 14:21:34 [ permanent link ]
Oliver Wong wrote:
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message> news:440CA653.61F0D4D4@det.ua.pt...> > I'm quite sure DbC approach (when applied to internal> > contracts) is much (much) better in every respect.> [to defensive programming]> > Of course I'm willing to continue to argue about that.>
Well, before we get into an argument about this, I'd better understand> what I'm arguing about. ;)
Wise decision.
What do you mean by "internal contracts"?
(I have already provided a definition.)
Internal contracts are the ones a program can take responsibility to ensure 100% (not 99.99...99%, but 100%).
The reason I have make such a thing explicit, was to try to reduce our communication problems. Many times a program cannot take 100% responsibility for a given behavior, because such behavior depends on external entities (file handling, user input, embedded systems, etc.). In those cases, a defensive approach (which may include exceptions) is a wise decision.
Initially, I *wrongly* gave the idea (it was not intended) that exceptions are only to express defined behavior for incorrect programs (with false assertions). So a lot of noise resulted from there (despite my attempts to make things crystal clear). My original claim was (and is) that exceptions are the required behavior for a detected incorrect program.
I.e. what change in meaning> does the adjective "internal" cause when applied to "contracts" in this> context?
Miguel Oliveira e Silva 7 March 2006 16:47:21 [ permanent link ]
Dmitry,
It is very difficult (and unproductive) to argue against a "moving target".
Please take the time (or not) to read my messages from the start, and surely you'll see that I don't use rhetorical "tricks" to present my arguments and to support what DbC is. I tooked extreme care to precisely define the terms I use (definitions that you chosed to ignore at your convenience, arguing back with fallacious absurd comments, or even with my own arguments), and to match those definitions with DbC terminology. That was the case of normal and exceptional behavior in the context of DbC (which I have defined since the very beginning).
In fact you should not read my messages, you should *learn* what DbC is from those original established references (feel free to ask for them).
We can, of course, discuss if DbC (the real one, given by established references) is good or bad when compared with *other* approaches. (In fact I've been doing that from the start comparing it with defensive approaches.). At this moment, I just feel that it is useless to persist a deft conversation with someone which (deliberately or not) refuses to understand what I'm saying (replying back with my own arguments as if they were not, is quite an amusing *trick* to someone as myself, used to many lengthy argumentations in environmental policy, and politics, wars here in Portugal), and persists with ambiguous confusing messages.
I also provided references for the other established terms I use, such as structured programming. Again your only response to that, instead of acknowledging the correctness (or not) of those references (is it that difficult for you to concede and explicitly agree with other people?), was to keep on saying that there are (somewhere) people who disagree with such definitions (as if I didn't know that). Communication, exactly as with contracts, requires two sides willing to communicate (willing to teach *and* learn).
Select precisely the points you want to discuss (I quite sure that most of them were already approached), making *your* definitions very clear, so that we can indeed communicate productively with each other.
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message news:440D6CBE.F233EA0@det.ua.pt...> Oliver Wong wrote:>
"Miguel Oliveira e Silva" <mos@det.ua.pt> wrote in message>> news:440CA653.61F0D4D4@det.ua.pt...>> > I'm quite sure DbC approach (when applied to internal>> > contracts) is much (much) better in every respect.>> [to defensive programming]>> > Of course I'm willing to continue to argue about that.>>
Well, before we get into an argument about this, I'd better >> understand>> what I'm arguing about. ;)>
Wise decision.>
What do you mean by "internal contracts"?>
(I have already provided a definition.)>
Internal contracts are the ones a program can> take responsibility to ensure 100% (not> 99.99...99%, but 100%).>
The reason I have make such a thing explicit,> was to try to reduce our communication problems.> Many times a program cannot take 100%> responsibility for a given behavior, because> such behavior depends on external entities> (file handling, user input, embedded systems, etc.).> In those cases, a defensive approach (which may> include exceptions) is a wise decision.>
Initially, I *wrongly* gave the idea (it was> not intended) that exceptions are only to> express defined behavior for incorrect> programs (with false assertions). So a> lot of noise resulted from there (despite> my attempts to make things crystal clear).> My original claim was (and is) that> exceptions are the required behavior> for a detected incorrect program.>
I.e. what change in meaning>> does the adjective "internal" cause when applied to "contracts" in this>> context?>
Its meaning is now clear to you?
Yes, and I don't dispute your claim, so no argument is nescessary, I think. =)
"Oliver Wong" <owong@castortech.com> wrote in message news:J2iPf.16201$vC4.13731@clgrps12...
I guess if I had to only emphasize one point right now, it would be my > logical-consistency point. To address the point, you would simply need to > answer this one question (there should only be two choices):
Are pre-conditions ALWAYS tested, or can they be disabled?
(a) They are ALWAYS tested.> (b) Sometimes they can be disabled.
With DbC, preconditions can be enabled/disabled for a component, by the component user. Which is possible because preconditions are adornments of the component.
Contrast with defensive programming, where the equivalent conditions are embedded within the component (how does one enable/disable the conditions when you are given a pre-built component etc) .
I'm not saying having preconditions in your contract has the same> > >> ethical values of murdering nuns. I'm saying that if you want robust> > >> software, you should have fewer pre-conditions.> > >
Wrong.> > >
If you want robust programs, you should make sure they respect> > > their internal contracts (regardless of the strength of preconditions> > > and other assertions).> >
As I've said over and over again, if a module doesn't respect its> > contract, then you've got big problems.>
That's the easier part to ensure contracts: postconditions and> invariants depend only on the module implementation (it> is a local problem).
(I keep forgetting that there are some very important things ensured in languages such as Eiffel, but that cannot be taken for granted in other languages such as C++ and Java. Sorry.)
To be more precise. A class invariant can only be a local module problem if, and only if, there are no public modifiable attributes. This kind of attributes can, by definition, be unconditionally modified directly by external clients, hence the module is helpless to ensure their part on the invariant.
(Public modifiable attributes are a very [very] bad idea.)
To be more precise. A class invariant can only be a> local module problem if, and only if, there are no> public modifiable attributes. This kind of attributes> can, by definition, be unconditionally modified> directly by external clients, hence the module> is helpless to ensure their part on the invariant.
Suppose the class invariant is `the current value of V is the value last written to it`. Then modifying V does not violate the invariant.
Sometimes an object is just a shell round a variable.
(Public modifiable attributes are a very [very]> bad idea.)
Often. But not always.
-- Chris "sparqling" Dollin "Who do you serve, and who do you trust?"
Kenneth P. Turvey 12 March 2006 12:43:36 [ permanent link ]
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Mon, 06 Mar 2006 20:27:16 +0000, Oliver Wong wrote:
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).
I've been thinking about this a bit and I think your statement is still too strong. For example I have a method that updates some sub-set in a set:
void updateSubSet(SomeParam param) { .. }
The preconditions include the pre-condition that no element of set will be null and the post-conditions include the post-condition that no element of set will be null.
The method must make sure that any updates it makes to the the set do not result in null values. Since it can be confident that the set didn't contain any null values when it was called, it was a precondition, it can guarantee that the set will not have any null when it completes.
In order to replace this method with a method that does not have the pre-condition requiring that all elements are defined in the set means that the method must iterate over every element in the set and check it. This is not asymptoticly the same as the original since the original only had to look at the subset. We can imagine that the entire set is possibly infinite.
- -- Kenneth P. Turvey <kt-usenet@squeakydolphin.com> Phone : (314) 255-2199
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message newsan.2006.03.12.08.43.30.195267@squeakydolphin.com...>
On Mon, 06 Mar 2006 20:27:16 +0000, Oliver Wong wrote:>
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).>
I've been thinking about this a bit and I think your statement is still> too strong. For example I have a method that updates some sub-set in a> set:>
void updateSubSet(SomeParam param) {> ..> }>
The preconditions include the pre-condition that no element of set will> be null and the post-conditions include the post-condition that no element> of set will be null.>
The method must make sure that any updates it makes to the the set do not> result in null values. Since it can be confident that the set didn't> contain any null values when it was called, it was a precondition, it can> guarantee that the set will not have any null when it completes.>
In order to replace this method with a method that does not have the> pre-condition requiring that all elements are defined in the set means> that the method must iterate over every element in the set and check it.> This is not asymptoticly the same as the original since the original only> had to look at the subset. We can imagine that the entire set is possibly> infinite.
You would be correct if I had omitted the keyword "runnable" before "pre-conditions", but since the pre-condition "no element of the set will be null" is not runnable in the case where the set is infinite, then this "counter example" doesn't actually counter my statement above.
I must admit I hadn't considered the example you proposed though, and it does make me more nervous now about the truth value of my statement. ;)
Kenneth P. Turvey 14 March 2006 01:35:37 [ permanent link ]
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Mon, 13 Mar 2006 15:21:35 +0000, Oliver Wong wrote:
You would be correct if I had omitted the keyword "runnable" before > "pre-conditions", but since the pre-condition "no element of the set will be > null" is not runnable in the case where the set is infinite, then this > "counter example" doesn't actually counter my statement above.
Even with the "runnable" one can imagine that checking the entire set is O(n^2) while just checking the subset is O(n). The point was that the extra time a method takes after your transformation is unbounded by the original runtime.
I must admit I hadn't considered the example you proposed though, and it > does make me more nervous now about the truth value of my statement. ;)
I think your statement is true until you start making claims about runtime. As soon as you do, I think you are leaving out a large class of possible pre-conditions. The example I just gave is an example of a "Good Data in -> Good data out" set of conditions that isn't at all atypical and often would require quite a bit of effort to convert to "Any data in -> Good data out or exception".
- -- Kenneth P. Turvey <kt-usenet@squeakydolphin.com>
"Kenneth P. Turvey" <kt-usenet@squeakydolphin.com> wrote in message newsan.2006.03.13.21.35.32.807446@squeakydolphin.com...> -----BEGIN PGP SIGNED MESSAGE-----> Hash: SHA1>
On Mon, 13 Mar 2006 15:21:35 +0000, Oliver Wong wrote:>
You would be correct if I had omitted the keyword "runnable" before>> "pre-conditions", but since the pre-condition "no element of the set will >> be>> null" is not runnable in the case where the set is infinite, then this>> "counter example" doesn't actually counter my statement above.>
Even with the "runnable" one can imagine that checking the entire set is> O(n^2) while just checking the subset is O(n). The point was that the> extra time a method takes after your transformation is unbounded by the> original runtime.
I'll try to make this discussion more concrete by providing pseudo code. So contract A, and implementation A, look like this:
/* pre: data does not contain null. post: return some subset of data. This subset will not contain null. */ public implementationA(data) { return getEveryOtherElementIn(data); }
/* pre: none. post: returns some subset of data. If data did not contain null, the subset will not contain null either. */ public implementationB(data) { return getEveryOtherElementIn(data); }
I must admit I hadn't considered the example you proposed though, and >> it>> does make me more nervous now about the truth value of my statement. ;)>
I think your statement is true until you start making claims about> runtime. As soon as you do, I think you are leaving out a large class of> possible pre-conditions. The example I just gave is an example of a "Good> Data in -> Good data out" set of conditions that isn't at all atypical and> often would require quite a bit of effort to convert to "Any data in ->> Good data out or exception".
You are probably correct about it being difficult to convert "Good Data in -> Good Data out" to "Any data in -> Good data out or exception", but perhaps it's "always easy" to convert it to "Good data in -> Good data out; Bad data in -> Bad data out".
If you would like to report an abuse of our service, such as a spam message, please . Если Вы хотите пожаловаться на содержимое этой страницы, пожалуйста .