Output contract's arguements

H. S. Teoh hsteoh at quickfur.ath.cx
Thu Sep 19 13:43:33 PDT 2013


On Thu, Sep 19, 2013 at 09:46:35PM +0200, Joseph Rushton Wakeling wrote:
> On Thursday, 19 September 2013 at 18:27:52 UTC, H. S. Teoh wrote:
> >In my understanding, an out-contract is some condition C that the
> >function F promises to the caller will hold after it has finished
> >executing. Since F's internal state is uninteresting to the caller, C
> >is expressed in terms of what is visible to the caller -- that is,
> >the original function parameters and its return value(s) (which may
> >include the final state of the parameters if changes to those
> >parameters are visible to the caller). An out-contract shouldn't
> >depend on the state of local variables in F, since those are not
> >visible to the caller and are therefore uninteresting.  If you wish
> >to check that F's internal state satisfies some condition X at the
> >end of F, then it should be done as assert(X) before return, not as
> >an out-contract.
> 
> OK, you could define an out-contract that way -- but what's wrong in
> principle with the contract also being able to promise, "My internal
> state was sane on exit"?

Use an assert. That's what asserts are meant for.

And if you have multiple return statements and don't want to duplicate
code, just write:

	scope(exit) assert(myStateIsSane);

at the top of the function.


> Nitpicking over what should and shouldn't be allowed to be in
> contracts seems like a good way to complicate implementing them for
> no obvious technical benefit.

I'm not saying that the compiler should purposely check and reject such
uses. I'm saying that such uses are wrong usages of out-contracts.  It's
not the compiler's job to reject a badly-designed class API, for example
(it wouldn't know how to determine if something was badly-designed, in
the first place), but that doesn't mean poor API design is a good idea.

What the OP was concerned about was how to write an out-contract that
references the *original* values of the function arguments, which is
definitely a legitimate case that should be supported. This legitimate
use case should take precedence over the wrong usage of referencing
local variables in an out-contract (if indeed it's not worth the trouble
to make the compiler reject the latter case).

There is an obvious benefit of using out-contracts correctly: the
function becomes self-documenting:

	real sqrt(real x)
	in { assert(x >= 0.0); }
	out(result) { assert(abs(result*result - x) < EPSILON); }
	body { ... }

Just by reading the contracts of sqrt, you know that (1) it expects its
argument to be non-negative, and (2) its return value is the square root
of x (i.e., the return value multiplied by itself is equal to x up to
the specified precision).

But if 'x' in the out-contract refers to a possibly modified value of x
rather than the original argument, then the out-contract is basically
meaningless to the caller of this function, since, not knowing how sqrt
is implemented, the final value of x could be anything, and any
condition satisfied by x is of no relevance outside of sqrt itself.  And
since it has no relevance outside of sqrt, why bother with out-contracts
at all? Just put the assert inside the function body, where it belongs.

The only case where the modified value of an argument is relevant is
when the argument is passed by reference, and the caller can observe the
effects of the function on it. For example:

	void sqrtInPlace(ref real x)
	in { assert(x >= 0.0); }
	out {
		// Note: .out and .in is just hypothetical syntax.
		assert(abs(x.out * x.out - x.in) < EPSILON);
	}
	body { ... }

The out-contract is very useful, because it tells the user exactly how
the function will modify its argument, and what relation the final value
has to the original value. Currently, however, we don't have .out
syntax, so the above contract is inexpressible. Worse yet, in the current 
implementation, we may write:

	void sqrtInPlace(ref real x)
	in { assert(x >= 0.0); }
	out {
		// Does this x refer to the initial value of x, or the
		// final value?
		assert(x >= 0.0);

		// It sure looks like the initial value to me. Which
		// makes it totally pointless. But it actually refers to
		// the final value, which is non-obvious, and also of
		// limited benefit since we can't express a more
		// definite guarantee about its final value w.r.t. its
		// original value (i.e., that the final value squared
		// equals the original value up to a given precision).
	}
	body { ... }


> Is there really any technical cost to allowing the out-contract to
> address internal function variables, if the programmer wants it?

There is no cost -- it currently already lets you do that. :)

But it's also useless, because then there is no benefit to using an
out-contract as opposed to a plain old assert before the return
statement. In terms of the final executable code, there is basically no
advantage that an out-contract offers above an in-body assert statement.
The added value of an out-contract is the documented (and
runtime-checked) guarantee it provides to the users of the API.
Referencing implementation details that the users of the API don't care
about, such as local variables in the out-contract, invalidates this
benefit, which makes it useless.


T

-- 
Just because you can, doesn't mean you should.


More information about the Digitalmars-d mailing list