an old topic (pun intended)
Davidson Corry
davidsoncorry at comcast.net
Sat Oct 29 20:21:11 PDT 2011
On 10/28/2011 5:10 AM, kennytm wrote:
> Davidson Corry<davidsoncorry at comcast.net> wrote:
>> > ...it occurred to me the other day that we could write contracts as
>> >
>> > void foo(T t)
>> > {
>> > scope(in) {
>> > // pre-condition contracts
>> > }
>> > scope(out) {
>> > // post-condition contracts
>> > }
>> > // ...body of function...
>> > }
>> >
>> > and eliminate the 'in', 'out' and 'body' keywords/constructs entirely.
Just in passing, I think that 'scope(requires)' and 'scope(ensures)' are
somewhat more evocative of what these blocks actually do than
'scope(in)' and 'scope(out)' would be. The words 'in' and 'out' have so
many other connotations that they could be confusing.
>> > Similarly, an invariant could appear in the default constructor of a class as
>> >
>> > this()
>> > {
>> > scope(invariant) {
>> > // invariant guarantees...
>> > }
>> > // ...body of constructor...
>> > }
>> >
>> > Or*any* constructor, really, although you would probably want the
>> > compiler to enforce that no more than one constructor defined a
>> > scope(invariant) block. This notation also suggests (correctly) that the
>> > invariant doesn't take effect until you have entered the constructor.
>> > (Exited it, really, but...)
>
> The contracts are parts of the function's interface, not implementation. If
> you put the scope(in/out) inside the '{ ... }' how would a .di file handle
> it?
>
> And class invariant is also checked outside the constructor. Your syntax
> makes it look like it will run only when the constructor is called.
Good point. A class invariant is special enough that it should be
declared at class scope, rather than inside any particular method
(including constructors):
class foo {
scope(invariant) { /* the invariant checks */ }
// implementation of the class...
}
As for .di files, those can declare the contracts inside the braces and
simply elide the body of the function. This is similar to Eiffel's
short-flat form. Eiffel doesn't use braces for statement grouping or
lexical scoping, but its contracts are just as much "inside" the
function or method or class as the parameter lists of those functions or
methods are.
Positioning the contract blocks inside the braces is also suggestive of
the lexical environment (the "scope", although I'm trying not to use
that word because it's overloaded in this context) within which the
contract testing code runs -- anything that could be declared or
referenced at that position in the source code is available to the
contract code. (As with 'static if', the braces around a contract block
are for grouping, and do *not* introduce a new lexical scope. You would
need to {{double-brace}} the block to introduce new lexical scope, as
you do with 'static if'.)
============
I do think that using the 'scope(something)' form for contracts is still
a good idea.
'scope' is one of the big wins of the D design team. It allows the
programmer to declare code over *here* where it's close to the resource
it's going to clean up after, while making that code effective over
*there* where the clean-up needs to be done -- upon exit from a block,
upon failure of a block, etc. Or in this proposal, upon entry/exit to a
class method (invariant) or upon entry into (requires) or exit from
(ensures) a class method or a bare function... all without requiring the
user to keep track of a complex scaffolding of try/catch/finally blocks,
which the compiler manages for you.
It has a bit of the flavor of aspect-oriented programming, but without
AOP's vagueness about what's going to happen when and where.
-- Davidson
More information about the Digitalmars-d
mailing list