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.

Reply via email to