On Tue, Jun 20, 2017 at 11:57:55AM +0000, Mike Parker via Digitalmars-d wrote: > DIP 1009 is titled "Improve Contract Usability". > > https://github.com/dlang/DIPs/blob/master/DIPs/DIP1009.md [...]
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 Having several semicolons outside a braced block makes declarations harder to parse. External tools will no longer be able to just scan for top-level semicolons to find the end of the declaration, but will have to understand contract syntax too, even if that particular tool doesn't care about contracts. It gets worse if your contract involves calling another function; you'll end up with: int myFunc(Args...)(Args args) if (Args.length > 2) in assert(args[0] != 0); // is this the end of the declaration? in otherFunc(args[0]); // is this declaring another function? // (i.e., a typo of int -> in?) ; // ouch 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). 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. 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 also makes it possible to completely elide `body` or `do` when there's a function body, since we no longer have the possibility of two braced blocks appearing next to each other (i.e., `in { ... } { ... }` that Timon strongly objected to). So we have: int myFunc(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) { // function body here } Notice how the nice symmetry with the current syntax for functions with sig constraints. Also, getting rid of "assert" also makes this syntax less verbose, easier to type, and easier to read. Furthermore, since we will retain backward compatibility with the current verbose syntax, it's not a problem that using parentheses implies we only allow expressions inside; if you need to write, say, a for-loop in your contract, just use the old syntax: int myFunc(Args...)(Args args) if (Args.length > 2) in { // Complicated in-contract here requiring multiple // statements: just use the current braced syntax. int total; foreach (arg; args) { assert(arg > 0); total += arg; } assert(total < 1000); } do { // function body here } The idea is that one-line, single-expression contracts are the norm, and complex, multiple-statement contracts are rare. So the common case ought not need to pay for the extra syntactic load that's only rarely necessary, but the (current) verbose syntax is still available when it's actually needed. For out-contracts, we could use the existing lambda syntax to avoid the ugliness of having two parenthesized expressions next to each other: // Body-less declaration int myFunc(Args...)(Args args) if (Args.length > 1) in (args[0] > 0) out (result => result > 10); // N.B.: instead of: `out(result)(result > 10)` which is uglier. // Full declaration int myFunc(Args...)(Args args) if (Args.length > 1) in (args[0] > 0) out (result => result > 10) { ... // function body goes here } T -- Computers are like a jungle: they have monitor lizards, rams, mice, c-moss, binary trees... and bugs.