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

Reply via email to