www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Pre / Post / Invariant Best Practices

reply "David Barrett" <dbarrett quinthar.com> writes:
I'm trying to wrap my head around how best to use these.

Preconditions I get:
Validate every input to my methods because I can't trust the caller.
Likewise, validate that the object is in an acceptable state *for this
method*. (Different methods might have different "valid" states: File.open()
asserts that the file is closed, while File.close() asserts that it's open.)


Invariants I kinda get:
Validate that the object is in one of the many possible valid states.  This
doesn't eliminate validating the specific state in the Precondition, but it
can simplify the Precondition. (For example, if File.filename must be set
whenever it's open, then assert this relationship in the invariant.  Then
you don't need to test both the filename and the open state in the
File.open() and File.close() Precondition -- testing one is sufficient to
prove the other.)


But Postconditions elude me:
It seems of very low value for a method to test its own output: that's like
the fox guarding the henhouse.  If I'm going to have a test, I'd prefer that
the caller do the testing to verify the method output what it expected.
Just like a method can't trust its input and needs to validate it with
preconditions, it can't trust a return value and thus must validate it, too.
After all, the return value might be "valid" from the perspective of the
method, but "invalid" from the perspective of the caller.

Granted, I can see the value of periodically asserting your state while
within some complex operation.  But by the time I get to actually returning
a value, I should have a pretty high degree of confidence that I'm returning
the right value.  And if I'm just doing a final check to verify the object
is still sound, that should be done in a class invariant, no?


So my question is: what "best practices" have you been using in reality?  I
personally use Preconditions and copious amounts of asserts.  But Invariants
and Postconditions don't seem to offer real-world value to the code I'm
writing.  Am I missing something?  Do others find that writing/maintaining
Invariants and Postconditions pays for themselves in the bugs they avoid?

-david
Jul 08 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"David Barrett" <dbarrett quinthar.com> wrote in message
news:cckop5$c8g$1 digitaldaemon.com...
 So my question is: what "best practices" have you been using in reality?
I
 personally use Preconditions and copious amounts of asserts.  But
Invariants
 and Postconditions don't seem to offer real-world value to the code I'm
 writing.  Am I missing something?  Do others find that writing/maintaining
 Invariants and Postconditions pays for themselves in the bugs they avoid?
Consider a function that sorts its input. A postcondition would test to see if the data is really sorted.
Jul 08 2004
parent reply "David Barrett" <dbarrett quinthar.com> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cckuq3$kab$1 digitaldaemon.com...
 "David Barrett" <dbarrett quinthar.com> wrote in message
 news:cckop5$c8g$1 digitaldaemon.com...
 So my question is: what "best practices" have you been using in reality?
I
 personally use Preconditions and copious amounts of asserts.  But
Invariants
 and Postconditions don't seem to offer real-world value to the code I'm
 writing.  Am I missing something?  Do others find that
writing/maintaining
 Invariants and Postconditions pays for themselves in the bugs they
avoid?
 Consider a function that sorts its input. A postcondition would test to
see
 if the data is really sorted.
I agree that there are times a Postcondition *could* be used, and that's a good example. But I'm not disputing it's theoretical value. Rather, I'm asking asking: How often do people really use them *in practice*? Would you say that "none", "some", "most", or "all" D functions you write use Postconditions? As a comparison, do you use "fewer", "as many", or "more" Postconditions as Preconditions? -david
Jul 08 2004
parent "Walter" <newshound digitalmars.com> writes:
"David Barrett" <dbarrett quinthar.com> wrote in message
news:ccl7qu$1216$1 digitaldaemon.com...
 But I'm not disputing it's theoretical value.  Rather, I'm asking asking:
 How often do people really use them *in practice*?
Not much right now. But I expect as people get more into D, they'll use them more. For example, look at std.format.doFormat() - it's full of nested functions!
Jul 09 2004
prev sibling next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cckop5$c8g$1 digitaldaemon.com>, David Barrett says...
I'm trying to wrap my head around how best to use these.
Curiously, the way I use them is completely different from that in which you use them. Could be the start of an interesting discussion...
Preconditions I get:
Validate every input to my methods because I can't trust the caller.
Oh contraire! You shouldn't use preconditions to validate USER input - because those "in" blocks will disappear altogether in a release build. You'll end up with code that only works in a debug build. Preconditions should never result in different behavior between release and debug builds. Besides which, what use is an assert message to an end-user? No, preconditions exist to help YOU find YOUR bugs. The way I use it, if the input is dependant upon user-input, then I should test for validity in the function body, not the precondition, and throw an exception or otherwise handle it if it turns out to be nonsense. The in-block is there to test whether or not my own code contains bugs. In simple terms, the in-block asserts that the input is what I *EXPECT* it to be. (And in the case of user-input, I expect it to sometimes be rubbish, and hence consider rubbish to be legal input). In this paradigm, every assert failure within an in-block represents one bug found in another part of code ... which then gives me the power to go and fix it.
Likewise, validate that the object is in an acceptable state *for this
method*. (Different methods might have different "valid" states: File.open()
asserts that the file is closed, while File.close() asserts that it's open.)
I don't think it should be an error to close a file twice. The second close should be silently ignored, and should be completely harmless. And writing to a file which is not open should surely throw a WriteError? Again, I remind you that asserts - along with in-blocks and out-blocks - will not be present in a release build. What's your program gonna do then? Crash?
Invariants I kinda get:
They're pretty easy to understand, in that they can only really be used for bug-finding.
But Postconditions elude me:
It seems of very low value for a method to test its own output: that's like
the fox guarding the henhouse.
Really? I find postconditions most useful of all. In the Int class (etc.bigint.bigint) they're used all the time. For example - there's a function which, given input x, returns the integer square root y of x, and the remainder r. The postcondition asserts that (y*y+r == x). In other words, the postcondition will tell me if the function contains a bug. Simply calling the function a few times without seeing an assert gives me high confidence that the function is okay.
After all, the return value might be "valid" from the perspective of the
method, but "invalid" from the perspective of the caller.
There is no "valid" or "invalid". Just "bug-free" or "not bug-free". That's what you're testing for.
And if I'm just doing a final check to verify the object
is still sound, that should be done in a class invariant, no?
Yes it should. But that wouldn't have helped me with the square root example, would it? Postconditions serve a different purpose.
So my question is: what "best practices" have you been using in reality?  I
personally use Preconditions and copious amounts of asserts. But Invariants
and Postconditions don't seem to offer real-world value to the code I'm
writing.
They offer value to the person WRITING the code, not value to the person USING the code.
Am I missing something?
You may possibly be missing the fact that all of these things will disappear into the ether in a release build. Never assume that an end user will have the benefit of these things in your code.
Do others find that writing/maintaining
Invariants and Postconditions pays for themselves in the bugs they avoid?
Yes, but they don't AVOID bugs - they FIND bugs. Your end-users avoid them, because /you/ fix them before those other people even see them. Arcane Jill
Jul 09 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cclgul$1g20$1 digitaldaemon.com...
 Oh contraire! You shouldn't use preconditions to validate USER input -
because
 those "in" blocks will disappear altogether in a release build. You'll end
up
 with code that only works in a debug build. Preconditions should never
result in
 different behavior between release and debug builds. Besides which, what
use is
 an assert message to an end-user?

 No, preconditions exist to help YOU find YOUR bugs. The way I use it, if
the
 input is dependant upon user-input, then I should test for validity in the
 function body, not the precondition, and throw an exception or otherwise
handle
 it if it turns out to be nonsense. The in-block is there to test whether
or not
 my own code contains bugs.

 In simple terms, the in-block asserts that the input is what I *EXPECT* it
to
 be. (And in the case of user-input, I expect it to sometimes be rubbish,
and
 hence consider rubbish to be legal input). In this paradigm, every assert
 failure within an in-block represents one bug found in another part of
code ...
 which then gives me the power to go and fix it.
Rock on, Jill! You've got it exactly right. Can I encourage you to post this in the D wiki?
Jul 09 2004
next sibling parent Derek <derek psyc.ward> writes:
On Fri, 9 Jul 2004 09:30:43 -0700, Walter wrote:

 "Arcane Jill" <Arcane_member pathlink.com> wrote in message
 news:cclgul$1g20$1 digitaldaemon.com...
 Oh contraire! You shouldn't use preconditions to validate USER input -
because
 those "in" blocks will disappear altogether in a release build. You'll end
up
 with code that only works in a debug build. Preconditions should never
result in
 different behavior between release and debug builds. Besides which, what
use is
 an assert message to an end-user?

 No, preconditions exist to help YOU find YOUR bugs. The way I use it, if
the
 input is dependant upon user-input, then I should test for validity in the
 function body, not the precondition, and throw an exception or otherwise
handle
 it if it turns out to be nonsense. The in-block is there to test whether
or not
 my own code contains bugs.

 In simple terms, the in-block asserts that the input is what I *EXPECT* it
to
 be. (And in the case of user-input, I expect it to sometimes be rubbish,
and
 hence consider rubbish to be legal input). In this paradigm, every assert
 failure within an in-block represents one bug found in another part of
code ...
 which then gives me the power to go and fix it.
Rock on, Jill! You've got it exactly right. Can I encourage you to post this in the D wiki?
Agreed. This is spot on, Jill. And very nicely said too. -- Derek Melbourne, Australia
Jul 09 2004
prev sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <ccmhl2$30jv$2 digitaldaemon.com>, Walter says...

Rock on, Jill! You've got it exactly right. Can I encourage you to post this
in the D wiki?
I don't know how. I headed over to Wiki4D to make the attempt, but got lost and confused. To be honest, until someone said "Let's start a Wiki for D" I thought Wiki was a free encyclopedia (although I see now that's actually called Wikipedia). It looks like there's no connection between the two. So, since my knowledge appears to be very deficient in some areas, perhaps someone can enlighten me. 1) What the hell is a Wiki? 2) Why is it called "wiki"? 3) How do you add pages to it, edit it, etc? 4) What's to stop spammers and site-vandals from doing (3)? 5) If I click on Edit, I see a plain text entry box in some weird non-HTML format I don't understand. What format is this? 6) Is there a tutorial somewhere on the web that explains all this for us Wiki-newbies? 7) What is the connection between this use of the word "wiki" and the wikipedia (if any). In short, I can't put that previous post in the Wiki. I tried, and gave up in confusion. But I give permission for someone else to post my words if they so desire. Long term - obviously the Wiki thing looks useful, so someone would care to enlighten me I promise I'll try to bring my skills up to date. Arcane Jill
Jul 09 2004
parent Andy Friesen <andy ikagames.com> writes:
Arcane Jill wrote:
 [...] perhaps someone can enlighten me.
 
 1) What the hell is a Wiki?
A website that anybody can edit, basically. The first one is the Portland Pattern Repository, which can be found at <http://c2.com/cgi/wiki> (incidently, there's loads and loads of awesome stuff buried in there. it's not hard to dive in and come back out with gray hair)
 2) Why is it called "wiki"?
<http://c2.com/cgi/wiki?EtymologyOfWiki>
 3) How do you add pages to it, edit it, etc?
 4) What's to stop spammers and site-vandals from doing (3)?
The biggest reason WhyNobodyDeletesWiki <http://c2.com/cgi/wiki?WhyNobodyDeletesWiki> is because it's so very easy to undo. Every wiki I know of stores changes made in the last few weeks or so, so anybody at all can undo it, and there are generally quite a lot more people interested in fixing it than breaking it. :)
 5) If I click on Edit, I see a plain text entry box in some weird non-HTML
 format I don't understand. What format is this?
Wikis have their own FormattingRules <http://c2.com/cgi/wiki?TextFormattingRules> Anyway, I'm sure you get the idea. It really is as simple as it sounds. :) -- andy
Jul 10 2004
prev sibling next sibling parent "David Barrett" <dbarrett quinthar.com> writes:
"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:cclgul$1g20$1 digitaldaemon.com...
 In article <cckop5$c8g$1 digitaldaemon.com>, David Barrett says...
Preconditions I get:
Validate every input to my methods because I can't trust the caller.
Oh contraire! You shouldn't use preconditions to validate USER input -
because
 those "in" blocks will disappear altogether in a release build.
Sorry! I didn't mean to imply otherwise. I agree, user input requires it's own type of validation. I meant to state that a precondition validates a method's input *arguments*, not user input. Preconditions, Postconditions, and Invariants are tools to pre-emptively identify bugs in close proximity to the source. I think we agree on this point.
Likewise, validate that the object is in an acceptable state *for this
method*. (Different methods might have different "valid" states:
File.open()
asserts that the file is closed, while File.close() asserts that it's
open.)
 I don't think it should be an error to close a file twice. The second
close
 should be silently ignored, and should be completely harmless.
That's an interesting argument in itself (I take a very strict approach using the same logic as the D compiler having no warnings). But that's outside the scope of my question.
But Postconditions elude me:
It seems of very low value for a method to test its own output: that's
like
the fox guarding the henhouse.
Really? I find postconditions most useful of all. In the Int class (etc.bigint.bigint) they're used all the time. For example - there's a
function
 which, given input x, returns the integer square root y of x, and the
remainder
 r. The postcondition asserts that (y*y+r == x).
Ah, thanks. This helps me. I guess Postconditions -- like anything -- are an art. Looking over bigint_int.d helps me see that. I can see that functions whose primary value is to produce output benefit most from Postconditions. But I guess I had in mind things like Sockets (how can I confirm the data was sent?) or complex algorithms (how can I verify an XML document was properly parsed?). Of course, big functions are composed of small functions, and each of those might have its own Postconditions. Thanks for walking me through this; still trying to figure out how to properly place D in the pantheon of tools at hand. -david
Jul 09 2004
prev sibling parent Norbert Nemec <Norbert.Nemec gmx.de> writes:
Arcane Jill wrote:

But Postconditions elude me:
It seems of very low value for a method to test its own output: that's
like the fox guarding the henhouse.
Really? I find postconditions most useful of all. In the Int class (etc.bigint.bigint) they're used all the time. For example - there's a function which, given input x, returns the integer square root y of x, and the remainder r. The postcondition asserts that (y*y+r == x).
It is more than that. For finding your own bugs, a plain assert would be good enough. The post-condition is more like a promise to the outside world. You are telling publicly: this function will do that. Better than any comment: the promise can even be checked.
Jul 10 2004
prev sibling next sibling parent "Bent Rasmussen" <exo bent-rasmussen.info> writes:
To my knowledge Design by Contract is an Eiffel concept by origin. If you
want to see powerful use of contracts you should check out the Eiffel
structures library. A powerful aspect of pre- and postconditions is that
they can be inherited. Since in Eiffel you cannot assign a value to a field
from outside the object, you will need to make a set method to do this. So
you make a setter. Next someone inherits from your class, redefines the
setter, adds event hanling, but forgets to set the actual value. The
postcondition will be inherited and the contract breach will manifest at
runtime. This example, although trivial, shows that even in the simplest of
cases, a postcondition is relevant. I'm not so sure it isn't an error
(Arcane Jill thinks not), to close a file twice. It is an error in that it
isn't possible to close a closed file, no transition takes place, however
the outcome is acceptable. So it is an error without negative consequence
other than bloat or (remotely possible) an uncorrected misunderstanding of
the semantics of close (I can see it now: But the file was only half-way
closed!) Whether bloat like this should be avoided through contracts is a
matter of taste.
Jul 09 2004
prev sibling parent reply Norbert Nemec <Norbert.Nemec gmx.de> writes:
David Barrett wrote:

 But Postconditions elude me:
It is design-by-contract: Both, pre- and post-conditions are part of the *interface*. There are several aspects to that fact. In D, there seems to be very little distinction between interface and implementation, both being coded in the same file. I believe, though, that it is possible to extract the interface of a given module, using the compiler. In that case, the pre- and post-conditions should be put into the interface as well. They document how a function is to be used, and what it produces. It is understood, that pre- and postconditions should only ever depend on public functions and the function arguments. One important aspect of pre- and post-conditions is, that they are inherited along with the interface. (The the chapter "contracts") An overriding function must keep the contract of the original. It my loosen the in-contract or tighten the out-contract. Plain asserts in the body, on the other hand, have nothing to do with contracts, but are merely a kind of "checked documentation", helping in understanding the implementation code correctly. Whether design-by-contract helps you in avoiding bugs depends on your coding style. It certainly is only interesting for libraries and larger projects that define clear interfaces between individual parts. For small projects, thinking about clear interfaces often is more effort than it is worth.
Jul 10 2004
parent "Walter" <newshound digitalmars.com> writes:
"Norbert Nemec" <Norbert.Nemec gmx.de> wrote in message
news:cco50m$27n6$1 digitaldaemon.com...
 Whether design-by-contract helps you in avoiding bugs depends on your
coding
 style. It certainly is only interesting for libraries and larger projects
 that define clear interfaces between individual parts. For small projects,
 thinking about clear interfaces often is more effort than it is worth.
True enough. One aspect of D that sometimes is controversial is I wanted to make it easy to write quick-and-dirty programs as well as provide the scaffolding for large, complex projects. That's why, for example, 'public' is the default access rule for class members, and object.d is implicitly imported. DbC is a waste of effort for Q&D programs, which is why it is optional.
Jul 11 2004