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