D's design by contract is missing "old"?

Russ Williams digitalmars.D.learn at russcon.removethispart.andthistoo.org
Sun Jun 17 07:08:12 PDT 2007


Derek Parnell Wrote:

> > state machine.  I was hoping I could do this:
> > 
> > void descend()
> > in
> > {
> >     assert( depth != depth.max );
> >     auto old_depth = depth;
> > }
> > out
> > {
> >     // Note that I'm using a variable declared in the "in" contract.
> >     assert( depth == old_depth + 1 );
> > }

> As a workaround idea ...
> 
> alias typeof(depth) depth_T;
> 
> depth_T descend()
> in
> {
>     assert( depth != depth.max );
> }
> out(old_depth)
> {
>     // Note that I'm using a variable declared in the "in" contract.
>     assert( depth == old_depth + 1 );
> }
> body
> {
>     depth_T old = depth;
>     ++depth;
>     return old;
> }

That workaround has the extremely unpleasant property of changing the function return type to return the old value, and becomes even more clunky when the contract mentions more than one field of "this", as often happens.

E.g. in a game where you use resources to build stuff, you might have:

void function buildStuff(int n)
in
{
    assert(0 <= n && n < resourceCount);
}
out
{
    assert(resourceCount = old(resourceCount) - n);
    assert(stuffCount = old(stuffCount) + n);
}

Your workaround would have to change buildStuff to return a struct with 2 fields (the old resourceCount and the old stuffCount).  Writing code like this would get very tedious very quickly.

"old" is the sort of thing that you really need built-in language support for.  It's an extremely useful part of DBC, and I'm quite surprised and disappointed to find it apparently missing from D.  It's essential for defining the output contract of a function in any kind of useful serious way (as opposed to very simplistic "sanity checks" at function exit).

I can imagine workarounds that involve writing code at function entry to explicitly clone "this" so that the out clause can use its values, and storing that clone as a global variable (yuck, and non-reentrant) or as part of every object (waste of space).  But why should the programmer have to write such boilerplate code over and over?

Without "old" postconditions are vastly less useful.  It's analogous to other languages like C/C++/Java that only have plain old asserts but are sometimes claimed to support DBC, without having function preconditions/postconditions and class invariants.


More information about the Digitalmars-d-learn mailing list