an old topic (pun intended)

Davidson Corry davidsoncorry at comcast.net
Wed Oct 26 12:51:22 PDT 2011


     (continuing a thread from D.learn)

Eiffel supports an 'old' construct in post-conditions. For example, an 
Add method of a Bag class (generic collection container) might verify 
that exactly one element was added to the collection, not zero and not 
more than one:

     Add(newItem : T) is
     do
         -- insert newItem into the Bag
     ensure
         exactly_one_added: count = old count + 1
     end

D2 post-conditions do not provide 'old', and are less useful than they 
could be for its lack.

(Note: D post-conditions appear to be applicable to bare statements, not 
just functions. I don't see the point of statement post-conditions -- 
you can do the same thing with 'assert' -- so I focus here on function 
post-conditions.)

It has been suggested that 'unittest' largely obviates the need for 
'old'. I disagree with this view. A unit test verifies only the 
conditions that it explicitly proposes.  In contrast, a post-condition 
will exercise "real-world data" in production-level runs during testing, 
and is thus likely to trigger on conditions that the unit test never sees.

In the D.learn thread, Jonathan M Davis and Ali Çehreli comment

     JMD> IIRC, old was rejected because it added extra complication for 
little value.
      AÇ> Yes, there were technical difficulties in making the stack 
frame at the call point available to the stack frame at the exit point.

Another difficulty is examining the post-condition code for 'old expr' 
or 'old(expr)' and hoisting those elements out of the middle of an 
expression for prestate capture.

I want to propose a syntax extension that, it seems to me, makes it 
easier for the compiler to do all of this.

In D2, a post-condition is introduced with either 'out(result)' or 
'out(...)' [the latter now elided(??) to merely 'out']. That is, 'out' 
has what appears to be something like an argument list. Let's use it:

     void foo(T param_T, Q param_Q)
     out(result, X preX = exprX, Y preY = exprY) {   // <== prestate 
captures added to out "argument" list HERE
         // various assert()s using the captured prestates
     }
     body {
         // ..actual body of foo()
     }

A lowering of this would look something like

     void foo(T param_T, Q param_Q) {
         X __fresh_symbol_for_preX = exprX;
         Y __fresh_symbol_for_preY = exprY;
         // ..actual body of foo()
         assert(__postcondition_code);
     }

That is, the compiler would hoist the prestate variables (and the code 
that sets their values) out of the "argument" list, and inject them into 
the function body at its very start, much like a template instantiation.

Notes:

     +  "erasing" the actual symbols in the argument list and replacing 
them with compiler-generated fresh symbols hides those symbols from the 
function body code (which should not know of them)
     +  but those captured prestate-values exist at the outermost scope 
of the function body, just as the returned value of the function does. 
If we can pass the magic symbol 'result' to the post-condition, we 
should be able to pass
the captured prestate
     +  the scope and condition-at-time-of-evaluation for exprX and 
exprY are exactly what we want: they have access to anything that the 
function body can access (such as variables in enclosing functions, 
class members etc.) but NOT to
local declarations (which they should not be able to see) because those 
haven't been declared yet; they are part of the function body code that 
follows the injected prestate-capture code
     +  the compiler is relieved of the burden of sniffing out "old" 
sub-elements of expressions in the post-condition body and figuring out 
exactly what bits of frame and scope need to be preserved to compute 
them. The onus is on
the programmer to declare what prestate he wants to verify, and how to 
capture it, to whatever level of complexity he wants or needs to employ
     +  exprX etc. can use any syntax element that is legal within the 
function body (since it is hoisted and injected *into* the function body 
by the compiler), including things like lambda functions, etc. (In 
practice, "old"
expressions in post-conditions tend to be pretty tame.)
     +  the programmer can name the captured prestate values with any 
symbols he wishes. This preserves some of the flavor of Eiffel's 
per-post-condition labels.
     +  capture of prestates is separated from evaluation of 
post-function conditions *using* those prestates. This may make it more 
obvious what's being tested and how.
     +  no new 'old' keyword or magic 'old()' function needs to be added 
to the language, particularly one that is useful only in this isolated 
corner of the syntax

Two weird characteristics of this syntax:

     -  'result' is evaluated AFTER the function body executes, but 
prestate-captures are evaluated BEFORE the function body executes
     -  normal argument lists contain only expressions, not declarations 
of symbols to be assigned from those expressions

I think the syntax is clean and flexible enough to forgive those 
transgressions.



An example ('count' and 'color' here are members of an enclosing class, 
not shown):

     void change_count_and_color(int countIncrement, bool flipColor)
     out (result, auto preCount = count, auto previousColor = color) {
         assert(count == preCount + countIncrement);
         if (flipColor) {
             assert(color == ComplementaryColorOf(previousColor));
         }
         assert(isSensible(result));
     }
     body {
         // ..actual body of the function
     }



   =========

Comments, criticisms, jeers and guffaws cheerfully invited. (Also "if 
you're so smart, there's the source code, YOU implement it!" But then 
*I'll* guffaw.)

-- Davidson




More information about the Digitalmars-d mailing list