DIP 1009--Improve Contract Usability--Preliminary Review Round 1

MysticZach via Digitalmars-d digitalmars-d at puremagic.com
Tue Jun 20 17:51:11 PDT 2017


On Tuesday, 20 June 2017 at 17:42:13 UTC, H. S. Teoh wrote:
> What would a body-less declaration of a function look like 
> under the new syntax? Hopefully not this:
>
> 	int myFunc(Args...)(Args args)
> 	if (Args.length > 2)
> 	in assert(args[0] != 0);	// semicolon: ouch
> 	in assert(args[1] > 1);		// semicolon: ouch
> 	// How do we end the declaration here? Another semicolon?
> 	;	// ouch

Such declarations are only legal when declaring interfaces. An 
investigation reveals that the existing grammar actually does not 
require that extra semicolon [1]. Thus, the parser would check 
for `body` (or `do`) after the contract statement, like it 
already does anyway, and just keep parsing. That said, it's 
certainly reasonable to disallow the new syntax in virtual 
interface functions, as is also done for enhancement 3: "Virtual 
interface functions cannot use this syntax..."

But I think a more reasonable solution — and the one I prefer — 
is simply to disallow semicolon contracts outside the function 
body, which is also mentioned in enhancement 3: "Note that it's 
possible to allow enhancements 1 and 2 only within function 
bodies." What that would boil down to is that existing contracts 
remain the same. Within function bodies, they can now be 
expressed as one-liners.

> Also, I don't like the idea of putting contracts inside the 
> function body. As the DIP already mentions, this makes parsing 
> of contracts more difficult. It also causes cognitive 
> dissonance (contracts are a part of the function's signature, 
> not its implementation).

I think people could get used to the cognitive dissonance. I've 
already gotten used to it just by writing this DIP.

As for the parsing, it isn't much more difficult. The compiler 
just adds any `in` or `out` statement to the list of statements 
in the contract, creating one when necessary. The only reason it 
_might_ be more difficult is in the case of a certain kind of 
documentation parsing which I'm not sure even exists as of yet 
(somebody tell me if it does!). Such documentation parsing is 
that which actually wants to publish the contracts verbatim, as 
part of the documentation. Normally if you're scanning for 
documentation, you can speed up parsing by skipping the function 
body. If you don't need to document the contracts verbatim, then 
you can still just skip them like you would the rest of the 
function body. Only if you _did_ want to parse the contracts 
verbatim would it get more complicated. But the problems aren't 
that bad. The easiest way to find the contracts is to require 
that they occur at the top of the function. This would allow 
searching for the tokens `in` and `out` to detect contracts. 
Anything other than `in` or `out` just gets skipped.

But remember, I don't even know if this will be a problem, as in 
any other scenario, you have to parse the whole program anyway. 
The compiler just has to add  any `in` and `out` statement to the 
existing contracts as it encounters them. With the alternative 
enhancement listed in the DIP as 4 (not part of the basic 
proposal), there is a little more difficulty with something like:

int fun(int x) {
     static if(...) in assert(x);
}

...because in this case, there's no existing semantics for that 
construction, and the compiler will need to work a little magic. 
I suggested simply rewriting it as:

int fun(int x) {
     in static if(...) assert(x);
}

which lowers to:

int fun(int x)
in { static if(...) assert(x); }
body { }

Which seems to work. But of course the compiler would need to be 
able to catch what was going on and lower the syntax as 
necessary, which may or may not be trivial.

> It's even worse if you allow contracts in arbitrary places 
> inside the function body -- then even somebody reading the code 
> wouldn't know, at a glance, what the contracts are, without 
> scanning the entire function body! That makes contracts 
> *harder* to read and use, rather than easier, in direct 
> contradiction of the purpose of this DIP.

This is a strong argument in favor of the existing proposal, 
which states, under enhancement 3, "They must occur at the 
beginning of the function..."

In the Alternatives section, as enhancement 5, I mentioned the 
possibility of allowing contracts anywhere in the function. I 
don't think it's a good idea. I think you're right. But I thought 
that it was worth mentioning, at least as an alternative.

> Here's my counter-proposal: since the sig constraint line uses 
> parentheses (and yes, I deliberately planted a sig constraint 
> above just to make this point), why not go for syntactical 
> symmetry? I.e., like this:
>
> 	int myFunc(Args...)(Args args)
> 	if (Args.length > 2)
> 	in (args[0] != 0)
> 	in (args[1] > 1);	// one semicolon to end them all

This proposal has syntax and semantics too. The syntax is that 
contracts occur inside parentheses, which I have no problems 
with. The semantics, however, are very questionable, namely in 
that they now imply `assert` whereas before it had to be 
explicit. This is questionable because I simply can't imagine 
every possible programmer and code base will prefer existing 
assert-based checking to their own in-house system. If you force 
assert-based checking directly into the syntax, then such syntax 
becomes useless to those using a different system. It doesn't 
seem prudent to me. I think flexibility is preferable. In this 
case, the cost of such flexibility here is verbosity. Having to 
explicitly say `assert`, or whatever, is a cost I think D should 
bear.

> [snip]

I think the rest of your counter-proposal breaks down  on the 
weight of my argument about needing to require explicit 
`assert`s, or whatever checking system a given codebase needs to 
use. If such an alternative checking system is utilized, the 
syntax for writing contracts should be as easy for them as for 
those using `assert`.

That said, Andrei recently said he has "bigger plans for assert" 
[2], or something like that. If those plans were so big as to 
allow `assert` to become a truly one-stop shop for all things 
contract-based — and it _would_ have to be _ALL_ things, in my 
opinion, to merit being bound up directly with the syntax — then 
I think your proposal has more legs to stand on.

[1] https://dlang.org/spec/interface.html#interface-contracts
[2] https://github.com/dlang/dmd/pull/6901#issuecomment-309016307


More information about the Digitalmars-d mailing list