Jump to page: 1 214  
Page
Thread overview
DIP 1009--Improve Contract Usability--Preliminary Review Round 1
Jun 20, 2017
Mike Parker
Jun 20, 2017
H. S. Teoh
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
MysticZach
Jun 21, 2017
meppl
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
meppl
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
H. S. Teoh
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
H. S. Teoh
Jun 22, 2017
MysticZach
Jun 22, 2017
H. S. Teoh
Jun 21, 2017
Moritz Maxeiner
Jun 21, 2017
MysticZach
Jun 21, 2017
Timon Gehr
Jun 21, 2017
MysticZach
Jun 22, 2017
Timon Gehr
Jun 22, 2017
MysticZach
Jun 22, 2017
H. S. Teoh
Jun 22, 2017
MysticZach
Jun 22, 2017
MysticZach
Jun 22, 2017
jmh530
Jun 22, 2017
Timon Gehr
Jun 22, 2017
H. S. Teoh
Jun 22, 2017
MysticZach
Jun 22, 2017
jmh530
Jun 22, 2017
Timon Gehr
Jun 22, 2017
jmh530
Jun 22, 2017
Timon Gehr
Jun 22, 2017
MysticZach
Jun 22, 2017
MysticZach
Jun 22, 2017
Timon Gehr
Jun 22, 2017
MysticZach
Jun 22, 2017
Timon Gehr
Jun 22, 2017
jmh530
Jun 23, 2017
Patrick Schluter
Jun 23, 2017
MysticZach
Jun 23, 2017
Solomon E
Jun 23, 2017
MysticZach
Jun 23, 2017
jmh530
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
MysticZach
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
MysticZach
Jun 23, 2017
H. S. Teoh
Jun 23, 2017
MysticZach
Jun 23, 2017
jmh530
Jun 23, 2017
Timon Gehr
Jun 23, 2017
jmh530
Jun 25, 2017
Moritz Maxeiner
Jun 25, 2017
Timon Gehr
Jun 25, 2017
Moritz Maxeiner
Jun 25, 2017
Moritz Maxeiner
Jun 25, 2017
Guillaume Boucher
Jun 25, 2017
Timon Gehr
Jun 28, 2017
Enamex
Jun 28, 2017
Moritz Maxeiner
Jun 28, 2017
Enamex
Jun 28, 2017
Moritz Maxeiner
Jun 28, 2017
MysticZach
Jun 28, 2017
jmh530
Jun 26, 2017
MysticZach
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
MysticZach
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
MysticZach
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
MysticZach
Jun 23, 2017
Moritz Maxeiner
Jun 24, 2017
MysticZach
Jun 24, 2017
Moritz Maxeiner
Jun 24, 2017
MysticZach
Jun 24, 2017
Solomon E
Jun 24, 2017
MysticZach
Jun 24, 2017
Mark
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
H. S. Teoh
Jun 27, 2017
Olivier FAURE
Jun 28, 2017
MysticZach
Jun 28, 2017
MysticZach
Jun 23, 2017
Timon Gehr
Jun 23, 2017
Moritz Maxeiner
Jun 23, 2017
MysticZach
Jun 23, 2017
H. S. Teoh
Jun 24, 2017
Timon Gehr
Jun 23, 2017
MysticZach
Jun 23, 2017
Timon Gehr
Jun 22, 2017
Moritz Maxeiner
Jun 22, 2017
MysticZach
Jun 22, 2017
MysticZach
Jun 22, 2017
MysticZach
Jun 22, 2017
Moritz Maxeiner
Jun 22, 2017
Moritz Maxeiner
Jun 22, 2017
H. S. Teoh
Jun 26, 2017
Timon Gehr
Jun 27, 2017
MysticZach
Jun 27, 2017
Wulfklaue
Jun 27, 2017
Mark
Jun 27, 2017
Moritz Maxeiner
Jun 29, 2017
Mark
Jun 29, 2017
H. S. Teoh
Jun 29, 2017
H. S. Teoh
Jun 30, 2017
Mark
Jul 04, 2017
Mike Parker
June 20, 2017
DIP 1009 is titled "Improve Contract Usability".

https://github.com/dlang/DIPs/blob/master/DIPs/DIP1009.md

All review-related feedback on and discussion of the DIP should occur in this thread. The review period will end at 11:59 PM ET on July 3 (3:59 AM GMT July 4), or when I make a post declaring it complete.

At the end of Round 1, if further review is deemed necessary, the DIP will be scheduled for another round. Otherwise, it will be queued for the formal review and evaluation by the language authors.

Thanks in advance to all who participate.

Destroy!
June 20, 2017
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.
June 20, 2017
On 6/20/17 1:42 PM, H. S. Teoh via Digitalmars-d wrote:
> 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 is much much better. The verbosity of contracts isn't really the brace, it's the asserts. This also gives the compiler a better handle on what causes the thing to fail (better error message).

Note that you NEED the semicolon for the DIP's proposal, because `in` is also a binary operator, and could be misinterpreted. With yours, it's not possible to misinterpret.

> 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
> 	}

I'm not understanding that last part. Current syntax would be:

out(result)
{
   // check result
}

I'm concerned this would be ambiguous with the current syntax.

IMO, this whole proposal doesn't carry enough weight, either your version or the DIP itself. I would not be in favor. Current syntax is understandable, and not too verbose IMO.

-Steve
June 21, 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
June 21, 2017
On Tuesday, 20 June 2017 at 21:04:16 UTC, Steven Schveighoffer wrote:
> On 6/20/17 1:42 PM, H. S. Teoh via Digitalmars-d wrote:
>> 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 is much much better. The verbosity of contracts isn't really the brace, it's the asserts.

I think it's both, and I think the brace is the only thing that can be improved upon. How could you justify insisting that everyone use the built-in asserts for their contracts? If you can, then okay, and other avenues are worth exploring. Are there not, however, many reasons to want to use other types of contracts? That's the main reason I went with my design, because I thought it was going too far to imply `assert` when people might want or need to use other things.

> This also gives the compiler a better handle on what causes the thing to fail (better error message).

I'm pretty sure that the reason asserts are so sparse in terms of error information is due to the fear of slowdown, were they to contain more information. This tradeoff would be present regardless of contract syntax.

> IMO, this whole proposal doesn't carry enough weight, either your version or the DIP itself. I would not be in favor. Current syntax is understandable, and not too verbose IMO.

That's a fair opinion. I wish I had a crystal ball to see how many more people would use contracts if this DIP were accepted. I imagine a lot of people will agree with you. I also know that some people don't [1].

http://forum.dlang.org/post/mailman.2288.1494811099.31550.digitalmars-d@puremagic.com

June 21, 2017
On Wednesday, 21 June 2017 at 01:06:40 UTC, MysticZach wrote:
> On Tuesday, 20 June 2017 at 21:04:16 UTC, Steven Schveighoffer
>
>> IMO, this whole proposal doesn't carry enough weight, either your version or the DIP itself. I would not be in favor. Current syntax is understandable, and not too verbose IMO.
>
> That's a fair opinion. I wish I had a crystal ball to see how many more people would use contracts if this DIP were accepted.

Well, one more datapoint for your prediction model, then: Not me. I like DbC, but both what D currently has (too verbose) as well as what this DIP does (see below) is not acceptable to me:
Enhancement 1 introduces significant cognitive dissonance for me and I would argue that it also introduced a language inconsistency by polluting the space between normal function signature and body with ";". My brain keeps screaming "NO" when I see it. Maybe I could get used to it, but if I allow "less than ideal (within the limits of the language, of course)" as an acceptable goal, I can just stick to checks like assert in the function body; at least I (and most people reading my code) are already familiar with that.
Enhancement 2 is fine in and of itself, but compounds the issues caused by 1
Enhancement 3 introduces even more cognitive dissonance for me by breaking the model that contracts are part of a functions signature, not its body. I can also already do checks in the function body as shown in the following, this enhancement is (at best) superfluous to me:

---
int myFunc(Args...)(Args args)
  if (Args.length > 2)
{
    assert (args[0] != 0);
    assert (args[1] > 1);
    int result;
    scope (success) assert (result > 0);
}
---

vs DIP Enhancement 3

---
int myFunc(Args...)(Args args)
  if (Args.length > 2)
{
    in assert (args[0] != 0);
    in assert (args[1] > 1);
    out (result) assert (result > 0);
}
---

What *I* need from a DIP that addresses DbC in D (to make it viable for me) is to make the simple case as easy as possible to read while not introducing language inconsistencies.
With that in mind I am strongly in favor of the syntax H. S. Teoh already proposed:

---
int myFunc(Args...)(Args args)
  if (Args.length > 2)
  in (args[0] != 0)
  in (args[1] > 1)
  out (result => result > 0);

int myFunc(Args...)(Args args)
  if (Args.length > 2)
  in (args[0] != 0)
  in (args[1] > 1)
  out (result => result > 0) { ... }
---

- in contracts take a parenthesis delimited bool expression
- out contracts take a parenthesis delimited bool function literal.
June 20, 2017
On Wed, Jun 21, 2017 at 01:06:40AM +0000, MysticZach via Digitalmars-d wrote:
> On Tuesday, 20 June 2017 at 21:04:16 UTC, Steven Schveighoffer wrote:
> > On 6/20/17 1:42 PM, H. S. Teoh via Digitalmars-d wrote:
> > > 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 is much much better. The verbosity of contracts isn't really the brace, it's the asserts.
> 
> I think it's both, and I think the brace is the only thing that can be improved upon. How could you justify insisting that everyone use the built-in asserts for their contracts?
[...]

Umm... I think we're not quite on the same page here.  What *else* are people supposed to use inside their contracts besides the built-in assert??

While D currently gives you the flexibility of arbitrary code inside a contract, a contract is not supposed to do anything other than to verify that the caller has passed in arguments that are valid.[1] Furthermore, contracts specify what constitutes valid input to the function -- this serves both as documentation to would-be callers, and also as specification to the compiler as to what are acceptable arguments.  The built-in assert mechanism serves both purposes -- especially the second purpose because the compiler understands it directly, as opposed to some other user-defined mechanism that the compiler wouldn't understand.

([1] The current implementation lets you do crazy things like modifying global state from inside a contract, but that's not the purpose of contracts, and I argue that such uses are abusive.  In any case, the current syntax isn't going away, so even if people somehow come up with a legit reason for doing such strange things, they still can.)

Besides, this is a matter of semantics, whereas this DIP is addressing the DbC syntax.  If people demand an alternative to the built-in assert, it's not hard to have the compiler lower the syntax into some globally-defined symbol that can be redefined by the user. Or, indeed, simply funnel the expression into a user-defined assert alternative, e.g.:

	int myFunc(Args...)(Args args)
	if (args.length > 1)
	in (myAssert(args[0] > 0))
	{
		return doStuff(args);
	}

	bool myAssert(T)(T t) {
		// do whatever alternative assert stuff you need to do
		// here

		return true; // bypass the built-in assert
	}

But the semantics is a different issue than the syntax, which is the scope of this DIP.


T

-- 
Never trust an operating system you don't have source for! -- Martin Schulze
June 21, 2017
On Wednesday, 21 June 2017 at 04:16:22 UTC, Moritz Maxeiner wrote:
> What *I* need from a DIP that addresses DbC in D (to make it viable for me) is to make the simple case as easy as possible to read while not introducing language inconsistencies.
> With that in mind I am strongly in favor of the syntax H. S. Teoh already proposed:
>
> ---
> int myFunc(Args...)(Args args)
>   if (Args.length > 2)
>   in (args[0] != 0)
>   in (args[1] > 1)
>   out (result => result > 0);
>
> int myFunc(Args...)(Args args)
>   if (Args.length > 2)
>   in (args[0] != 0)
>   in (args[1] > 1)
>   out (result => result > 0) { ... }
> ---
>
> - in contracts take a parenthesis delimited bool expression
> - out contracts take a parenthesis delimited bool function literal.

`out` contracts would also have to account for the case where they don't check the return value. This would confuse the grammar a little in the case of function literals as boolean expressions. A different possible grammar that wouldn't have this problem is:

OutStatement:
    out ( IfCondition )
    out ( Identifier ) ( IfCondition )

plus the existing ones:

OutStatement:
    out BlockStatement
    out ( Identifier ) BlockStatement

June 21, 2017
On Wednesday, 21 June 2017 at 05:19:26 UTC, H. S. Teoh wrote:
> On Wed, Jun 21, 2017 at 01:06:40AM +0000, MysticZach via Digitalmars-d wrote:
>> On Tuesday, 20 June 2017 at 21:04:16 UTC, Steven Schveighoffer wrote:
>> > This is much much better. The verbosity of contracts isn't really the brace, it's the asserts.
>> 
>> I think it's both, and I think the brace is the only thing that can be improved upon. How could you justify insisting that everyone use the built-in asserts for their contracts?
> [...]
>
> Umm... I think we're not quite on the same page here.  What *else* are people supposed to use inside their contracts besides the built-in assert??

Many people have expressed discontent with existing asserts. In fact, they were just changed yesterday to address one of these concerns:

https://github.com/dlang/dmd/pull/6901

I believe `assert` would have to be extremely robust to merit being included directly into the syntax of the language. I'm not opposed to this in principle. But I'm no expert, and not willing to assume it's desirable. On the other hand, if `assert` were made so perfect as to ensure that no one would prefer a different method of bailing out of their programs, then you're right, and the problem of contract syntax could be solved at that level instead of the more "pedestrian" approach I'm taking.

> While D currently gives you the flexibility of arbitrary code inside a contract, a contract is not supposed to do anything other than to verify that the caller has passed in arguments that are valid.[1]

To me, it's still a question of whether `assert` is the only valid way to bail out of a program. I agree that arbitrary other code inside contracts is bad practice, and that wanting to prohibit it makes sense.

> Furthermore, contracts specify what constitutes valid input to the function -- this serves both as documentation to would-be callers, and also as specification to the compiler as to what are acceptable arguments.  The built-in assert mechanism serves both purposes -- especially the second purpose because the compiler understands it directly, as opposed to some other user-defined mechanism that the compiler wouldn't understand.

This wouldn't change just with a syntax rewrite. If the compiler wanted to use `assert` for optimization purposes, it could do that just as well with any of the proposed syntaxes. People who didn't want to use `assert` would be at a disadvantage in this regard. But at least they would have that option.

> Besides, this is a matter of semantics, whereas this DIP is addressing the DbC syntax.  If people demand an alternative to the built-in assert, it's not hard to have the compiler lower the syntax into some globally-defined symbol that can be redefined by the user. Or, indeed, simply funnel the expression into a user-defined assert alternative, e.g.:
>
> 	int myFunc(Args...)(Args args)
> 	if (args.length > 1)
> 	in (myAssert(args[0] > 0))
> 	{
> 		return doStuff(args);
> 	}
>
> 	bool myAssert(T)(T t) {
> 		// do whatever alternative assert stuff you need to do
> 		// here
>
> 		return true; // bypass the built-in assert
> 	}

I guess the optimizer can elide the assert if it knows it's always true. If this method was sure not to incur a performance penalty, then it may be better than my approach.

> But the semantics is a different issue than the syntax, which is the scope of this DIP.

The scope of the DIP can change, if need be. My primary goal is in the title, to "Improve Contract Usability". I used this title on purpose because I didn't want to get stuck if my proposal turned out to be worse than some other one. If adding a new semantics is actually preferable I can rewrite the DIP (and give you credit — or you can do it yourself, if you want). It was just as important to me to get the ball rolling as to have my particular suggestion accepted. I wanted to stay close to the shore, because I thought it was a little outlandish to propose a whole new semantics. I still think it's a little outlandish, because I can imagine a large organization wanting to rig up its own bailout mechanism, and the new semantics would prevent them from doing that. But so far, the comments suggest that it's worth it to many people.

June 21, 2017
On Wednesday, 21 June 2017 at 08:15:34 UTC, MysticZach wrote:
> On Wednesday, 21 June 2017 at 04:16:22 UTC, Moritz Maxeiner wrote:
>> int myFunc(Args...)(Args args)
>>   if (Args.length > 2)
>>   in (args[0] != 0)
>>   in (args[1] > 1)
>>   out (result => result > 0) { ... }
>> ---
>>
>> - in contracts take a parenthesis delimited bool expression
>> - out contracts take a parenthesis delimited bool function literal.
>
> `out` contracts would also have to account for the case where they don't check the return value. This would confuse the grammar a little in the case of function literals as boolean expressions. A different possible grammar that wouldn't have this problem is:
>
> OutStatement:
>     out ( IfCondition )
>     out ( Identifier ) ( IfCondition )
>
> plus the existing ones:
>
> OutStatement:
>     out BlockStatement
>     out ( Identifier ) BlockStatement

I may have spoke too soon. If the grammar were:

OutStatement:
    out ( IfCondition )
    out ( FunctionLiteral )

It might still work okay. A little hiccup in the semantics, figuring out that the first parameter to the function literal is meant to be the return identifier. Maybe it's not a big deal.
« First   ‹ Prev
1 2 3 4 5 6 7 8 9 10 11