www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP 1009--Improve Contract Usability--Preliminary Review Round 1

reply Mike Parker <aldacron gmail.com> writes:
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!
Jun 20
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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.
Jun 20
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Jun 20
parent reply MysticZach <reachzach ggmail.com> writes:
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
Jun 20
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
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.
Jun 20
parent reply MysticZach <reachzach ggmail.com> writes:
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
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
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.
Jun 21
parent reply meppl <mephisto nordhoff-online.de> writes:
On Wednesday, 21 June 2017 at 09:27:20 UTC, MysticZach wrote:
 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) { ... }
avoiding the "anonymous scope"-extra wouldnt hurt much? int myFunc(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out (result => result > 0) do { ... }
Jun 21
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 09:53:40 UTC, meppl wrote:
 On Wednesday, 21 June 2017 at 09:27:20 UTC, MysticZach wrote:
 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) { ... }
avoiding the "anonymous scope"-extra wouldnt hurt much? int myFunc(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out (result => result > 0) do { ... }
Adding `if (...)` should not be different from adding `in (...)` or `out (...)` in terms of syntax rules: it's inconsistent. If you want to have that `do` there, I would argue that it should also become required if only an `if (...)` is present, so --- int myFunc(Args...)(Args args) if (Args.length > 2) { ... } --- should then become illegal and must be rewritten as --- int myFunc(Args...)(Args args) if (Args.length > 2) do { ... } --- I doubt that's going to happen, though (too much code breakage), and I also don't like it. Simply drop the `do`.
Jun 21
next sibling parent reply meppl <mephisto nordhoff-online.de> writes:
On Wednesday, 21 June 2017 at 12:05:55 UTC, Moritz Maxeiner wrote:
 On Wednesday, 21 June 2017 at 09:53:40 UTC, meppl wrote:
 On Wednesday, 21 June 2017 at 09:27:20 UTC, MysticZach wrote:
 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) { ... }
avoiding the "anonymous scope"-extra wouldnt hurt much? int myFunc(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out (result => result > 0) do { ... }
Adding `if (...)` should not be different from adding `in (...)` or `out (...)` in terms of syntax rules: it's inconsistent. If you want to have that `do` there, I would argue that it should also become required if only an `if (...)` is present, so --- int myFunc(Args...)(Args args) if (Args.length > 2) { ... } --- should then become illegal and must be rewritten as --- int myFunc(Args...)(Args args) if (Args.length > 2) do { ... } --- I doubt that's going to happen, though (too much code breakage), and I also don't like it. Simply drop the `do`.
yeah, i was probably not thinking too carefully about it. My idea was to keep the code readable, if the contracts are long. but as long as the "do" must appear behind a '}', everything is still fine, more or less. int myFunc1(Args...)(Args args) if (Args.length > 2) in { // much code } do { ... } int myFunc2(Args...)(Args args) if (Args.length > 2) in { // much code } out (result => result > 0) { ... } both are readable, but one time we write `do` and the other time we dont. furthermore the second function body looks a little bit like belonging to "out". someone who is learning the D-language might get confused.
Jun 21
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 12:43:46 UTC, meppl wrote:
 yeah, i was probably not thinking too carefully about it. My 
 idea was to keep the code readable, if the contracts are long. 
 but as long as the "do" must appear behind a '}', everything is 
 still fine, more or less.

 both are readable, but one time we write `do` and the other 
 time we dont. furthermore the second function body looks a 
 little bit like belonging to "out". someone who is learning the 
 D-language might get confused.
Oh, I hadn't thought about mixing the two syntax forms. I would just forbid it. Your signature then uses *either* the verbose form, *or* the compact form.
Jun 21
prev sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 12:05:55 UTC, Moritz Maxeiner wrote:
 On Wednesday, 21 June 2017 at 09:53:40 UTC, meppl wrote:
 On Wednesday, 21 June 2017 at 09:27:20 UTC, MysticZach wrote:
 On Wednesday, 21 June 2017 at 08:15:34 UTC, MysticZach wrote:
 On Wednesday, 21 June 2017 at 04:16:22 UTC, Moritz Maxeiner
Adding `if (...)` should not be different from adding `in (...)` or `out (...)` in terms of syntax rules: it's inconsistent. If you want to have that `do` there, I would argue that it should also become required if only an `if (...)` is present, so --- int myFunc(Args...)(Args args) if (Args.length > 2) { ... } --- should then become illegal and must be rewritten as --- int myFunc(Args...)(Args args) if (Args.length > 2) do { ... } --- I doubt that's going to happen, though (too much code breakage), and I also don't like it. Simply drop the `do`.
I tend to agree. I think the grammar for `out` contracts is still murky, though, because the normal existing case is: OutStatement: out ( Identifier ) { OutContractsBody } out { OutContractsBody } My fix would be to require two sets of parentheses for the new conditional, like so: OutStatement: ... // new version out ( Identifier ) ( IfCondition ) out ( ) ( IfCondition ) This makes the grammar unambiguous and clean. And because normally `out` contracts want to check the return value, the last case, `out ( ) ( ... )` will be the rare one. If you do accidentally forget the extra set of parens on the `out` contract, you would get "Error: `do` expected before function body after a bracketed `out` contract" at the end of the function. (If, however, it a happens to be a nested function, and the next statement in that function happens to be `do`, then the parser will think the `do` loop is the function body... Mmmm, is this worth worrying about??)
Jun 21
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 13:11:10 UTC, MysticZach wrote:
 [...]

 My fix would be to require two sets of parentheses for the new 
 conditional, like so:

 OutStatement:
    ...
    // new version
    out ( Identifier ) ( IfCondition )
    out ( ) ( IfCondition )

 This makes the grammar unambiguous and clean. And because 
 normally `out` contracts want to check the return value, the 
 last case, `out ( ) ( ... )` will be the rare one.
If I read that correctly as --- int myFunc(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out (result)(result > 0) { ... } --- then yeah, I would be happy with that, as well (and would love for the DIP to do this instead).
 If you do accidentally forget the extra set of parens on the 
 `out` contract, you would get "Error: `do` expected before 
 function body after a bracketed `out` contract" at the end of 
 the function.

 (If, however, it a happens to be a nested function, and the 
 next statement in that function happens to be `do`, then the 
 parser will think the `do` loop is the function body... Mmmm, 
 is this worth worrying about??)
Could you give a specific (short) example of what you think of? I don't see any potential for ambiguity in the following at a quick glance, so I'm not sure I got where you think the problem lies: --- void foo() { int bar(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out (result)(result > 0) { ... } do {} while (true); } ---
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 14:22:52 UTC, Moritz Maxeiner wrote:
 If you do accidentally forget the extra set of parens on the 
 `out` contract, you would get "Error: `do` expected before 
 function body after a bracketed `out` contract" at the end of 
 the function.

 (If, however, it a happens to be a nested function, and the 
 next statement in that function happens to be `do`, then the 
 parser will think the `do` loop is the function body... Mmmm, 
 is this worth worrying about??)
Could you give a specific (short) example of what you think of? I don't see any potential for ambiguity in the following at a quick glance, so I'm not sure I got where you think the problem lies: --- void foo() { int bar(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out (result)(result > 0) { ... } do {} while (true); } ---
The only problem is if a programmer forgets to add the additional parentheses, in which case the bracketed block is then mistaken for the `out` contract, which will lead to a confusing error message. For example: void foo() { int bar(Args...)(Args args) if (Args.length > 2) in (args[0] != 0) in (args[1] > 1) out /*whoops, forgot `( )`*/(result) { ... } do { ... } while (true); // Error: while statement cannot contain just `;` } Honestly this doesn't seem like a big deal, as I'd imagine it'd be hard not to notice that code like this wasn't working as expected. And also extremely rare. So I'm still in favor.
Jun 21
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 16:23:53 UTC, MysticZach wrote:
 void foo()
 {
    int bar(Args...)(Args args)
       if (Args.length > 2)
       in (args[0] != 0)
       in (args[1] > 1)
       out /*whoops, forgot `( )`*/(result) { ... }

    do { ... }
    while (true); // Error: while statement cannot contain just 
 `;`
 }

 Honestly this doesn't seem like a big deal, as I'd imagine it'd 
 be hard not to notice that code like this wasn't working as 
 expected. And also extremely rare. So I'm still in favor.
I see. Well, I would only see that as an issue if it would compile and then generate code that differs from what one intended. But since it errors out, I don't see a problem, either, though it would be best to enhance that error message to give a hint that the programmer may have triggered that ambiguous case by accident. Something like "Did you mean `out() (result)`?"
Jun 21
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
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
Jun 20
parent reply MysticZach <reachzach ggmail.com> writes:
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.
Jun 21
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 09:10:33 UTC, MysticZach wrote:
 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
Just my two cents, again: DbC prohibits broken contracts, i.e. any violation is a bug. From my point of view, a broken contract in the simple syntax as proposed by H.S.Teoh, should by default - in debug mode: Give you debug information (stack trace, etc.) then terminate - in release mode: Print file and line number and then terminate No cleanup by default. People I've observed voicing issues with assert have happened to fall into one of these three categories: - People who agree with the terminate approach, but want custom cleanup I remain skeptical about the sanity of calling arbitrary code with the knowledge of a previously triggered bug (in release mode), but that can be easily addressed by allowing to register a hook that gets called on contract violations; the process will still terminate after the hook is finished, though. - People whose use cases supposedly allows recovery of bugs These are niche cases that can be covered by the preexisting verbose syntax: `in { if (!cond) throw Exception(); }`. I would simply not include support for that in the easy-to-read variant. - People who use assert (or other Errors) for user input validation That's not what they are for. To sum it up, I would like semantics similar to this (simplified code) for this syntax: --- void delegate() nothrow onContractViolation; void validateContract(bool)(lazy bool cond) { version (CheckContracts) if (!cond) { import core.stdc.stdlib : _Exit; debug printDebugInfo(...); else printFileAndLIne(); { if (onContractViolation !is null) onContractViolation(); _Exit(1); } else { print } } } ---
Jun 21
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 11:31:41 UTC, Moritz Maxeiner wrote:
 [...]
Sorry for double post, please ignore this one.
Jun 21
prev sibling next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 09:10:33 UTC, MysticZach wrote:
 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
Just my two cents, again: DbC prohibits broken contracts, i.e. any violation is a bug. From my point of view, a broken contract in the simple syntax as proposed by H.S.Teoh, should by default - in debug mode: Give you debug information (stack trace, etc.) then terminate - in release mode: Print file and line number and then terminate No cleanup by default. People I've observed voicing issues with assert have happened to fall into one of these three categories: - People who agree with the terminate approach, but want custom cleanup I remain skeptical about the sanity of calling arbitrary code with the knowledge of a previously triggered bug (in release mode), but that can be easily addressed by allowing to register a hook that gets called on contract violations; the process will still terminate after the hook is finished, though. - People whose use cases (allegedly) allows recovery of bugs I would consider these as niche cases that are already covered by the preexisting verbose syntax: `in { if (!cond) throw Exception(); }`. I would simply not include support for that in the easy-to-read variant. - People who use assert (or other Errors) for user input validation That's not what they are for. To sum it up, I would like semantics similar to this (simplified) code for this syntax: --- void delegate() nothrow onContractViolation; template validateContract(T) if (isInputContract!T || isOutputContract!T) { version (ValidateContracts) void violated(ref T contract) { import core.stdc.stdlib : _Exit; debug printDebugInfo(contract); else printFileAndLIne(contract); if (onContractViolation !is null) onContractViolation(); _Exit(1); // without scope guard since onContractViolation is `nothrow` } static if (isInputContract!T) { void validateContract(T contract) { version (ValidateContracts) if (!contract) violated(contract); } } else { void validateContract(T contract, T.ResultType result) { version (ValidateContracts) if (!contract(result)) violated(contract); } } } ---
Jun 21
prev sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 09:10:33 UTC, MysticZach wrote:
 On Wednesday, 21 June 2017 at 05:19:26 UTC, H. S. Teoh wrote:
 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??
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.
So weird how this discussion is happening in parallel with this other discussion :-) : http://forum.dlang.org/post/rkdpuuggltowhqmcmmke forum.dlang.org
Jun 21
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 13:24:24 UTC, MysticZach wrote:
 So weird how this discussion is happening in parallel with this 
 other discussion :-) :

 http://forum.dlang.org/post/rkdpuuggltowhqmcmmke forum.dlang.org
It is, though as I have pointer out over there, I would really like to decouple assert semantics from the new contract semantics to achieve the maximum elegance we can within the design limits of the language.
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 14:49:09 UTC, Moritz Maxeiner wrote:
 On Wednesday, 21 June 2017 at 13:24:24 UTC, MysticZach wrote:
 So weird how this discussion is happening in parallel with 
 this other discussion :-) :

 http://forum.dlang.org/post/rkdpuuggltowhqmcmmke forum.dlang.org
It is, though as I have pointer out over there, I would really like to decouple assert semantics from the new contract semantics to achieve the maximum elegance we can within the design limits of the language.
Well I'd be just as happy expanding the design limits of the language, i.e. `assert`, if that were a better option. The issue you raise is just how different are `in` and `out` contracts from regular `assert` contracts. They _are_ all listed in the same section of the documentation [1], for whatever that's worth. The practical question is whether one can assume that the same semantics used for `assert`, whatever they may be, will in all cases be desirable for `in` and `out` too. The flexibility of decoupling them is one solution, if they are clearly sufficiently different. But another option is simply to upgrade `assert` to make sure it offers what everyone wants. I don't know what to suggest, because I don't if `assert` can be made good enough to warrant direct inclusion into the grammar. If it could, then all contracts, including plain `assert` statements, would benefit from them. [1] https://dlang.org/spec/contracts.html
Jun 21
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 16:52:24 UTC, MysticZach wrote:
 Well I'd be just as happy expanding the design limits of the 
 language, i.e. `assert`, if that were a better option. The 
 issue you raise is just how different are `in` and `out` 
 contracts from regular `assert` contracts.
Well, a contract is an abstraction, asserts are just one possible implementation; so yeah, they are different things. Technically in the current (verbose) syntax, the `assert` just happen to be part of the contract's implementation; it could really be just anything, including `in { if (!cond) throw Exception }`.
 They _are_ all listed in the same section of the documentation 
 [1], for whatever that's worth.
Well, yeah, because assert's are the most common contract implementation in D.
 The practical question is whether one can assume that the same 
 semantics used for `assert`, whatever they may be, will in all 
 cases be desirable for `in` and `out` too. The flexibility of 
 decoupling them is one solution, if they are clearly 
 sufficiently different.
They are (see abstraction vs implementation argument above). We can just as well use another implementation in the new syntax and then we don't have to worry about asserts semantics possibly changing in the future anymore. Also, people arguing for changes in assert semantics don't have to care about contracts if they don't also do DbC. I think decoupling is the way to go here.
 But another option is simply to upgrade `assert` to make sure 
 it offers what everyone wants.
That would be really cool, but I doubt it will be feasible here. I think that in this case it will more likely end up with everyone hating that universal solution equally.
 I don't know what to suggest, because I don't if `assert` can 
 be made good enough to warrant direct inclusion into the 
 grammar. If it could, then all contracts, including plain 
 `assert` statements, would benefit from them.
Well, for me it would be this: - The compact syntax with `in (cond)` and `out (ident)(cond)` - Don't lower those contracts directly to any fixed implementation; lower them to a template (similar to what I posted) that you stick in druntime instead - Allow configuring the template's checking semantics via D versions (you can still then have one implementation choice in the template be asserts, if you want, though I would go with a more riguruous kill switch such as the one I posted) I would definitely be interested in helping out with the template implementation for druntime, btw.
Jun 21
next sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 17:38:02 UTC, Moritz Maxeiner wrote:
 But another option is simply to upgrade `assert` to make sure 
 it offers what everyone wants.
That would be really cool, but I doubt it will be feasible here. I think that in this case it will more likely end up with everyone hating that universal solution equally.
Well the universal solution could be, for example, the suggestions from my post in the other thread: 3. Allow an additional compiler flag for more informative, but heavier asserts, e.g. `-release=informativeAsserts`. 4. Allow a pragma in the code, e.g. `pragma(asserts, none/regular/informative)` for what kinds of asserts are to be used at a given moment. This would be added flexibility, rather than a one-size-fits-all solution. So the word "universal" is a little deceptive. The options could also include a user-defined hook for assert. Question: If `assert` itself allowed a user-defined hook, what would the remaining justification be for decoupling `in` and `out` contracts from the `assert` logic? That's what I mean by thinking the problem might be fixable by upgrading `assert`.
Jun 21
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 17:55:05 UTC, MysticZach wrote:
 This would be added flexibility, rather than a 
 one-size-fits-all solution. So the word "universal" is a little 
 deceptive. The options could also include a user-defined hook 
 for assert.

 Question: If `assert` itself allowed a user-defined hook, what 
 would the remaining justification be for decoupling `in` and 
 `out` contracts from the `assert` logic?
Because then you won't have normal asserts and contracts be subject to different semantics? If I use a library, I may very well want to disable the library's internal assert checks (because I have enough experience that it's working properly), but keep it's contracts alive, because my code is still shiny new and riddled with bugs.
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 18:04:07 UTC, Moritz Maxeiner wrote:
 On Wednesday, 21 June 2017 at 17:55:05 UTC, MysticZach wrote:
 Question: If `assert` itself allowed a user-defined hook, what 
 would the remaining justification be for decoupling `in` and 
 `out` contracts from the `assert` logic?
Because then you won't have normal asserts and contracts be subject to different semantics? If I use a library, I may very well want to disable the library's internal assert checks (because I have enough experience that it's working properly), but keep it's contracts alive, because my code is still shiny new and riddled with bugs.
Timon appears to think that the checking logic is more monolithic. From his reply above [1]: "If [an alternative checking system is utilized], there should be a way to hook into the checking logic. This has nothing at all to do with contract syntax. asserts and contracts are coupled already, as in-contracts form a disjunction on override by catching AssertErrors." So I'm hoping more people will weigh in on this issue. For example, how easy is it to separate a library's internal contracts from its external ones? Would you have to write internal contracts in a different way from the ones facing the user? How often is this distinction the one causing problems with productivity? The way I'm thinking about it would be that if there were a pragma to turn off contracts in a particularly hot code path, then the rest of the program could remain safe, while the fast part was allowed to go as fast as possible, addressing the performance issue. But regarding the information issue, what kind of error information is better delivered specifically through compiler knowledge of `in` and `out` contracts, versus what it would deliver in the same way via regular `assert`s? Or are all contracts basically just fancy sugar for asserts at the beginning and end of a function body? What can the compiler do with the extra information? What can it say to the user that the user wouldn't already be able to figure out if it were a regular assert? [1] http://forum.dlang.org/post/oie2nt$emf$1 digitalmars.com
Jun 21
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Wed, Jun 21, 2017 at 07:18:18PM +0000, MysticZach via Digitalmars-d wrote:
 [...] Or are all contracts basically just fancy sugar for asserts at
 the beginning and end of a function body?
[...] This is a sticky point about D's current DbC implementation that myself and several others feel is a design flaw. In particular, that in-contracts are executed as part of the *callee*, when the intent of DbC is really that it is the obligation of the *caller* to fulfill its stipulations, and therefore the contract verification should happen at *caller* site rather than at the beginning of the callee. This particular implementation detail causes problems with binary-only libraries: most library vendors would prefer to ship the library compiled with -release rather than not, but in -release, the asserts in any in-contracts would be elided, making them essentially non-existent by the user uses the library. So you either have to dispense with DbC altogether, or be forced to ship two versions of your library, one with contracts compiled in and one without, in order for your users to benefit from DbC *and* not have to suffer performance penalties in their own release builds. Had in-contracts been implemented on the caller's side instead, this would no longer be a problem: the contracts will still be part of the library API, so the user can benefit from them when not compiling with -release, but now the library itself can be shipped only with the binaries compiled with -release for best performance. This is probably something outside the scope of this DIP, however. T -- Political correctness: socially-sanctioned hypocrisy.
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 19:34:53 UTC, H. S. Teoh wrote:
 This is a sticky point about D's current DbC implementation 
 that myself and several others feel is a design flaw. In 
 particular, that in-contracts are executed as part of the 
 *callee*, when the intent of DbC is really that it is the 
 obligation of the *caller* to fulfill its stipulations, and 
 therefore the contract verification should happen at *caller* 
 site rather than at the beginning of the callee.

 This particular implementation detail causes problems with 
 binary-only libraries: most library vendors would prefer to 
 ship the library compiled with -release rather than not, but in 
 -release, the asserts in any in-contracts would be elided, 
 making them essentially non-existent by the user uses the 
 library.  So you either have to dispense with DbC altogether, 
 or be forced to ship two versions of your library, one with 
 contracts compiled in and one without, in order for your users 
 to benefit from DbC *and* not have to suffer performance 
 penalties in their own release builds.

 Had in-contracts been implemented on the caller's side instead, 
 this would no longer be a problem: the contracts will still be 
 part of the library API, so the user can benefit from them when 
 not compiling with -release, but now the library itself can be 
 shipped only with the binaries compiled with -release for best 
 performance.

 This is probably something outside the scope of this DIP, 
 however.
It's related. A design flaw in D's DbC means that contracts on the whole are less important. Which means, unfortunately, that improving them is less important. Which could affect the final decision. But on the other side, binary-only libraries published in release mode are actually rare in D, right?
Jun 21
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jun 22, 2017 at 06:17:54AM +0000, MysticZach via Digitalmars-d wrote:
 On Wednesday, 21 June 2017 at 19:34:53 UTC, H. S. Teoh wrote:
 This is a sticky point about D's current DbC implementation that
 myself and several others feel is a design flaw. In particular, that
 in-contracts are executed as part of the *callee*, when the intent
 of DbC is really that it is the obligation of the *caller* to
 fulfill its stipulations, and therefore the contract verification
 should happen at *caller* site rather than at the beginning of the
 callee.
 
 This particular implementation detail causes problems with
 binary-only libraries: most library vendors would prefer to ship the
 library compiled with -release rather than not, but in -release, the
 asserts in any in-contracts would be elided, making them essentially
 non-existent by the user uses the library.  So you either have to
 dispense with DbC altogether, or be forced to ship two versions of
 your library, one with contracts compiled in and one without, in
 order for your users to benefit from DbC *and* not have to suffer
 performance penalties in their own release builds.
[...]
 But on the other side, binary-only libraries published in release mode
 are actually rare in D, right?
Rare or not, if we want D to grow, we cannot ignore proprietary usage, which would most likely involve binary-only libraries. Precluding this (or at the very least discouraging it) just because of a design flaw in D's implementation of DbC would be a grave strategic error on our part. T -- Debian GNU/Linux: Cray on your desktop.
Jun 21
prev sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 21 June 2017 at 17:38:02 UTC, Moritz Maxeiner wrote:
 Well, for me it would be this:
 - The compact syntax with `in (cond)` and `out (ident)(cond)`
 - Don't lower those contracts directly to any fixed 
 implementation; lower them to a template (similar to what I 
 posted) that you stick in druntime instead
 - Allow configuring the template's checking semantics via D 
 versions (you can still then have one implementation choice in 
 the template be asserts, if you want, though I would go with a 
 more riguruous kill switch such as the one I posted)

 I would definitely be interested in helping out with the 
 template implementation for druntime, btw.
I think this should be taken even further and follow the footsteps of std.experimental.allocator (being thus consistent with what's becoming idiomatic D): - Introduce the abstract concept of an Allocator analogue called "Insister", which can provide DbI-detectable "insist" functions (e.g. for in contracts, for out contracts, etc.) - Introduce an interface like IInsister akin to IAllocator, with insisterObject akin to allocatorObject - Create a thread-local `IInsister theInsister` that can be swapped out at will - Lower contracts to calls to `theInsister.insist(...)` - Create some Insister implementations and load theInsister up with one that uses `assert` semantics
Jun 21
prev sibling parent reply MysticZach <reachzach ggmail.com> writes:
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
Jun 20
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 21.06.2017 02:51, MysticZach wrote:
 
 I think people could get used to the cognitive dissonance.
That's really not what D is about.
 I've already gotten used to it just by writing this DIP.
I think it is likely that you are an outlier.
 If such an alternative checking system is utilized,
If so, there should be a way to hook into the checking logic. This has nothing at all to do with contract syntax. asserts and contracts are coupled already, as in-contracts form a disjunction on override by catching AssertErrors.
 the syntax for  writing contracts should be as easy
 for them as for those using `assert`. 
Maybe, but your DIP does not pull its own weight as long as the latter syntax is not a notable improvement over what we have now. H. S. Teoh's counter-proposal is, and I think your DIP has a much higher chance of acceptance if you go with it.
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Wednesday, 21 June 2017 at 15:18:21 UTC, Timon Gehr wrote:
 On 21.06.2017 02:51, MysticZach wrote:
 
 I think people could get used to the cognitive dissonance.
That's really not what D is about.
My counterargument to that is that it's possible that the cognitive dissonance only occurs because of what people are used to, rather than what is inherent to the syntax.
 I've already gotten used to it just by writing this DIP.
I think it is likely that you are an outlier.
Well my impression was that Walter liked it too, although I could have misinterpreted his comment here: http://forum.dlang.org/post/ogvt66$1bcp$1 digitalmars.com
 If such an alternative checking system is utilized,
If so, there should be a way to hook into the checking logic. This has nothing at all to do with contract syntax. asserts and contracts are coupled already, as in-contracts form a disjunction on override by catching AssertErrors.
Improving the checking logic interface may solve at a higher level the problem I'm trying to solve at a very low one, with mere syntax changes, and it might be (is probably?) the best way forward.
 the syntax for  writing contracts should be as easy
 for them as for those using `assert`.
Maybe, but your DIP does not pull its own weight as long as the latter syntax is not a notable improvement over what we have now.
Well, my view is that my DIP is actually a very lightweight syntax change, and an equally lightweight improvement in contract syntax, so it's a lost-cost, mild improvement . The cognitive dissonance argument is the main one against it, and I really don't know if that dissonance is based on fundamental flaws in the way I'm thinking about it, or just the "Shock of the New" [1]. If allowing contracts in the function body really is a fundamentally flawed concept, then I won't keep advocating for it.
 H. S. Teoh's counter-proposal is, and I think your DIP has a 
 much higher chance of acceptance if you go with it.
I'm not actually worried about whether the proposal is accepted or not, as long the best ideas and arguments come forward and are heard. I have more faith in the process than I do in any particular proposal. As far as Teoh's proposal, I would say that its quality is highly correlated to the percentage of projects that find built-in `assert` adequate to their needs, which is hard to assess precisely - the better `assert` is, or can be made to be, the better Teoh's proposal is, I'd say. Moritz [2] suggests solving the problem by decoupling `in` and `out` contract syntax from the checking logic. This seems like a good way to go too. But I'd like to see a little more justification for it. [1] Robert Hughes documentary, "The Shock of the New" https://www.youtube.com/watch?v=J3ne7Udaetg [2] http://forum.dlang.org/post/uzzwmgqoqxuxhusjvlcg forum.dlang.org
Jun 21
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 21.06.2017 19:39, MysticZach wrote:
 On Wednesday, 21 June 2017 at 15:18:21 UTC, Timon Gehr wrote:
 On 21.06.2017 02:51, MysticZach wrote:
 I think people could get used to the cognitive dissonance.
That's really not what D is about.
My counterargument to that is that it's possible that the cognitive dissonance only occurs because of what people are used to, rather than what is inherent to the syntax. ...
This is a purely philosophical distinction without empirical basis.
 I've already gotten used to it just by writing this DIP.
I think it is likely that you are an outlier.
Well my impression was that Walter liked it too, although I could have misinterpreted his comment here: http://forum.dlang.org/post/ogvt66$1bcp$1 digitalmars.com ...
He is saying it is good that a DIP to improve contract syntax /exists/. I agree with that.
 If such an alternative checking system is utilized,
If so, there should be a way to hook into the checking logic. This has nothing at all to do with contract syntax. asserts and contracts are coupled already, as in-contracts form a disjunction on override by catching AssertErrors.
Improving the checking logic interface may solve at a higher level the problem I'm trying to solve at a very low one, with mere syntax changes, and it might be (is probably?) the best way forward. ...
Your proposal does not solve this problem, and there is no need for this DIP to do that.
 the syntax for  writing contracts should be as easy
 for them as for those using `assert`.
Maybe, but your DIP does not pull its own weight as long as the latter syntax is not a notable improvement over what we have now.
Well, my view is that my DIP is actually a very lightweight syntax change, and an equally lightweight improvement in contract syntax, so it's a lost-cost, mild improvement .
We are looking for a significant improvement. Otherwise, what's the point? We need to justify the cost.
 The cognitive dissonance argument 
(Just to be clear: I think framing it as a psychological issue does not have much merit.)
 is the main one against it, and I really don't know if that dissonance 
 is based on fundamental flaws in the way I'm thinking about it,
The point of contracts is assigning blame by documenting assumptions and guarantees. If something within the function body crashes, it's ideally the fault of the function implementation.
 or just the "Shock of the New" [1].
I like new as long as it is an improvement. This is not. Having syntax subtrees that do not actually logically belong to their parent in the grammar is awkward language design, especially if they affect the parent's signature.
 ...
 As far as Teoh's proposal, I would say that its quality is highly 
 correlated to the percentage of projects that find built-in `assert` 
 adequate to their needs,
The two issues might need to be decoupled, but that is not the job of the contract syntax overhaul.
 which is hard to assess precisely - the better 
 `assert` is, or can be made to be, the better Teoh's proposal is, I'd 
 say.
Projects that want to do fancy things within contracts can use the existing contract syntax, but all they can legitimately do is throw AssertErrors, possibly with a message, just as assert does; the new syntax does not change this fact. (BTW: There should be an optional message, as in: in(test(), "test failed").)
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 00:27:38 UTC, Timon Gehr wrote:
 On 21.06.2017 19:39, MysticZach wrote:
 My counterargument to that is that it's possible that the 
 cognitive dissonance only occurs because of what people are 
 used to, rather than what is inherent to the syntax.
This is a purely philosophical distinction without empirical basis.
Well I never experienced the dissonance myself, and was surprised to find that others did experience it. I just thought my proposal was a better syntax for `in` and `out` contracts.
 Well my impression was that Walter liked it too, although I 
 could have misinterpreted his comment here:
 
 http://forum.dlang.org/post/ogvt66$1bcp$1 digitalmars.com
He is saying it is good that a DIP to improve contract syntax /exists/. I agree with that.
It's really not clear what he meant. I guessed that the highest probability of what he meant was that he actually liked the DIP, as opposed to the mere fact that it _was_ a DIP. But as I already said, I could have misinterpreted his comment. In the face of lack of knowledge, it's a sign of wisdom to admit that one might be wrong, I think.
 If such an alternative checking system is utilized,
there should be a way to hook into the checking logic.
Improving the checking logic interface may solve at a higher level the problem I'm trying to solve at a very low one, with mere syntax changes, and it might be (is probably?) the best way forward.
Your proposal does not solve this problem, and there is no need for this DIP to do that.
The goal of this DIP, and the problem I'm trying to solve, is "Improve Contract Usability." And for this, H.S. Teoh's proposal is a very good one. But it still has a sticking point, which I want to resolve, namely that it elevates the existing `assert` functionality beyond the current requirement that one must explicitly write `assert`, going so far as to imply it in the grammar itself. But because of many complaints about the limitations of the assert mechanism as it currently exists, I would hesitate to install it into the grammar as the "One Chosen Way" to bail out of contracts with the new syntax. However, if the functioning of the `assert` mechanism were more customizable, then it would be easier to entrust it, in my opinion, with the "sacred responsibility" of being installed into the grammar. H.S. Teoh's proposal would then stand on a firmer foundation, and my initial proposed syntax would become the inferior optionl. At that point, this DIP could be rewritten to advocate his syntax instead. (I think it would be better to just retain the number and title "DIP1009: Improve Contract Usability" than to make a new one for the same issue. Other DIPs have followed this pattern, where it was the goal and title that remained the same, while the specifics changed.)
 We are looking for a significant improvement. Otherwise, what's 
 the point? We need to justify the cost.
The intent of my proposal was to make a small improvement. The cost (or so I thought, and may still believe) was also small. Small improvements are still improvements. DIP1003 is an example of this. https://github.com/dlang/DIPs/blob/master/DIPs/DIP1003.md
 The point of contracts is assigning blame by documenting 
 assumptions and guarantees. If something within the function 
 body crashes, it's ideally the fault of the function 
 implementation.
 ...
 I like new as long as it is an improvement. This is not. Having 
 syntax subtrees that do not actually logically belong to their 
 parent in the grammar is awkward language design, especially if 
 they affect the parent's signature.
I'm still wondering what, in practice, the difference really is. With existing syntax: int fun(int a) in { assert(a); } // 1 do { assert(a); // 2 ... } What will the compiler or programmer actually learn from 1 if it violates that they won't learn from 2 if it violates? What is the practical incentive for `in` contracts at all? All my new syntax does is assume that there is in fact a difference between 1 and 2, and makes 1 easier to write, as: int fun(int a) { in assert(a); // 1 assert(a); // 2 ... } As far as syntax subtrees not belonging to their parent, I can see where the cognitive dissonance comes from. But it just doesn't seem that bad to me, since contracts are always executed as if they are sequential statements anyway. I would imagine that new programmers who only ever encountered the new proposed syntax would be surprised that the old syntax ever existed in the first place, as it's so unnecessarily awkward. But at this point, we might as well wait for more feedback from other people.
Jun 21
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jun 22, 2017 at 05:46:06AM +0000, MysticZach via Digitalmars-d wrote:
[...]
 As far as syntax subtrees not belonging to their parent, I can see
 where the cognitive dissonance comes from. But it just doesn't seem
 that bad to me, since contracts are always executed as if they are
 sequential statements anyway.
[...] Then possibly you're missing the point behind DbC. The idea of "executing" a contract seems to indicate that you're taking the current D implementation of it as normative. However, that is not the case. The concept behind DbC is that the function specifies a set of conditions the caller must satisfy before calling it (the in-contract), and in return it promises to satisfy another set of conditions (the out-contract). Conceptually speaking, the contracts are not "executed" as if they were part of the code in the function's body; rather, they are checks that are made by the runtime (as a conceptual entity -- one might think of it as a circuit breaker or some such safeguarding device) such that if they are violated, the program is aborted because it has entered an invalid state from which further execution would lead to UB. Of course, how you implement the DbC concept is a different (albeit related) issue. In languages targeted for a VM like Java, one could conceivably implement contract verification as part of the VM itself, so that it is done "transparently" to user code. In languages like D, however, because we're targeting the machine directly, no such intermediate layer exists, and hence the natural choice of implementing contracts as part of the code. (Though, as I've said in another post, D's implementation leaves some things to be desired, such as in-contracts being part of the callee rather than the caller, which IMO would have been a better choice.) But in any case, the so-called "cognitive dissonance" comes from conflating contracts, which conceptually is part of the function's user-facing API, as opposed to the implementation details in the function body. It's like saying that the locking mechanism of the safety cap of a medicine bottle is part of the medicine's chemistry. The locking mechanism is intended to protect the medicine, e.g., from children who would suffer unintended consequences of the medicine's chemistry by their incorrect usage of it. But that hardly makes the safety mechanism the same thing as the medicine itself. Similarly, the in-contract of a function is intended to protect it from incorrect usage by buggy callers, but that hardly makes it a part of the function's body. T -- Береги платье снову, а здоровье смолоду.
Jun 21
parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 06:43:38 UTC, H. S. Teoh wrote:
 On Thu, Jun 22, 2017 at 05:46:06AM +0000, MysticZach via 
 Digitalmars-d wrote: [...]
 As far as syntax subtrees not belonging to their parent, I can 
 see where the cognitive dissonance comes from. But it just 
 doesn't seem that bad to me, since contracts are always 
 executed as if they are sequential statements anyway.
[...] Then possibly you're missing the point behind DbC. The idea of "executing" a contract seems to indicate that you're taking the current D implementation of it as normative.
I understand. Thus, a better DIP would suggest reimplementing D's DbC system altogether. Because as it is, they are little more than syntax dressing, which happens to be less convenient that just writing out the asserts in the first place. They're more like syntax _vinegar_ than sugar. Which explains why hardly anyone uses them. Why write this: int fun(int a) in { assert (a); } do { return a; } ...when you could just do this: int fun(int a) { assert(a); return a; } With your improvement, it's a little better, int fun(int a) in (a) { return a; } ...but the cost is that `assert` is implied, and no other system of checking is allowed, which seems like a sufficient flaw to make it not decisively better. To solve this, you'd have to decouple `assert` from the contract, requiring this: int fun(int a) in (assert(a)) { return a; } This also looks good. But now the grammar is weird because it's just an expression in there without a statement, which doesn't happen anywhere else in D. I start to get the nagging feeling that your point about the limitation in D's DbC implementation is actually the fatal flaw here. It's currently _illegal_ (outside of interface declarations) to expose the signature separately from the body, which, as you point out, is more or less the whole point of DbC. <shrug> The existing system is itself distinctly _worse_ than just writing out asserts manually... and as far as I can tell, none of the alternative syntaxes is a decisive improvement over it. Yours looks the best, but implying `assert` in the grammar seems to be going too far, especially for a DbC system that is only half functional. If the `assert` system were made more user-definable, it would make more sense. Do you agree with me on this? Plans to improve `assert` are already in the air. Maybe we should wait on them before promoting your suggestion?
Jun 22
next sibling parent MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 12:21:29 UTC, MysticZach wrote:
 I start to get the nagging feeling that your point about the 
 limitation in D's DbC implementation is actually the fatal flaw 
 here. It's currently _illegal_ (outside of interface 
 declarations) to expose the signature separately from the body, 
 which, as you point out,  is more or less the whole point of 
 DbC. <shrug> The existing system is itself distinctly _worse_ 
 than just writing out asserts manually... and as far as I can 
 tell,  none of the alternative syntaxes is a decisive 
 improvement over it. Yours looks the best, but implying 
 `assert` in the grammar seems to be going too far, especially 
 for a DbC system that is only half functional. If the `assert` 
 system were made more user-definable, it would make more sense. 
 Do you agree with me on this?

 Plans to improve `assert` are already in the air. Maybe we 
 should wait on them before promoting your suggestion?
Conversely, as Moritz suggests, contracts could have their own separate logic, with optional user-defined hooks. Do you have a suggestion on how to implement this behind the scenes?
Jun 22
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 22 June 2017 at 12:21:29 UTC, MysticZach wrote:
 I understand. Thus, a better DIP would suggest reimplementing 
 D's DbC system altogether. Because as it is, they are little 
 more than syntax dressing, which happens to be less convenient 
 that just writing out the asserts in the first place. They're 
 more like syntax _vinegar_ than sugar. Which explains why 
 hardly anyone uses them. Why write this:

 int fun(int a)
 in { assert (a); }
 do {
     return a;
 }

 ...when you could just do this:

 int fun(int a) {
    assert(a);
    return a;
 }
My recollection is that the most significant reason to use contracts in D is because of contract inheritance. There's a lot of focus in this discussion on normal functions, when I would say that contracts really aren't even needed. So a more useful situation to consider then is: class Foo { int fun(int a) in { assert(a > 0 && a < 10); } body { return a; } } class Bar : Foo { int fun(int a) in {assert(a > 0);} body { return a; } } I would say keep the current behavior for backwards compatibility (maybe with the body/do change), but also allow the following code: class Foo { int fun(int a) { in { assert(a > 0 && a < 10); } return a; } } class Bar : Foo { int fun(int a) { in {assert(a > 0);} return a; } } where the compiler would effectively re-write this into the code above. You wouldn't need to make a change to interfaces since they don't have bodies anyway.
Jun 22
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 22.06.2017 16:08, jmh530 wrote:
 
 My recollection is that the most significant reason to use contracts in 
 D is because of contract inheritance. There's a lot of focus in this 
 discussion on normal functions, when I would say that contracts really 
 aren't even needed.
They are needed if you want to write contracts, and there are legitimate reasons to want to write contracts.
Jun 22
prev sibling next sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jun 22, 2017 at 02:08:32PM +0000, jmh530 via Digitalmars-d wrote:
[...]
 My recollection is that the most significant reason to use contracts
 in D is because of contract inheritance. There's a lot of focus in
 this discussion on normal functions, when I would say that contracts
 really aren't even needed.
[...] I agree that contract inheritance is an important use case, but FWIW, I write contracts in my non-class methods all the time. They are useful to document to the user (i.e., me 3 months later after I forgot the details of the code) what exactly the function expects / assumes, and for catching accidental wrong usages. Coupled with judiciously crafted unittests, this is invaluable for weeding out bugs in my code both during coding and also prevents regressions / future wrong usages of the function later. T -- People tell me that I'm paranoid, but they're just out to get me.
Jun 22
prev sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 14:08:32 UTC, jmh530 wrote:
 My recollection is that the most significant reason to use 
 contracts in D is because of contract inheritance. There's a 
 lot of focus in this discussion on normal functions, when I 
 would say that contracts really aren't even needed.

 So a more useful situation to consider then is:
 [...]
 I would say keep the current behavior for backwards 
 compatibility (maybe with the body/do change), but also allow 
 the following code:

 class Foo
 {
     int fun(int a)
     {
         in {
             assert(a > 0 && a < 10);
         }
         return a;
     }
 }

 class Bar : Foo
 {
     int fun(int a)
     {
         in {assert(a > 0);}
         return a;
     }
 }
The existing proposal allows this, and goes further and allows removing the unnecessary brackets after `in`: class Foo { int fun(int a) { in assert(a > 0 && a < 10); return a; } } class Bar : Foo { // override override int fun(int a) { in assert(a > 0); return a; } } Some people don't like the cognitive dissonance of seeing `in` inside the function. It really doesn't look that bad to me, and I'm glad to have a little support for the idea. But there are a couple more considerations. First, classes in D are not as popular as they are in some other languages, because of how powerful regular structs have become. I'd want to get more feedback on the value of improving a syntax (assuming it is clearly an improvement, which several people so far disagree with) that is only really valuable in cases that aren't used that much. There are other questions too. The syntax is only one of three possible reasons contracts in D aren't that popular. The second reason is that the specific implementation, i.e. the "backend", currently has limited support as described by H.S. Teoh [1]. The third reason is more abstract and harder to pin down, which is that some things are just more attractive in theory than they are in practice. The question is, even with the best possible syntax, and the best possible implementation, how much total value can `in` and `out` contracts add to language? How much more total use would they see? If we knew in advance that the answer to the third question is, "People wouldn't use them that much," then we might spare ourselves the effort of improving them. We could more or less drop the issue as a lost cause. But I suppose the premise of this DIP is that it's worth it to try to find out. It also looks like there will be a separate DIP for reimplementing the backend, so this one will focus, as it already does, on the syntax. That said, H.S. Teoh's version of the above code would look like this: class Foo { int fun(int a) in(a > 0 && a < 10) { return a; } } class Bar : Foo { override int fun(int a) in(a > 0) { return a; } } I'm leaning towards pushing this one, as it simply hasn't gotten the pushback that mine has so far. I do have a problem with it, which is that the checking logic is mingled in with the grammar. This doesn't usually happen in D, which normally requires you to explicitly write `assert` in order to assert something. Here, `in` becomes grammatically equivalent to an `assert` expression, but one which is only allowed in the function signature. At minimum, I can say that the reception for this idea has been more positive than for mine. But I do appreciate the support :-} [1] http://forum.dlang.org/post/mailman.3554.1498074198.31550.digitalmars-d puremagic.com
Jun 22
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 22 June 2017 at 18:57:40 UTC, MysticZach wrote:
 [snip]
I don't mind that so much, but you made a good point earlier on how out would work with it. The whole double parentheses is a bit ugly to me. Is there any problem with out(return > 0) instead of out(r) (r > 0) Also, I can see the point of Critique 5 in the DIP for not including in/out anywhere and wanting to pin it to the top of the body. The suggestion in your post at least succeeds at that.
Jun 22
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 22.06.2017 22:02, jmh530 wrote:
 
 The whole double parentheses is a bit ugly to me. Is there any problem with
 out(return > 0)
out(a => { void foo()out(b => a == b){ return a; } return foo()>0; }());
Jun 22
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 22 June 2017 at 20:19:59 UTC, Timon Gehr wrote:
 On 22.06.2017 22:02, jmh530 wrote:
 
 The whole double parentheses is a bit ugly to me. Is there any 
 problem with
 out(return > 0)
out(a => { void foo()out(b => a == b){ return a; } return foo()>0; }());
I'm confused. The compiler should know that these returns are inside lambdas. Also, foo should not return void and I'm not sure what the extra set of parentheses are for at the end.
Jun 22
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 22.06.2017 22:43, jmh530 wrote:
 On Thursday, 22 June 2017 at 20:19:59 UTC, Timon Gehr wrote:
 On 22.06.2017 22:02, jmh530 wrote:
 The whole double parentheses is a bit ugly to me. Is there any 
 problem with
 out(return > 0)
out(a => { void foo()out(b => a == b){ return a; } return foo()>0; }());
I'm confused. The compiler should know that these returns are inside lambdas.
The point is that two results are in scope, so you cannot call both of them 'return'. It's a corner case, but you asked for any problems.
 Also, foo should not return void
Oops. That's a typo. Fixed version: out(a => { int foo()out(b => a == b){ return a; } return foo()>0; }())
 and I'm not sure what the 
 extra set of parentheses are for at the end.
That's calling the lambda. Version that works today: int bar()out(a){ int foo()out(b){ assert(a==b); }body{ return a; } assert(foo()>0); }body{ return 2; }
Jun 22
prev sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 20:02:17 UTC, jmh530 wrote:
 On Thursday, 22 June 2017 at 18:57:40 UTC, MysticZach wrote:
 [snip]
I don't mind that so much, but you made a good point earlier on how out would work with it. The whole double parentheses is a bit ugly to me. Is there any problem with out(return > 0) instead of out(r) (r > 0)
The current grammar for `out` is: OutStatement: out { Statement(s) } out ( Identifier ) { Statement(s) } If the out contract with the new syntax had only one assertion, and that assertion were a single identifier, there would be a parsing ambiguity: int fun(int a) { int nested(int b) out (b) // out with a single assert, or out with identifier and brackets? { assert(b); // function body, or `out` contract? } do { ... } // function body, or do-while loop? while (true); // only here do we finally figure it out } There's probably an easy way to tidy this up, but I couldn't think of one off hand, so I suggested `out ( ) ( IfCondition )` as an unambiguous alternative. It would in fact be the rare case, since most `out` contracts will just want to check the return value.
 Also, I can see the point of Critique 5 in the DIP for not 
 including in/out anywhere and wanting to pin it to the top of 
 the body. The suggestion in your post at least succeeds at that.
I assume you mean H.S. Teoh's suggestion? If it's as good as the others seem to think, hopefully it will succeed more than that... but preventing enhancement 5 is as simple as having the compiler issue an error if the user violates the rule that contracts must occur first and be grouped together in the body.
Jun 22
parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 21:41:55 UTC, MysticZach wrote:
 The whole double parentheses is a bit ugly to me. Is there any 
 problem with
 out(return > 0)
 instead of
 out(r) (r > 0)
I'm sorry, I didn't read closely. I think that's just asking for trouble, wanting to use `return` as an identifier. Timon found a specific reason why, but in general contextual keywords are frowned upon for precisely this type of ambiguity in the meaning of the code.
Jun 22
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 22.06.2017 23:51, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:41:55 UTC, MysticZach wrote:
 The whole double parentheses is a bit ugly to me. Is there any 
 problem with
 out(return > 0)
 instead of
 out(r) (r > 0)
I'm sorry, I didn't read closely. I think that's just asking for trouble, wanting to use `return` as an identifier. Timon found a specific reason why, but in general contextual keywords are frowned upon for precisely this type of ambiguity in the meaning of the code.
(It's not a contextual keyword. A contextual keyword is an identifier that is reserved in some contexts but not others.)
Jun 22
next sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 21:56:29 UTC, Timon Gehr wrote:
 On 22.06.2017 23:51, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:41:55 UTC, MysticZach wrote:
 The whole double parentheses is a bit ugly to me. Is there 
 any problem with
 out(return > 0)
 instead of
 out(r) (r > 0)
I'm sorry, I didn't read closely. I think that's just asking for trouble, wanting to use `return` as an identifier. Timon found a specific reason why, but in general contextual keywords are frowned upon for precisely this type of ambiguity in the meaning of the code.
(It's not a contextual keyword. A contextual keyword is an identifier that is reserved in some contexts but not others.)
I would argue that the above suggestion promotes `return` precisely that way. It's now an identifier in precisely that one context, but is reserved as a keyword in all other contexts. Not sure what to call it. But we're a little off topic, as we both agree that the above solution to the double parens isn't viable, right?
Jun 22
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 23.06.2017 00:12, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:56:29 UTC, Timon Gehr wrote:
 On 22.06.2017 23:51, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:41:55 UTC, MysticZach wrote:
 The whole double parentheses is a bit ugly to me. Is there any 
 problem with
 out(return > 0)
 instead of
 out(r) (r > 0)
I'm sorry, I didn't read closely. I think that's just asking for trouble, wanting to use `return` as an identifier. Timon found a specific reason why, but in general contextual keywords are frowned upon for precisely this type of ambiguity in the meaning of the code.
(It's not a contextual keyword. A contextual keyword is an identifier that is reserved in some contexts but not others.)
I would argue that the above suggestion promotes `return` precisely that way. It's now an identifier in precisely that one context,
It's not an identifier.
 but is reserved as a keyword in all other contexts. Not sure what to call it.
It's an overloaded keyword.
 But we're a little off topic, as we both agree that the above solution 
 to the double parens isn't viable, right?
I think it is viable in principle, but verbose and not really in line with the existing out contract syntax.
Jun 22
parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 22 June 2017 at 22:28:14 UTC, Timon Gehr wrote:
 But we're a little off topic, as we both agree that the above 
 solution to the double parens isn't viable, right?
I think it is viable in principle, but verbose and not really in line with the existing out contract syntax.
Yeah.
Jun 22
prev sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 22 June 2017 at 22:12:57 UTC, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:56:29 UTC, Timon Gehr wrote:
 On 22.06.2017 23:51, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:41:55 UTC, MysticZach wrote:
 The whole double parentheses is a bit ugly to me. Is there 
 any problem with
 out(return > 0)
 instead of
 out(r) (r > 0)
I'm sorry, I didn't read closely. I think that's just asking for trouble, wanting to use `return` as an identifier. Timon found a specific reason why, but in general contextual keywords are frowned upon for precisely this type of ambiguity in the meaning of the code.
(It's not a contextual keyword. A contextual keyword is an identifier that is reserved in some contexts but not others.)
I would argue that the above suggestion promotes `return` precisely that way. It's now an identifier in precisely that one context, but is reserved as a keyword in all other contexts. Not sure what to call it. But we're a little off topic, as we both agree that the above solution to the double parens isn't viable, right?
It's not an identifier, means syntactically it is still a reserved keyword but you are right that it would be semantically very different. In normal contexts it's a flow control statement in that context it is an alias to a value with no influence of the program flow. Would be really annoyingly contradictory.
Jun 22
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/22/17 5:56 PM, Timon Gehr wrote:
 On 22.06.2017 23:51, MysticZach wrote:
 On Thursday, 22 June 2017 at 21:41:55 UTC, MysticZach wrote:
 The whole double parentheses is a bit ugly to me. Is there any 
 problem with
 out(return > 0)
 instead of
 out(r) (r > 0)
I'm sorry, I didn't read closely. I think that's just asking for trouble, wanting to use `return` as an identifier. Timon found a specific reason why, but in general contextual keywords are frowned upon for precisely this type of ambiguity in the meaning of the code.
(It's not a contextual keyword. A contextual keyword is an identifier that is reserved in some contexts but not others.)
We can call that contextual "keyword" result. Doesn't have to be a keyword, just the implied return value symbol name. It would work fine on it's own. My concern is that out(result > 4) matches the grammar for the declaration (a bit) for the declaration of the out contract today. If we allow not supplying the body (do) keyword, then it's almost exactly the same. In fact, if you are just asserting the result, then: int foo() out(result) { } what does this mean? Does it mean assert(result) on the out contract, or this is the old form of out? If we didn't have contract syntax already, and many existing code bases that use it, I'd say this would be the way to go. -Steve
Jun 22
next sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 00:17:23 UTC, Steven Schveighoffer 
wrote:
 int foo()
 out(result)
 {
 }

 what does this mean? Does it mean assert(result) on the out 
 contract, or this is the old form of out?

 If we didn't have contract syntax already, and many existing 
 code bases that use it, I'd say this would be the way to go.
The current grammar is: OutStatement: out ( Identifier ) { Statement } out { Statement } So one way out is simply to demand that there be more than just an identifier when checking in the new way. So you'd have to require more tokens, e.g.`out(result !is null)` instead of just `out(result)` in order for the parser to easily distinguish between the two. It's a little messy. On the other hand, requiring two sets of parens is clean, but ugly. So it's a choice between ugly and messy, unless someone comes up with something more elegant.
Jun 22
parent reply Solomon E <default avatar.org> writes:
On Friday, 23 June 2017 at 04:08:39 UTC, MysticZach wrote:
 On Friday, 23 June 2017 at 00:17:23 UTC, Steven Schveighoffer 
 wrote:
 int foo()
 out(result)
 {
 }

 what does this mean? Does it mean assert(result) on the out 
 contract, or this is the old form of out?

 If we didn't have contract syntax already, and many existing 
 code bases that use it, I'd say this would be the way to go.
The current grammar is: OutStatement: out ( Identifier ) { Statement } out { Statement } So one way out is simply to demand that there be more than just an identifier when checking in the new way. So you'd have to require more tokens, e.g.`out(result !is null)` instead of just `out(result)` in order for the parser to easily distinguish between the two. It's a little messy. On the other hand, requiring two sets of parens is clean, but ugly. So it's a choice between ugly and messy, unless someone comes up with something more elegant.
What I expected from my impression of existing D syntax was that something like this might be coming up: T foo(T)(T x, T y) in (x > 0, y > 0) out (r; r > 0) { return x % y + 1; } `out ()` has syntax similar to `foreach` and `for`, allowing mentioning something about the return type, or unpacking a return value, then testing it. (When there's no semicolon, an `out` block follows.) `in ()` has syntax similar to an initializer for an array of `bool`, so that which condition failed can be given an index for generating error messages. Not because it's elegant, but because it's complicated. It provides some features that were asked for in this thread and in some comments on DIP 1003, plus more possibilities. I don't see where this breaks any existing D syntax, but maybe this sort of complication should be reserved for D++.
Jun 23
next sibling parent MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 09:06:59 UTC, Solomon E wrote:
 What I expected from my impression of existing D syntax was 
 that something like this might be coming up:

 T foo(T)(T x, T y)
     in (x > 0, y > 0)
     out (r; r > 0)
 {
     return x % y + 1;
 }

 `out ()` has syntax similar to `foreach` and `for`, allowing 
 mentioning something about the return type, or unpacking a 
 return value, then testing it. (When there's no semicolon, an 
 `out` block follows.)
This is a good idea, and I didn't think of it. Points: * It would be great if you were allowed to omit the identifier before the semicolon in cases where you didn't need it. The existing foreach grammar does not allow this [1]. I'm not sure if that is intended to prevent bugs, or if it was just never thought to be useful. * The grammar for what's inside the parens should also mimic what's inside `assert`s, allowing an error message. So the new grammar would actually be a mix of the existing `foreach` and `assert` grammars, e.g. out(r; r >0, "contract violated!") Comparing with the double parens solution: out(result)(result > 0, "violation!") out()(otherData > 0, "prohibited!") vs. out(result; result > 0, "violation!") out(; otherData > 0, "prohibited!") It's a close call. Nice job! [1] https://dlang.org/spec/statement.html#foreach-statement
Jun 23
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/23/17 5:06 AM, Solomon E wrote:
 What I expected from my impression of existing D syntax was that 
 something like this might be coming up:
 
 T foo(T)(T x, T y)
      in (x > 0, y > 0)
      out (r; r > 0)
 {
      return x % y + 1;
 }
The out contract looks pretty good actually. The in contract, not as good. That looks like a comma expression. I'd rather see this be: in(x > 0) in(y > 0) or in(x > 0 && y > 0) In this case, we have something I think I would support. -Steve
Jun 23
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 23 June 2017 at 13:00:30 UTC, Steven Schveighoffer 
wrote:
 The out contract looks pretty good actually. The in contract, 
 not as good. That looks like a comma expression. I'd rather see 
 this be:

      in(x > 0)
      in(y > 0)

 or

      in(x > 0 && y > 0)

 In this case, we have something I think I would support.

 -Steve
I agree.
Jun 23
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 13:26:53 UTC, jmh530 wrote:
 On Friday, 23 June 2017 at 13:00:30 UTC, Steven Schveighoffer 
 wrote:
 The out contract looks pretty good actually. The in contract, 
 not as good. That looks like a comma expression. I'd rather 
 see this be:

      in(x > 0)
      in(y > 0)

 or

      in(x > 0 && y > 0)

 In this case, we have something I think I would support.

 -Steve
I agree.
Same here: Kudos (!) to Solomon for the out resembling foreach, but I don't support comma separation within in contracts, as it's inconsistent when compared to `&&` and `||`. Adjusted earlier example: --- int myFunc(Args...)(Args args) if (Args.length > 2) in (args[0] != 0, "Yikes!") in (args[1] > 1) out (result; result > 0, "Oops...") out (; globalStateStillValid) { ... } --- Looks nice and clear to me. OT: Anyone interested in a DIP for more template constraint unfulfilled information in a consistent way to contracts (?) : --- int myFunc(Args...)(Args args) if (Args.length > 0, "Starving!") if (Args.length > 1, "Still hungry!") if (Args.length > 2, "Just a little bit more!") in (args[0] != 0, "Yikes!") in (args[1] > 1, "Why you do this?") out (result; result > 0, "Oops...") { ... } ---
Jun 23
next sibling parent MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 13:58:38 UTC, Moritz Maxeiner wrote:
 OT: Anyone interested in a DIP for more template constraint 
 unfulfilled information in a consistent way to contracts (?) :
 ---
 int myFunc(Args...)(Args args)
   if (Args.length > 0, "Starving!")
   if (Args.length > 1, "Still hungry!")
   if (Args.length > 2, "Just a little bit more!")
   in (args[0] != 0, "Yikes!")
   in (args[1] > 1, "Why you do this?")
   out (result; result > 0, "Oops...") { ... }
 ---
This would be a good question for a different thread.
Jun 23
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/23/17 9:58 AM, Moritz Maxeiner wrote:

    out (; globalStateStillValid) { ... }
What's that mean? -Steve
Jun 23
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 14:53:44 UTC, Steven Schveighoffer 
wrote:
 On 6/23/17 9:58 AM, Moritz Maxeiner wrote:

    out (; globalStateStillValid) { ... }
What's that mean?
Parenthesisless function call (as function calls are expressions).
Jun 23
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/23/17 11:41 AM, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 14:53:44 UTC, Steven Schveighoffer wrote:
 On 6/23/17 9:58 AM, Moritz Maxeiner wrote:

    out (; globalStateStillValid) { ... }
What's that mean?
Parenthesisless function call (as function calls are expressions).
OK. so if I read that correctly, you are saying you don't need to use the result in your out contract, and the { ... } is actually the function body? I initially read the { ... } as part of the out contract. -Steve
Jun 23
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 17:09:18 UTC, Steven Schveighoffer 
wrote:
 On 6/23/17 11:41 AM, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 14:53:44 UTC, Steven Schveighoffer 
 wrote:
 On 6/23/17 9:58 AM, Moritz Maxeiner wrote:

    out (; globalStateStillValid) { ... }
What's that mean?
Parenthesisless function call (as function calls are expressions).
OK. so if I read that correctly, you are saying you don't need to use the result in your out contract, and the { ... } is actually the function body?
Yes, as was proposed here[1]
 I initially read the { ... } as part of the out contract.
The example was just meant to give an overview of all contract specification variants of the latest proposal (which I support). [1] http://forum.dlang.org/post/wgarisncalisyleckikg forum.dlang.org
Jun 23
prev sibling parent MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 13:00:30 UTC, Steven Schveighoffer 
wrote:
 On 6/23/17 5:06 AM, Solomon E wrote:
 What I expected from my impression of existing D syntax was 
 that something like this might be coming up:
 
 T foo(T)(T x, T y)
      in (x > 0, y > 0)
      out (r; r > 0)
 {
      return x % y + 1;
 }
The out contract looks pretty good actually. The in contract, not as good. That looks like a comma expression. I'd rather see this be: in(x > 0) in(y > 0) or in(x > 0 && y > 0)
I would assume the grammar for these expressions would be the same as for `assert`s. So you're right in pointing out the flaw above.
Jun 23
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Fri, Jun 23, 2017 at 09:06:59AM +0000, Solomon E via Digitalmars-d wrote:
[...]
 T foo(T)(T x, T y)
     in (x > 0, y > 0)
     out (r; r > 0)
 {
     return x % y + 1;
 }
Hmm, I like this syntax for out-contracts! It borrows from existing foreach syntax, so it has some precedence, whereas the previous proposal of `out(...)(...)` looks uglier and also looks deceptively like a template function declaration. `out (r; r > 0)` gets my vote. OTOH, I don't like the comma in the in-contract. Let's just keep it as either separate clauses: in (x > 0) in (y > 0) or just use a boolean operator: in (x > 0 && y > 0) T -- Two wrongs don't make a right; but three rights do make a left...
Jun 23
next sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 16:21:28 UTC, H. S. Teoh wrote:
 On Fri, Jun 23, 2017 at 09:06:59AM +0000, Solomon E via 
 Digitalmars-d wrote: [...]
 T foo(T)(T x, T y)
     in (x > 0, y > 0)
     out (r; r > 0)
 {
     return x % y + 1;
 }
Hmm, I like this syntax for out-contracts! It borrows from existing foreach syntax, so it has some precedence, whereas the previous proposal of `out(...)(...)` looks uglier and also looks deceptively like a template function declaration. `out (r; r > 0)` gets my vote. OTOH, I don't like the comma in the in-contract. Let's just keep it as either separate clauses: in (x > 0) in (y > 0)
Yeah, my take is that the grammar for `assert`s applies to the new syntax as well. If the grammar for asserts is this: AssertExpression: assert ( AssertParameters ) ... then the grammar for the new syntax is: InExpression: in ( AssertParameters ) OutExpression: out ( ; AssertParameters ) out ( Identifier ; AssertParameters )
Jun 23
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
Jun 23
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 23.06.2017 19:52, jmh530 wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
Because it cannot be parsed greedily. See: http://forum.dlang.org/post/beovehhmoxzuoepterzz forum.dlang.org
Jun 23
parent jmh530 <john.michael.hall gmail.com> writes:
On Friday, 23 June 2017 at 18:06:34 UTC, Timon Gehr wrote:
 Because it cannot be parsed greedily. See:
 http://forum.dlang.org/post/beovehhmoxzuoepterzz forum.dlang.org
I see what you're saying. I don't think I really grokked it when you posted the example yesterday. And also it is consistent with the for syntax, which requires semi-colon, even if blank.
Jun 23
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.com> writes:
On 6/23/17 6:52 PM, jmh530 wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
The path of least resistance is to use existing language constructs, i.e. out result => assert(result > 0) Andrei
Jun 25
next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 25 June 2017 at 11:37:07 UTC, Andrei Alexandrescu 
wrote:
 On 6/23/17 6:52 PM, jmh530 wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
The path of least resistance is to use existing language constructs, i.e. out result => assert(result > 0)
I consider adapting foreach syntax as using existing language constructs (instead of inventing completely new syntax). And while your proposal may indeed be the path of least resistance (since you and Walter will end up deciding whether to accept it or not), it's also the path of least benefits as it still requires manually specifying the contract implementation. Coupling a contract with its implementation is verbose and can already be done with the existing syntax; imho it would ensure that the new syntax will receive only negligible more usage than the existing one.
Jun 25
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 25.06.2017 13:37, Andrei Alexandrescu wrote:
 On 6/23/17 6:52 PM, jmh530 wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
The path of least resistance is to use existing language constructs, i.e. out result => assert(result > 0) Andrei
This would face quite some resistance, on the following grounds: out(result){ assert(result > 0); } // exists out result => assert(result > 0) // more of the same out(result; result > 0) // better
Jun 25
next sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Sunday, 25 June 2017 at 12:10:02 UTC, Timon Gehr wrote:
 On 25.06.2017 13:37, Andrei Alexandrescu wrote:
 On 6/23/17 6:52 PM, jmh530 wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
The path of least resistance is to use existing language constructs, i.e. out result => assert(result > 0) Andrei
This would face quite some resistance, on the following grounds: out(result){ assert(result > 0); } // exists out result => assert(result > 0) // more of the same out(result; result > 0) // better
out result => result > 0 // not much worse out __result > 0 // even better (__result works at least since [0]) I suggested adding a shorter / better looking contextual keyword - `result` - a while ago [1]: --- out result > 0 // perhaps the best --- And before that I proposed this: --- // `Unqual` is not needed for `isNumeric` strictly speaking, // it's used here only for illustration purposes. T sqrt(T)(T n) if Unqual!T U: isNumeric!U || is(U == BigInt) in n >= 0 out (result) result * result == n { //Implementation } --- (I think as a reply to you in the DIP1003 thread) I'm wondering if `out result > 0` (or if not `out __result > 0`) would be too much of a stretch for the language grammar. [0]: https://github.com/dlang/dmd/commit/620acd53b63bfede6179a1b6a5c7d1b01a14ed0e#diff-1faebda3a5778a9d5b2dc0037a8f589bR8 [1]: https://github.com/dlang/DIPs/pull/66#discussion_r117613661
Jun 25
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 25 June 2017 at 15:46:12 UTC, Petar Kirov [ZombineDev] 
wrote:
 out result => result > 0 // not much worse

 out __result > 0 // even better (__result works at least since 
 [0])

 I suggested adding a shorter / better looking contextual 
 keyword -
 `result` - a while ago [1]:
 ---
 out result > 0 // perhaps the best
 ---
I have no stance with having to specify a result identifier, or using __result, making `result` special, *but*: Not requiring parentheses here introduces an unacceptable language inconsistency: auto x(T)(T t) if (...) in (...) out ... { ... } If you also propose to drop the parentheses for `in`, that still leaves the inconsistency with `if`, i.e. now `if` must be changed to also drop the parentheses in template constraints. That, however, leads then to an inconsistency between different uses of `if` and thus all other occurrences of `if` in the grammar must now be made to work without parenthesis. This then leads to even more inconsistencies with `for`, `while`, etc. that now also will have to be changed.
 I'm wondering if `out result > 0` (or if not `out __result > 0`)
 would be too much of a stretch for the language grammar.
As argued above, it would essentially require making all uses of parenthesis for conditions optional or introduce atrocious inconsistencies. If this is to be done, I believe it belongs in a separate DIP "Optional parenthesis for conditions", while this DIP uses parentheses.
Jun 25
parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Sunday, 25 June 2017 at 16:50:38 UTC, Moritz Maxeiner wrote:
 [...]
Just to be clear, I think that at least for now out(result; result > 0, "worse enough") is the best solution, and I support it. Everything else are just some random ideas that I'm not pushing strongly. As expected, my proposal's grammar wasn't to well thought through and Guillaume Boucher provided a good example for that.
 I have no stance with having to specify a result identifier, or 
 using __result, making `result` special, *but*: Not requiring 
 parentheses here introduces an unacceptable language 
 inconsistency:

 auto x(T)(T t)
   if (...)
   in (...)
   out ...
 {
     ...
 }

 If you also propose to drop the parentheses for `in`, that 
 still leaves the inconsistency with `if`, i.e. now `if` must be 
 changed to also drop the parentheses in template constraints. 
 That, however, leads then to an inconsistency between different 
 uses of `if` and thus all other occurrences of `if` in the 
 grammar must now be made to work without parenthesis. This then 
 leads to even more inconsistencies with `for`, `while`, etc. 
 that now also will have to be changed.
No, I'm not proposing removing parenthesis in just one case - I agree that it would be stupidly inconsistent and there's no point in that. I proposed this: T sqrt(T)(T n) if Unqual!T U: isNumeric!U || is(U == BigInt) in n >= 0 out (result) result * result == n { //Implementation } But as mentioned already, that has grammar ambiguities. So what about something like this: T sqrt(T)(T n) where U is Unqual!T: isNumeric!U || is(U == BigInt) require: n >= 0 ensure result: result * result == n { //Implementation } Assuming that in this particular case there's no need for custom error messages, which are still unsupported for template constraints presently. (I'm aware that this has low chances in getting into the language, I'm just interested in hearing your unbiased opinion as if it was targeted at a hypothetical D3.)
Jun 25
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Sunday, 25 June 2017 at 17:59:02 UTC, Petar Kirov [ZombineDev] 
wrote:
 So what about something like this:

 T sqrt(T)(T n)
 where U is Unqual!T: isNumeric!U || is(U == BigInt)
 require: n >= 0
 ensure result: result * result == n
 {
     //Implementation
 }

 Assuming that in this particular case there's no need for 
 custom error
 messages, which are still unsupported for template constraints 
 presently.

 (I'm aware that this has low chances in getting into the 
 language,
 I'm just interested in hearing your unbiased opinion as if it 
 was
 targeted at a hypothetical D3.)
While I have no issues with changing keywords in the function signature like that [1], removing the parenthesis delimiters makes parsing more complex (and thus slower) for both the human reader, as well as the compiler; for me personally it's the difference between looking at the signature and grasping the meaning instantly and having to verify where the template constraint actually ends. [1] Strictly speaking require/ensure *are* the words matching the original DbC definition, after all
Jun 25
prev sibling next sibling parent Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Sunday, 25 June 2017 at 15:46:12 UTC, Petar Kirov [ZombineDev] 
wrote:
 out result > 0 // perhaps the best
How would you handle things like this: T minimum(T)(RedBlackTree!T tree) in (!tree.empty) out result in (tree) { return tree.front; } That could either mean "out(result){assert(result in (tree));}" or out(result){assert(result);} in{assert(tree);}"
Jun 25
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 25.06.2017 17:46, Petar Kirov [ZombineDev] wrote:
 On Sunday, 25 June 2017 at 12:10:02 UTC, Timon Gehr wrote:
 On 25.06.2017 13:37, Andrei Alexandrescu wrote:
 On 6/23/17 6:52 PM, jmh530 wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
Why not? OutExpression: out ( AssertParameters ) out ( Identifier ; AssertParameters )
The path of least resistance is to use existing language constructs, i.e. out result => assert(result > 0) Andrei
This would face quite some resistance, on the following grounds: out(result){ assert(result > 0); } // exists out result => assert(result > 0) // more of the same out(result; result > 0) // better
out result => result > 0 // not much worse
out(result; result > 0, "worse enough") Also, what Guillaume said.
Jun 25
parent reply Enamex <enamex+d outlook.com> writes:
On Sunday, 25 June 2017 at 17:20:51 UTC, Timon Gehr wrote:
 On 25.06.2017 17:46, Petar Kirov [ZombineDev] wrote:
 On Sunday, 25 June 2017 at 12:10:02 UTC, Timon Gehr wrote:
 On 25.06.2017 13:37, Andrei Alexandrescu wrote:
 out(result){ assert(result > 0); } // exists

 out result => assert(result > 0) // more of the same

 out(result; result > 0) // better
out result => result > 0 // not much worse
out(result; result > 0, "worse enough") Also, what Guillaume said.
Why do we need to name the result at all? Any conflicts with using `out(out > 0, "message")` or `out(return > 0, "message")`? Or even `out(someCond($), "message")`? So using either `out` or `return` or `$` or whatever to always refer to the return value of the function. Just something that's already relevant and used instead of `__result`. This could even be naturally extended to having an implicitly declared 'result' variable for functions (which could help in optimizations maybe? Something like always having NRVO possible) called `out` or `return`. R foo(Args...)(Args args) { out(return > bar && ensured(return), "foo() fudged its return"); // ... return = blah; // ... return.quux(var); static assert(is(typeof(return) == R)); // of course; this's old syntax }
Jun 28
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 28 June 2017 at 12:17:36 UTC, Enamex wrote:
 Why do we need to name the result at all?

 Any conflicts with using
 `out(out > 0, "message")`
No conflict AFAICT. I personally don't like it, though.
 `out(return > 0, "message")`?
Yes, see [1]
 `out(someCond($), "message")`?
Overloading symbols with context dependent meaning is one more step into obfuscation.
 So using either `out` or `return` or `$` or whatever to always 
 refer to the return value of the function. Just something 
 that's already relevant and used instead of `__result`.
Well, `__result` is already implemented and usable, so I would argue it is thus relevant.
 R foo(Args...)(Args args) {
     out(return > bar && ensured(return), "foo() fudged its 
 return");
Contracts inside function bodies should not be allowed imho. [1] http://forum.dlang.org/post/oihbot$134s$1 digitalmars.com
Jun 28
parent reply Enamex <enamex+d outlook.com> writes:
On Wednesday, 28 June 2017 at 12:34:59 UTC, Moritz Maxeiner wrote:
 On Wednesday, 28 June 2017 at 12:17:36 UTC, Enamex wrote:
 `out(return > 0, "message")`?
Yes, see [1]
It has the already used `typeof(return)` going for it, though. A big point against it IMO would be its moving further from `return`'s signal that a function scope is exited at that line (instead it could be a variable assignment which is meh (usage in a contract check is OK since we're already outside the function)).
 `out(someCond($), "message")`?
Overloading symbols with context dependent meaning is one more step into obfuscation.
True.
 So using either `out` or `return` or `$` or whatever to always 
 refer to the return value of the function. Just something 
 that's already relevant and used instead of `__result`.
Well, `__result` is already implemented and usable, so I would argue it is thus relevant.
It's not used in the wild yet though :T
 R foo(Args...)(Args args) {
     out(return > bar && ensured(return), "foo() fudged its 
 return");
Contracts inside function bodies should not be allowed imho. [1] http://forum.dlang.org/post/oihbot$134s$1 digitalmars.com
I was going with the current 'Proposal' syntax in the DIP's document. There a more recent proposal here?
Jun 28
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 28 June 2017 at 14:09:40 UTC, Enamex wrote:
 On Wednesday, 28 June 2017 at 12:34:59 UTC, Moritz Maxeiner 
 wrote:
 On Wednesday, 28 June 2017 at 12:17:36 UTC, Enamex wrote:
 R foo(Args...)(Args args) {
     out(return > bar && ensured(return), "foo() fudged its 
 return");
Contracts inside function bodies should not be allowed imho.
I was going with the current 'Proposal' syntax in the DIP's document. There a more recent proposal here?
The DIP is still at pre-preliminary review round 1 (since it hasn't finished yet). The current syntax proposal is effectively emergent through H. S. Teoh's general proposal [1], Solomon E's out enhancement [2], and Timon Gehr's implementation of the former two [3]. You can see in Timon's examples [4] how it looks (and contracts are part of a function's signature). [1] http://forum.dlang.org/post/mailman.3511.1497981037.31550.digitalmars-d puremagic.com [2] http://forum.dlang.org/post/xtimtynqeuzmgaychpdq forum.dlang.org [3] http://forum.dlang.org/post/oijl5f$20fv$1 digitalmars.com [4] https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax#diff-0630cf91becfdb077688ebd1ef400c5a
Jun 28
parent MysticZach <reachzach ggmail.com> writes:
On Wednesday, 28 June 2017 at 14:33:52 UTC, Moritz Maxeiner wrote:
 The DIP is still at pre-preliminary review round 1 (since it 
 hasn't finished yet).
 The current syntax proposal is effectively emergent through H. 
 S. Teoh's general proposal [1], Solomon E's out enhancement 
 [2], and Timon Gehr's implementation of the former two [3].
 You can see in Timon's examples [4] how it looks (and contracts 
 are part of a function's signature).
Yes, this thread has been incredibly productive, and has resulted in a complete metamorphosis of my original proposal, into a proposal that I think is very strong. I have to rewrite the existing DIP to reflect that.
Jun 28
prev sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Wednesday, 28 June 2017 at 14:09:40 UTC, Enamex wrote:
 It has the already used `typeof(return)` going for it, though.
That's why I had suggested it above. However, I think the point they raised about parsing in response was a good one.
Jun 28
prev sibling parent MysticZach <reachzach ggmail.com> writes:
On Sunday, 25 June 2017 at 12:10:02 UTC, Timon Gehr wrote:
 The path of least resistance is to use existing language 
 constructs, i.e.
 
 out result => assert(result > 0)
 
 
 Andrei
This would face quite some resistance, on the following grounds: out(result){ assert(result > 0); } // exists out result => assert(result > 0) // more of the same out(result; result > 0) // better
I also imagine we'll end up seeing quite a lot of: out( ; __result > 0) ...even though `__result` is still undocumented, because it's DRY. That's actually an incentive, IMO, to go ahead and document it. It's just tighter.
Jun 25
prev sibling next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 [...]

 Yeah, my take is that the grammar for `assert`s applies to the 
 new syntax as well. If the grammar for asserts is this:

 AssertExpression:
   assert ( AssertParameters )

 ... then the grammar for the new syntax is:

 InExpression:
   in ( AssertParameters )

 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
I'm all for this syntax, just one spec/implementation question: If the new contract syntax (formally) shares grammar rules with assert, won't that cause more work for people who want to update the assert syntax later (since they will have to take contracts into account)?
Jun 23
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/23/17 2:24 PM, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 [...]

 Yeah, my take is that the grammar for `assert`s applies to the new 
 syntax as well. If the grammar for asserts is this:

 AssertExpression:
   assert ( AssertParameters )

 ... then the grammar for the new syntax is:

 InExpression:
   in ( AssertParameters )

 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
I'm all for this syntax, just one spec/implementation question: If the new contract syntax (formally) shares grammar rules with assert, won't that cause more work for people who want to update the assert syntax later (since they will have to take contracts into account)?
No. Asserts are the meat of in/out contracts, these are actually asserts. Anything you do to the assert grammar should be done here as well. -Steve
Jun 23
next sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 18:42:55 UTC, Steven Schveighoffer 
wrote:
 On 6/23/17 2:24 PM, Moritz Maxeiner wrote:
 I'm all for this syntax, just one spec/implementation question:
 If the new contract syntax (formally) shares grammar rules 
 with assert, won't that cause more work for people who want to 
 update the assert syntax later (since they will have to take 
 contracts into account)?
No. Asserts are the meat of in/out contracts, these are actually asserts. Anything you do to the assert grammar should be done here as well.
I agree. I can understand wanting to pass in/out violations to a different handler behind the scenes. But I don't see why that should affect the grammar.
Jun 23
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 19:38:11 UTC, MysticZach wrote:
 On Friday, 23 June 2017 at 18:42:55 UTC, Steven Schveighoffer 
 wrote:
 On 6/23/17 2:24 PM, Moritz Maxeiner wrote:
 I'm all for this syntax, just one spec/implementation 
 question:
 If the new contract syntax (formally) shares grammar rules 
 with assert, won't that cause more work for people who want 
 to update the assert syntax later (since they will have to 
 take contracts into account)?
No. Asserts are the meat of in/out contracts, these are actually asserts. Anything you do to the assert grammar should be done here as well.
I agree. I can understand wanting to pass in/out violations to a different handler behind the scenes. But I don't see why that should affect the grammar.
Because coupling the new contract syntax and assert syntax in the grammar means that changing assert syntax will affect the new contract syntax (when it shouldn't, as they are semantically decoupled).
Jun 23
parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 20:03:17 UTC, Moritz Maxeiner wrote:
 No. Asserts are the meat of in/out contracts, these are 
 actually asserts. Anything you do to the assert grammar 
 should be done here as well.
I agree. I can understand wanting to pass in/out violations to a different handler behind the scenes. But I don't see why that should affect the grammar.
Because coupling the new contract syntax and assert syntax in the grammar means that changing assert syntax will affect the new contract syntax (when it shouldn't, as they are semantically decoupled).
By default, I assume they will not be semantically decoupled, that they will all just use regular asserts. I think the consistency the assert grammar will bring is worth a whole lot, as once it is understood in one place, it is understood everywhere. That's why it's so easy for Timon to implement, for example, because the assert logic is already available. So, for me to be convinced that the the grammar for in/out contracts should be different, I'd have to see a clear and compelling use case. What exactly did you have in mind? My naive assumption is that any improvement in the in/out grammar would also apply to asserts, and vice versa. I would go further and say that having consistency among all types of contracts is valuable enough to be worth sacrificing a considerable amount of flexibility in the grammar, even if a compelling use case were presented.
Jun 23
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 20:27:58 UTC, MysticZach wrote:
 On Friday, 23 June 2017 at 20:03:17 UTC, Moritz Maxeiner wrote:
 No. Asserts are the meat of in/out contracts, these are 
 actually asserts. Anything you do to the assert grammar 
 should be done here as well.
I agree. I can understand wanting to pass in/out violations to a different handler behind the scenes. But I don't see why that should affect the grammar.
Because coupling the new contract syntax and assert syntax in the grammar means that changing assert syntax will affect the new contract syntax (when it shouldn't, as they are semantically decoupled).
By default, I assume they will not be semantically decoupled, that they will all just use regular asserts. I think the consistency the assert grammar will bring is worth a whole lot, as once it is understood in one place, it is understood everywhere. That's why it's so easy for Timon to implement, for example, because the assert logic is already available. So, for me to be convinced that the the grammar for in/out contracts should be different, I'd have to see a clear and compelling use case. What exactly did you have in mind?
Normal usage of the new syntax? If asserts later on get a (possibly breaking) syntax overhaul that will affect (and forward breakage to) the contract syntax, as well, if it uses the same compiler infrastructure.
 My naive assumption is that any improvement in the in/out 
 grammar would also apply to asserts, and vice versa.
Why should it? Contracts are not asserts or vice versa.
 I would go further and say that having consistency among all 
 types of contracts is valuable enough to be worth sacrificing a 
 considerable amount of flexibility in the grammar, even if a 
 compelling use case were presented.
I'm not sure what you're getting at here. With the proposal Timon implemented we have the following: - The older, flexible contract syntax, which essentially only provides contract "shells" that you have to fill with your own preferred contract implementation (for which asserts are typically used) - the newer, decoupled contract syntax that separates contract specification and implementation from each other; Timon's implementation uses asserts as the internal contract implementation AFAIK - Asserts as a D builtin for condition checking (*not* a type of contract) In any case, it's merely a concern about future work load. If asserts receive an overhaul, whoever does that will have to (potentially) deal with it, I guess.
Jun 23
parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 20:49:35 UTC, Moritz Maxeiner wrote:
 Normal usage of the new syntax? If asserts later on get a 
 (possibly breaking) syntax overhaul that will affect (and 
 forward breakage to) the contract syntax, as well, if it uses 
 the same compiler infrastructure.
I believe this fear is irrational. A breaking change to the assert syntax would be extremely stupid. Imagine how much code would break if there were a breaking change to `assert`! The only possible changes to it must be additive.
 My naive assumption is that any improvement in the in/out 
 grammar would also apply to asserts, and vice versa.
Why should it? Contracts are not asserts or vice versa.
I 95% disagree. As far as I can tell, they are _very_ similar. The only difference I can see is that when you violate an `in` contract, the exit message should point to the caller instead of the callee as the source of the problem. Beyond that, they seem like little more than glorified `assert`s. I think their greatest benefit is in helping the programmer read and understand the logic of the program, rather than in anything the compiler can do with them.
 I'm not sure what you're getting at here. With the proposal 
 Timon implemented we have the following:
 - the newer, decoupled contract syntax that separates contract 
 specification and implementation from each other; Timon's 
 implementation uses asserts as the internal contract 
 implementation AFAIK
Not exactly. It actually _couples_ `in` expressions with assert expressions. In essence it just lowers int fun(int a) in(a) { } ...to: int fun(int a) in { assert(a); } { } This is a sensible default. In the future, some logic could be added to provide a hook to a different implementation of the `in` function, so that it would lower instead to: int fun(int a) in { __userDefinedInContract(a); } { } But I suspect the vast majority will stick with the default.
Jun 23
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 21:14:47 UTC, MysticZach wrote:
 On Friday, 23 June 2017 at 20:49:35 UTC, Moritz Maxeiner wrote:
 Normal usage of the new syntax? If asserts later on get a 
 (possibly breaking) syntax overhaul that will affect (and 
 forward breakage to) the contract syntax, as well, if it uses 
 the same compiler infrastructure.
 My naive assumption is that any improvement in the in/out 
 grammar would also apply to asserts, and vice versa.
Why should it? Contracts are not asserts or vice versa.
I 95% disagree. As far as I can tell, they are _very_ similar. The only difference I can see is that when you violate an `in` contract, the exit message should point to the caller instead of the callee as the source of the problem. Beyond that, they seem like little more than glorified `assert`s.
This is (unfortunately) not a matter of opinion (within the context of DbC), see Meyer's original publication [1], who effectively birthed and established the DbC paradim. Contracts within the DbC paradigm *are* abstractions that do not necessitate any particular implementation. Most native languages simply (sensibly) choose to use asserts for that.
 I'm not sure what you're getting at here. With the proposal 
 Timon implemented we have the following:
 - the newer, decoupled contract syntax that separates contract 
 specification and implementation from each other; Timon's 
 implementation uses asserts as the internal contract 
 implementation AFAIK
Not exactly. It actually _couples_ `in` expressions with assert expressions. In essence it just lowers
It would only couple the contracts with asserts if the DIP also specifies that asserts *must* be used for the lowering (which I would be against btw). Otherwise, it couples contracts with the request of "you take care of the implementation for me", which Timon's dmd implementation just happens to be use asserts for as the default (which is fine). [1] Meyer, Bertrand: Design by Contract, Technical Report TR-EI-12/CO, Interactive Software Engineering Inc., 1986
Jun 23
parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 21:36:07 UTC, Moritz Maxeiner wrote:
 Contracts within the DbC paradigm *are* abstractions that do 
 not necessitate any particular implementation.
In practice, though, what must any such implementation actually achieve? 1. allow the source code to express its intent to the reader 2. prevent programs in invalid states from continuing 3. provide information about those invalid states after the fact Goal 1 lies in the domain of the language grammar. Goals 2 and 3 reflect the behind-the-scenes implementation of that grammar.
 Most native languages simply (sensibly) choose to use asserts 
 for that.
Which could mean that they use the grammar of asserts, e.g. `assert(a, "message");`, or that they use a specific behind-the-scenes implementation of that grammar, i.e. the code that such a grammar compiles to.
 It would only couple the contracts with asserts if the DIP also 
 specifies that asserts *must* be used for the lowering (which I 
 would be against btw).
I think this reveals the sticking point. I think that the way D compiles assert expressions can and should be improved by making it more flexible behind the scenes. But that doesn't necessarily mean that the grammar needs to change. The problem is that we need to put *some* grammar into the new `in` expression design. If we allow, for example, a random sequence of function arguments, e.g.`in(a, b < 0, "yay!", myDvar)` there is no go-to way to implement it. The surface code would then require a particular backend implementation that could handle the grammar, and would furthermore now be coupled with that implementation. I'd rather not deal with that problem. It's vastly easier to just put my faith into the `assert` grammar, and hope that any improvements that it needs and receives will apply equally to `assert`s as well.
Jun 23
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Saturday, 24 June 2017 at 05:16:45 UTC, MysticZach wrote:
 On Friday, 23 June 2017 at 21:36:07 UTC, Moritz Maxeiner wrote:
 Contracts within the DbC paradigm *are* abstractions that do 
 not necessitate any particular implementation.
In practice, though, what must any such implementation actually achieve? [...] 2. prevent programs in invalid states from continuing
With "program" in this abtract domain meaning the specific computational task that broke the contract, not necessarily the entire implementation domain OS process - it's just that the finest safe termination of computational tasks in the presence of bugs on modern OSs you get for free (because of process isolation) is a process. With a runtime environment (e.g. a VM like the JVM, or an appropriate druntime implementation) that supports safe killing on a finer resolution (e.g. threads and fibers) there's no inherent reason not to map contract violations to thread/fiber killing instead of process killing (if it's proven to be safe within that runtime).
 3. provide information about those invalid states after the fact
All it *must* do is identify which contract failed (i.e. (file,line/row,column)). The rest is optional.
 It would only couple the contracts with asserts if the DIP 
 also specifies that asserts *must* be used for the lowering 
 (which I would be against btw).
[...]. I'd rather not deal with that problem. It's vastly easier to just put my faith into the `assert` grammar, and hope that any improvements that it needs and receives will apply equally to `assert`s as well.
I would simply copy the current assert grammar into a separate contract grammar, so that further modifications to the assert grammar do not change the new contract grammar. But as I said, it's not important to me, it was just an observation/question. Should the need ever arise for (new) contract and assert grammar to diverge it can be dealt with then by whoever does the diverging.
Jun 24
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/24/17 8:26 AM, Moritz Maxeiner wrote:

 I would simply copy the current assert grammar into a separate contract 
 grammar, so that further modifications to the assert grammar do not 
 change the new contract grammar.
 But as I said, it's not important to me, it was just an 
 observation/question. Should the need ever arise for (new) contract and 
 assert grammar to diverge it can be dealt with then by whoever does the 
 diverging.
Exactly, we can cross that bridge when (if) it happens. -Steve
Jun 24
prev sibling parent MysticZach <reachzach ggmail.com> writes:
On Saturday, 24 June 2017 at 12:26:57 UTC, Moritz Maxeiner wrote:
 Should the need ever arise for (new) contract and assert 
 grammar to diverge it can be dealt with then by whoever does 
 the diverging.
Yes, I'll keep the grammar the same for now, because there is a benefit to doing so, in that any improvements to the assert grammar will immediately apply to the contract grammar too.
Jun 24
prev sibling parent reply Solomon E <default avatar.org> writes:
On Friday, 23 June 2017 at 21:14:47 UTC, MysticZach wrote:
 On Friday, 23 June 2017 at 20:49:35 UTC, Moritz Maxeiner wrote:
 ...
 I'm not sure what you're getting at here. With the proposal 
 Timon implemented we have the following:
 - the newer, decoupled contract syntax that separates contract 
 specification and implementation from each other; Timon's 
 implementation uses asserts as the internal contract 
 implementation AFAIK
Not exactly. It actually _couples_ `in` expressions with assert expressions. In essence it just lowers int fun(int a) in(a) { } ...to: int fun(int a) in { assert(a); } { } This is a sensible default. In the future, some logic could be added to provide a hook to a different implementation of the `in` function, so that it would lower instead to: int fun(int a) in { __userDefinedInContract(a); } { } But I suspect the vast majority will stick with the default.
I think my proposal to add another use of semicolon in parentheses, like `foreach` or `for` but not the same as either, was needlessly complicated. in (a) out (result) (a) as syntax sugar where each (a) lowers to {assert(a);} and in future can lower to something else, to renovate contract implementation That's so much easier, in every way.
Jun 23
next sibling parent MysticZach <reachzach ggmail.com> writes:
On Saturday, 24 June 2017 at 02:31:09 UTC, Solomon E wrote:
 I think my proposal to add another use of semicolon in 
 parentheses, like `foreach` or `for` but not the same as 
 either, was needlessly complicated.
It's very popular, actually. :)
 in (a)
 out (result) (a)
This resembles template function declarations. Both proposals resemble something else that they are not. Your proposal for `out` is attractive, actually, because the semantics of foreach are closer to what we're looking for than are the semantics of template declarations.
 as syntax sugar where each (a) lowers to
 {assert(a);}
 and in future can lower to something else, to renovate contract 
 implementation
The foreach syntax can be just as easily lowered: out(result; a) ...to: out(result) {assert(a);} That also includes the optional message. out(result; result < 1, "alert!") ...lowers to: out(result) { assert(result < 1, "alert!"); }
 That's so much easier, in every way.
There's no intention of making it complicated!
Jun 24
prev sibling parent Mark <smarksc gmail.com> writes:
On Saturday, 24 June 2017 at 02:31:09 UTC, Solomon E wrote:
 I think my proposal to add another use of semicolon in 
 parentheses, like `foreach` or `for` but not the same as 
 either, was needlessly complicated.

 in (a)
 out (result) (a)

 as syntax sugar where each (a) lowers to
 {assert(a);}
 and in future can lower to something else, to renovate contract 
 implementation

 That's so much easier, in every way.
Another option is to allow giving a name to a function's return value in its signature, so something like: int result myFunc(Args...)(Args args) if (Args.length > 1) in (args[0] > 0) out (result > 10); This is the approach taken by Dafny, which was mentioned earlier in the thread. Can't say I like it.
Jun 24
prev sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 18:42:55 UTC, Steven Schveighoffer 
wrote:
 On 6/23/17 2:24 PM, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 [...]

 Yeah, my take is that the grammar for `assert`s applies to 
 the new syntax as well. If the grammar for asserts is this:

 AssertExpression:
   assert ( AssertParameters )

 ... then the grammar for the new syntax is:

 InExpression:
   in ( AssertParameters )

 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
I'm all for this syntax, just one spec/implementation question: If the new contract syntax (formally) shares grammar rules with assert, won't that cause more work for people who want to update the assert syntax later (since they will have to take contracts into account)?
No. Asserts are the meat of in/out contracts, these are actually asserts. Anything you do to the assert grammar should be done here as well.
I'm not sure I get what you mean here: Asserts are just one possible implementation of contract checking; this new syntax explicitly decouples contract specification from contract checking (semantics), opening the way for future DIPs addressing such things as changing contract checking from assert to a different system and/or moving contract checking from the callee to the caller transparently. My worry is that having contract specification and contract implementation share grammar rules leaves them artificially coupled, though I don't know enough about the compiler internals to know for sure: That's the essence of my question.
Jun 23
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/23/17 3:51 PM, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 18:42:55 UTC, Steven Schveighoffer wrote:
 On 6/23/17 2:24 PM, Moritz Maxeiner wrote:
 I'm all for this syntax, just one spec/implementation question:
 If the new contract syntax (formally) shares grammar rules with 
 assert, won't that cause more work for people who want to update the 
 assert syntax later (since they will have to take contracts into 
 account)?
No. Asserts are the meat of in/out contracts, these are actually asserts. Anything you do to the assert grammar should be done here as well.
I'm not sure I get what you mean here: Asserts are just one possible implementation of contract checking; this new syntax explicitly decouples contract specification from contract checking (semantics), opening the way for future DIPs addressing such things as changing contract checking from assert to a different system and/or moving contract checking from the callee to the caller transparently. My worry is that having contract specification and contract implementation share grammar rules leaves them artificially coupled, though I don't know enough about the compiler internals to know for sure: That's the essence of my question.
Moving the check from the callee to the caller wouldn't matter here, it just defines when the asserts are called. I don't think there's an issue here -- you can implement some other form of checking if you wish, but it's going to be outside the language. You can always still do in and out block syntax. Or is this DIP proposing to do away with that? I didn't see any mention of deprecating current syntax. I view this improvement like short lambda syntax. When your lambda is one expression, so much easier and convenient to use the => form. But you can always reach for the multi-statement form if needed. -Steve
Jun 23
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Fri, Jun 23, 2017 at 04:15:38PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 You can always still do in and out block syntax. Or is this DIP
 proposing to do away with that? I didn't see any mention of
 deprecating current syntax.
 
 I view this improvement like short lambda syntax. When your lambda is
 one expression, so much easier and convenient to use the => form. But
 you can always reach for the multi-statement form if needed.
[...] Yes, this is also my understanding: the current syntax will be retained (hence, no breakage of current code), but a new syntax better suited for short contracts will be introduced. I think this is the right way to go. T -- It won't be covered in the book. The source code has to be useful for something, after all. -- Larry Wall
Jun 23
prev sibling parent reply Olivier FAURE <olivier.faure epitech.eu> writes:
On Friday, 23 June 2017 at 17:31:15 UTC, MysticZach wrote:
 Yeah, my take is that the grammar for `assert`s applies to the 
 new syntax as well. If the grammar for asserts is this:

 AssertExpression:
   assert ( AssertParameters )

 ... then the grammar for the new syntax is:

 InExpression:
   in ( AssertParameters )

 OutExpression:
   out ( ; AssertParameters )
   out ( Identifier ; AssertParameters )
A bit late to the party, but I would recommend the following syntax: out (void; myTest) for argument-less tests. A casual reader would be less likely to see this in code and think it's some sort of typo; it would be easier to google; and it would make some semantic sense (functions that don't return anything return void).
Jun 27
parent reply MysticZach <reachzach ggmail.com> writes:
On Tuesday, 27 June 2017 at 09:18:11 UTC, Olivier FAURE wrote:
 A bit late to the party, but I would recommend the following 
 syntax:

     out (void; myTest)

 for argument-less tests. A casual reader would be less likely 
 to see this in code and think it's some sort of typo; it would 
 be easier to google; and it would make some semantic sense 
 (functions that don't return anything return void).
It's a creative suggestion, and not a bad one. But it's verbose, and I'd like to be able to omit the identifier altogether. Currently, only `for` loops allow this, as when people write: for( ; ; ) Theoretically, `foreach foreach( ; a) ... out( ; ...) Currently `foreach` does not allow omitting
Jun 27
parent MysticZach <reachzach ggmail.com> writes:
On Wednesday, 28 June 2017 at 01:23:18 UTC, MysticZach wrote:
 On Tuesday, 27 June 2017 at 09:18:11 UTC, Olivier FAURE wrote:
 A bit late to the party, but I would recommend the following 
 syntax:

     out (void; myTest)

 for argument-less tests. A casual reader would be less likely 
 to see this in code and think it's some sort of typo; it would 
 be easier to google; and it would make some semantic sense 
 (functions that don't return anything return void).
It's a creative suggestion, and not a bad one. But it's verbose, and I'd like to be able to omit the identifier altogether. Currently, only `for` loops allow this, as when people write: for( ; ; ) Theoretically, `foreach foreach( ; a) ... out( ; ...) Currently `foreach` does not allow omitting
Sorry, clicked the `send` button too soon. Anyway, currently `foreach` does not allow omitting the initial identifier, but in theory it _could_ be enhanced to do so. If `out` expressions also allow this, then we get the desired symmetry between `for`,`foreach`, and `out` expressions, with minimal verbosity. That's the solution I promote. It's better than requiring something to be there when nothing really has to be. I don't know why `foreach` isn't already this way.
Jun 27
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 23.06.2017 18:21, H. S. Teoh via Digitalmars-d wrote:
 On Fri, Jun 23, 2017 at 09:06:59AM +0000, Solomon E via Digitalmars-d wrote:
 [...]
 T foo(T)(T x, T y)
      in (x > 0, y > 0)
      out (r; r > 0)
 {
      return x % y + 1;
 }
Hmm, I like this syntax for out-contracts! It borrows from existing foreach syntax, so it has some precedence, whereas the previous proposal of `out(...)(...)` looks uglier and also looks deceptively like a template function declaration. `out (r; r > 0)` gets my vote. OTOH, I don't like the comma in the in-contract. Let's just keep it as either separate clauses: in (x > 0) in (y > 0) or just use a boolean operator: in (x > 0 && y > 0) T
Agreed. Implementation: https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax (At most one contract of each type is supported. It is not very hard to implement multiple contracts, but this requires touching semantic analysis.)
Jun 23
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 23 June 2017 at 18:03:26 UTC, Timon Gehr wrote:
 On 23.06.2017 18:21, H. S. Teoh via Digitalmars-d wrote:
 [...]
 
Agreed. Implementation: https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax (At most one contract of each type is supported. It is not very hard to implement multiple contracts, but this requires touching semantic analysis.)
Holy ***. I set aside some time this weekend to try implementing this myself as an exercise, but oh well. Thanks!
Jun 23
parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 18:20:23 UTC, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 18:03:26 UTC, Timon Gehr wrote:
 On 23.06.2017 18:21, H. S. Teoh via Digitalmars-d wrote:
 [...]
 
Agreed. Implementation: https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax (At most one contract of each type is supported. It is not very hard to implement multiple contracts, but this requires touching semantic analysis.)
Holy ***. I set aside some time this weekend to try implementing this myself as an exercise, but oh well. Thanks!
I know! I'd say it'll take me a good deal longer to rewrite the DIP than he's taking to implement it!
Jun 23
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Fri, Jun 23, 2017 at 06:57:57PM +0000, MysticZach via Digitalmars-d wrote:
 On Friday, 23 June 2017 at 18:20:23 UTC, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 18:03:26 UTC, Timon Gehr wrote:
[...]
 Agreed. Implementation:
 https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax
 
 (At most one contract of each type is supported. It is not very
 hard to implement multiple contracts, but this requires touching
 semantic analysis.)
Holy ***. I set aside some time this weekend to try implementing this myself as an exercise, but oh well. Thanks!
I know! I'd say it'll take me a good deal longer to rewrite the DIP than he's taking to implement it!
Yeah! Kudos to Timon for the quick implementation! This will increase the odds of this DIP being accepted, I'm sure. :-) T -- "Real programmers can write assembly code in any language. :-)" -- Larry Wall
Jun 23
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 23.06.2017 21:04, H. S. Teoh via Digitalmars-d wrote:
 On Fri, Jun 23, 2017 at 06:57:57PM +0000, MysticZach via Digitalmars-d wrote:
 On Friday, 23 June 2017 at 18:20:23 UTC, Moritz Maxeiner wrote:
 On Friday, 23 June 2017 at 18:03:26 UTC, Timon Gehr wrote:
[...]
 Agreed. Implementation:
 https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax

 (At most one contract of each type is supported. It is not very
 hard to implement multiple contracts, but this requires touching
 semantic analysis.)
Holy ***. I set aside some time this weekend to try implementing this myself as an exercise, but oh well. Thanks!
I know! I'd say it'll take me a good deal longer to rewrite the DIP than he's taking to implement it!
Yeah! Kudos to Timon for the quick implementation!
Thanks! :) Support for multiple contracts: https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax
 This will increase
 the odds of this DIP being accepted, I'm sure. :-)
 
 
 T
 
Jun 24
prev sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Friday, 23 June 2017 at 00:17:23 UTC, Steven Schveighoffer 
wrote:
 We can call that contextual "keyword" result. Doesn't have to 
 be a keyword, just the implied return value symbol name.
Another problem is that any given contextual reserved identifier you choose will run the risk of shadowing other variables. `result` could easily already be defined in an outer context. A solution to this is to use a variable starting with `__`, as those are the existing way of defining reserved identifiers. Maybe `__result`?
Jun 22
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 23.06.2017 06:27, MysticZach wrote:
 On Friday, 23 June 2017 at 00:17:23 UTC, Steven Schveighoffer wrote:
 We can call that contextual "keyword" result. Doesn't have to be a 
 keyword, just the implied return value symbol name.
Another problem is that any given contextual reserved identifier you choose will run the risk of shadowing other variables. `result` could easily already be defined in an outer context. A solution to this is to use a variable starting with `__`, as those are the existing way of defining reserved identifiers. Maybe `__result`?
out{ assert(__result>0); } actually works already, but I think it is undocumented. ;)
Jun 23
prev sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 22 June 2017 at 05:46:06 UTC, MysticZach wrote:
 On Thursday, 22 June 2017 at 00:27:38 UTC, Timon Gehr wrote:

 But it still has a sticking point, which I want to resolve, 
 namely that it elevates the existing `assert` functionality 
 beyond the current requirement that one must explicitly write 
 `assert`, going so far as to imply it in the grammar itself.
What happens with H.S. Teoh's proposal is not that `assert` is implied by the grammar, but that a function can *finally* specify its contracts *independent* of how those contracts are actually implemented. That's a good thing, because a) it allows the contract implementation for that new syntax to be swapped out transparently (no changes need to be made by the programmer to get a better contract implementation) b) it makes contracts more compact (and thus easier to read) without losing relevant information (contract implementation is not something you should have to deal with for every single library independently)
 But because of many complaints about the limitations of the 
 assert mechanism as it currently exists, I would hesitate to 
 install it into the grammar as the "One Chosen Way" to bail out 
 of contracts with the new syntax.
Again, that's not what H.S. Teoh's proposal would do. All it does is install an *implementation agnostic*, *abtract* way to specify contracts into the grammar. Whether that is lowered to assert, or anything else is an implementation detail and it certainly isn't fixed to asserts. Simply document the behaviour one can expect when using it in debug and release mode and leave the implementation details (assert or something else) unspecified.
 However, if the functioning of the `assert` mechanism were more 
 customizable, then it would be easier to entrust it, in my 
 opinion, with the "sacred responsibility" of being installed 
 into the grammar. H.S. Teoh's proposal would then stand on a 
 firmer foundation, and my initial proposed syntax would become 
 the inferior optionl. At that point, this DIP could be 
 rewritten to advocate his syntax instead.
A more customizable assert would not change anything for the DIP imo, since I do not want implementation details to be part of contract writing.
 The intent of my proposal was to make a small improvement. The 
 cost (or so I thought, and may still believe) was also small. 
 Small improvements are still improvements. DIP1003 is an 
 example of this.
DIP1003 did not introduce an entirely new system, it merely slightly changed an existing sytem. DIP1009 *does* introduce several new syntax forms, i.e. adding a whole new system, which means it *also* introduces the responsibility of maintaining backwards compatibility when someone tries to improve contracts again (and then we would have three systems to specify contracts). Both DIP1003 and DIP1009 need to have a positive benefit/cost balance, which is easy for DIP1003 (since the costs are insignificant), but not for DIP1009: The costs are high down the line and it needs to justify them. The only way I can see those costs being justified is by separating constract specification from contract implementation and that requires syntax similar to H.S. Teoh's proposal.
Jun 22
parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 10:59:18 UTC, Moritz Maxeiner wrote:
 Again, that's not what H.S. Teoh's proposal would do. All it 
 does is install an *implementation agnostic*, *abtract* way to 
 specify contracts into the grammar. Whether that is lowered to 
 assert, or anything else is an implementation detail and it 
 certainly isn't fixed to asserts.
Okay. Then the proposal needs to be accompanied by an explicit description of how to hook into the new semantics. Which is what you provided, but I suppose it would need to be exactly specified. In particular, where exactly is the code for the (optional) user-defined hook to be found? In a separate file, maybe as indicated with a command line option, e.g. `-contractsConfig=myContracts.d`?
 DIP1003 did not introduce an entirely new system, it merely 
 slightly changed an existing sytem.
 DIP1009 *does* introduce several new syntax forms, i.e. adding 
 a whole new system, which means it *also* introduces the 
 responsibility of maintaining backwards compatibility when 
 someone tries to improve contracts again (and then we would 
 have three systems to specify contracts).
Okay, I'm a convert. Although still one big downside is that DbC in D is somewhat impaired, as H.S. Teoh pointed out [1]. [1] http://forum.dlang.org/post/mailman.3554.1498074198.31550.digitalmars-d puremagic.com
Jun 22
next sibling parent reply MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 12:54:00 UTC, MysticZach wrote:
 On Thursday, 22 June 2017 at 10:59:18 UTC, Moritz Maxeiner 
 wrote:
 Again, that's not what H.S. Teoh's proposal would do. All it 
 does is install an *implementation agnostic*, *abtract* way to 
 specify contracts into the grammar. Whether that is lowered to 
 assert, or anything else is an implementation detail and it 
 certainly isn't fixed to asserts.
Okay. Then the proposal needs to be accompanied by an explicit description of how to hook into the new semantics. Which is what you provided, but I suppose it would need to be exactly specified. In particular, where exactly is the code for the (optional) user-defined hook to be found? In a separate file, maybe as indicated with a command line option, e.g. `-contractsConfig=myContracts.d`?
As long as one is doing this, maybe a proposal for user-defined `assert` should be provided too? It'd be interesting to have a dedicated file for user-defined assert and contract configuration. The core idea is to allow *everyone*, from large companies to individual hobbyists, to use `assert`, `in`, and `out`, and have it work the way they want. I would definitely need help writing that DIP.
Jun 22
next sibling parent MysticZach <reachzach ggmail.com> writes:
On Thursday, 22 June 2017 at 13:11:12 UTC, MysticZach wrote:
 As long as one is doing this, maybe a proposal for user-defined 
 `assert` should be provided too? It'd be interesting to have a 
 dedicated file for user-defined assert and contract 
 configuration. The core idea is to allow *everyone*, from large 
 companies to individual hobbyists, to use `assert`, `in`, and 
 `out`, and have it work the way they want. I would definitely 
 need help writing that DIP.
:-)
Jun 22
prev sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 22 June 2017 at 13:11:12 UTC, MysticZach wrote:
 On Thursday, 22 June 2017 at 12:54:00 UTC, MysticZach wrote:
 On Thursday, 22 June 2017 at 10:59:18 UTC, Moritz Maxeiner 
 wrote:
 Again, that's not what H.S. Teoh's proposal would do. All it 
 does is install an *implementation agnostic*, *abtract* way 
 to specify contracts into the grammar. Whether that is 
 lowered to assert, or anything else is an implementation 
 detail and it certainly isn't fixed to asserts.
Okay. Then the proposal needs to be accompanied by an explicit description of how to hook into the new semantics. Which is what you provided, but I suppose it would need to be exactly specified. In particular, where exactly is the code for the (optional) user-defined hook to be found? In a separate file, maybe as indicated with a command line option, e.g. `-contractsConfig=myContracts.d`?
As long as one is doing this, maybe a proposal for user-defined `assert` should be provided too? It'd be interesting to have a dedicated file for user-defined assert and contract configuration. The core idea is to allow *everyone*, from large companies to individual hobbyists, to use `assert`, `in`, and `out`, and have it work the way they want. I would definitely need help writing that DIP.
Upon reflection, that is exactly what my Insister proposal is :p The reason it's not called Asserter, btw, is that assert is a a) a keyword, and b) is already deeply entrenched with the concept of Errors (bugs), in D and that `enforce`, is also de facto established for Exceptions (user input error), so `insist` would then be a configurable chooser between the two.
Jun 22
prev sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 22 June 2017 at 12:54:00 UTC, MysticZach wrote:
 On Thursday, 22 June 2017 at 10:59:18 UTC, Moritz Maxeiner 
 wrote:
 Again, that's not what H.S. Teoh's proposal would do. All it 
 does is install an *implementation agnostic*, *abtract* way to 
 specify contracts into the grammar. Whether that is lowered to 
 assert, or anything else is an implementation detail and it 
 certainly isn't fixed to asserts.
Okay. Then the proposal needs to be accompanied by an explicit description of how to hook into the new semantics. Which is what you provided, but I suppose it would need to be exactly specified. In particular, where exactly is the code for the (optional) user-defined hook to be found? In a separate file, maybe as indicated with a command line option, e.g. `-contractsConfig=myContracts.d`?
I would put the selection mechanics and prefinded semantics into druntime, e.g. under core.contracts. In particular I'm thinking about Insister [1] and if it makes sense to put something like that in core.contracts, expose a `IInsister theContractChecker` that can be overwritten at runtime and have the compiler lower contract checks to `theContractChecker.insist` calls. I'm not sure if that is the best way to go forward, but it would be nice for the configurability to be remain in language constructs without requiring addition of compiler options, I think.
 DIP1003 did not introduce an entirely new system, it merely 
 slightly changed an existing sytem.
 DIP1009 *does* introduce several new syntax forms, i.e. adding 
 a whole new system, which means it *also* introduces the 
 responsibility of maintaining backwards compatibility when 
 someone tries to improve contracts again (and then we would 
 have three systems to specify contracts).
Okay, I'm a convert. Although still one big downside is that DbC in D is somewhat impaired, as H.S. Teoh pointed out [1].
Yay! Regarding the location of contract checking: Yes, though I agree with him that a fix for that need not be part of this DIP. If this DIP cleanly separates contract specification from contract implementation, we can address it separately. In particular, if something like Insister gets chosen as the framework, then the issue of contract checking not occuring on the caller site should be fixable simply by changing where the compiler injects the lowered `theContractChecker.insist` into. [1] http://forum.dlang.org/post/pdqdaktjifgajiwnpxaz forum.dlang.org
Jun 22
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jun 22, 2017 at 01:33:17PM +0000, Moritz Maxeiner via Digitalmars-d
wrote:
[...]
 Regarding the location of contract checking: Yes, though I agree with
 him that a fix for that need not be part of this DIP. If this DIP
 cleanly separates contract specification from contract implementation,
 we can address it separately.
[...] Not to mention, if we restrict the scope of this DIP to just syntax improvement, it stands a better chance of being accepted by Walter & Andrei. Stuffing too many things into it stands the risk of having the whole thing rejected because of just one or two items that they deem undesirable. T -- Trying to define yourself is like trying to bite your own teeth. -- Alan Watts
Jun 22
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 20.06.2017 13:57, Mike Parker wrote:
 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!
The DIP is missing the corresponding syntax for invariants: class C{ private int x=1; invariant(x>0, "x must stay positive"); } Implementation: https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax
Jun 26
next sibling parent MysticZach <reachzach ggmail.com> writes:
On Monday, 26 June 2017 at 21:06:10 UTC, Timon Gehr wrote:
 The DIP is missing the corresponding syntax for invariants:

 class C{
     private int x=1;
     invariant(x>0, "x must stay positive");
 }

 Implementation:
 https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax
Well done. It seems to speak to the intuitive nature of the design that it's so easy to deduce how it would work in this context.
Jun 26
prev sibling parent Wulfklaue <wulfklaue wulfklaue.com> writes:
On Monday, 26 June 2017 at 21:06:10 UTC, Timon Gehr wrote:
 Implementation:
 https://github.com/dlang/dmd/compare/master...tgehr:contract-syntax
As one of the newbie D users, who just happened to look at contract programming. The proposed syntax for me: * Its clear to write, not too much useless syntax * Its not overly complex * It fits with the language * Easy to see the logic and order * Useful to the point that it can remove a lot of boilerplate code out of the body structure
Jun 27
prev sibling next sibling parent reply Mark <smarksc gmail.com> writes:
On Tuesday, 20 June 2017 at 11:57:55 UTC, Mike Parker wrote:
 DIP 1009 is titled "Improve Contract Usability".

 [...]
Veering a bit off topic,the compiler doesn't treat contracts any different from other code, does it? For instance, consider: int foo() out(result; result>0) { // whatever } int bar(int x) in (x<0) { // whatever } int main() { return bar(foo()); } I would have liked to get a *compile-time* error in case of such a trivial contract violation. Of course, I don't expect the compilier to try and prove that a contract holds (seems unrealitic and not so useful) but it would be nice if it could at least spot simple errors, like in the example above. In a similar vein, the information conveyed in a contract can potentially be used to apply simple optimizations, at least in trivial cases. If the contract system amounts to placing assertions at the beginning and/or end of a function, it doesn't seem so useful except for documentation and readability.
Jun 27
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 22:45:17 UTC, Mark wrote:
 On Tuesday, 20 June 2017 at 11:57:55 UTC, Mike Parker wrote:
 DIP 1009 is titled "Improve Contract Usability".

 [...]
Veering a bit off topic,the compiler doesn't treat contracts any different from other code, does it? For instance, consider: [...] I would have liked to get a *compile-time* error [...]
Compile time contract violation checking? While an interesting, I'm not sure the benefits would outweigh the implementation costs.
 If the contract system amounts to placing assertions at the 
 beginning and/or end of a function, it doesn't seem so useful 
 except for documentation and readability.
Considering that code is read a lot more than written those two are *critically* important. What more do you expect? It could eventually be optimized to inject the in contract check at the caller's side (before entering the function), but the point of contracts is that they *aren't* violated. If they are, well, your program[1] is broken and needs to die. [1] abstract: computational task; implementation barring safe thread/fiber killing: process
Jun 27
parent reply Mark <smarksc gmail.com> writes:
On Tuesday, 27 June 2017 at 23:17:15 UTC, Moritz Maxeiner wrote:
 Considering that code is read a lot more than written those two 
 are *critically* important.
 What more do you expect? It could eventually be optimized to 
 inject the in contract check at the caller's side (before 
 entering the function), but the point of contracts is that they 
 *aren't* violated. If they are, well, your program[1] is broken 
 and needs to die.

 [1] abstract: computational task; implementation barring safe 
 thread/fiber killing: process
Sure, but it would be even better if it hadn't compiled in the first place. For instance, the "pure" attribute is a sort of contract, and if a function designated "pure" modifies global state then my code *does not compile*. Furthermore, if a pure function foo() contains a call to a function bar() not designated "pure", then it doesn't compile either, even though bar() may in fact be pure. So the compiler's behavior is "I can't guarentee that this function is pure, therefore I refuse to compile". In other words, I get useful error-checking from the compiler at the cost of having to "help" him a bit from time to time (like designating bar() as pure in this example). The above behavior is unreasonable for validating a completely general contract - the compiler won't be able to guarentee much without excessive effort on the part of the programmer, supplying it with informative preconditions and postconditions. Nevertheless, as attributes like pure, const, safe and nogc demonstrate, the compiler can be of great help in writing correct code. Detecting violations of these function attributes in run-time is trivial but far less useful. So basically, I'd like the ability to implement fairly simple contracts (with a similar level of sophistication to the above examples) that the compiler can check for me. I don't know, maybe this is easier to implement using UDAs...
Jun 28
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jun 29, 2017 at 01:22:31AM +0000, Mark via Digitalmars-d wrote:
[...]
 So basically, I'd like the ability to implement fairly simple
 contracts (with a similar level of sophistication to the above
 examples) that the compiler can check for me. I don't know, maybe this
 is easier to implement using UDAs...
One idea I have is that the compiler could recognize certain straightforward contracts (like int < value) and use VRP (value range propagation) to detect cases where it's clear that the contract would be violated. However, this can only be done in a very rudimentary fashion, because: (1) DbC contracts pertain to *runtime* argument values, so while checking for simple cases at compile-time is nice, it isn't really in the charter of (D's implementation of) DbC. (2) VRP in the compiler currently only works within a very limited scope, IIRC within a single expression. So while it may detect the `foo(bar())` case, it probably won't detect the `x=bar(); foo(x);` case. Ostensibly the scope of VRP ought to be expanded, however: (3) In the past Walter has shown some reluctance in adding features that hurt compilation time; if a more sophisticated implementation of VRP is required and it's deemed too expensive in terms of compilation time, Walter may veto it. (4) Arbitrarily complex boolean conditions are undecidable in general, so unless you have a way to solve the halting problem, a general solution is intractible. Of course, the kind of conditions you find in contracts generally ought to be nowhere near the halting problem in complexity, but it's not clear how far one can go without running the risk of (3). My guess is that something based on VRP is the most likely to materialize in the foreseeable future. T -- First Rule of History: History doesn't repeat itself -- historians merely repeat each other.
Jun 28
next sibling parent reply Petar Kirov [ZombineDev] <petar.p.kirov gmail.com> writes:
On Thursday, 29 June 2017 at 01:37:17 UTC, H. S. Teoh wrote:
 On Thu, Jun 29, 2017 at 01:22:31AM +0000, Mark via 
 Digitalmars-d wrote: [...]
 So basically, I'd like the ability to implement fairly simple 
 contracts (with a similar level of sophistication to the above 
 examples) that the compiler can check for me. I don't know, 
 maybe this is easier to implement using UDAs...
One idea I have is that the compiler could recognize certain straightforward contracts (like int < value) and use VRP (value range propagation) to detect cases where it's clear that the contract would be violated. However, this can only be done in a very rudimentary fashion, because: (1) DbC contracts pertain to *runtime* argument values, so while checking for simple cases at compile-time is nice, it isn't really in the charter of (D's implementation of) DbC. (2) VRP in the compiler currently only works within a very limited scope, IIRC within a single expression. So while it may detect the `foo(bar())` case, it probably won't detect the `x=bar(); foo(x);` case. Ostensibly the scope of VRP ought to be expanded, however: (3) In the past Walter has shown some reluctance in adding features that hurt compilation time; if a more sophisticated implementation of VRP is required and it's deemed too expensive in terms of compilation time, Walter may veto it. (4) Arbitrarily complex boolean conditions are undecidable in general, so unless you have a way to solve the halting problem, a general solution is intractible. Of course, the kind of conditions you find in contracts generally ought to be nowhere near the halting problem in complexity, but it's not clear how far one can go without running the risk of (3). My guess is that something based on VRP is the most likely to materialize in the foreseeable future. T
https://github.com/dlang/dmd/pull/3799
Jun 29
parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jun 29, 2017 at 08:22:50AM +0000, via Digitalmars-d wrote:
 On Thursday, 29 June 2017 at 01:37:17 UTC, H. S. Teoh wrote:
[...]
 One idea I have is that the compiler could recognize certain
 straightforward contracts (like int < value) and use VRP (value
 range propagation) to detect cases where it's clear that the
 contract would be violated.
[...]
 https://github.com/dlang/dmd/pull/3799
Haha, funny, I've seen that PR before but totally forgot about it. Seems to have reached an impasse, unfortunately. Seems that Walter and other are not perfectly happy with it, and handing contracts off to CTFE raises other issues. T -- Some days you win; most days you lose.
Jun 29
prev sibling parent Mark <smarksc gmail.com> writes:
On Thursday, 29 June 2017 at 01:37:17 UTC, H. S. Teoh wrote:
 (1) DbC contracts pertain to *runtime* argument values, so while
 checking for simple cases at compile-time is nice, it isn't 
 really in
 the charter of (D's implementation of) DbC.


 T
That's a good point. I guess UDAs combined with static assert is the right way to implement compile-time checkable contracts. This seems to be the approach taken by Andrei in his Big O notation library [1]. Anyway, contracts are still useful for documentation purposes, as was pointed out. Personally I like them because they encourage me to think carefully about the preconditions and postconditions of the functions I write. [1] http://forum.dlang.org/post/mailman.898.1482091540.9448.digitalmars-d puremagic.com
Jun 30
prev sibling parent Mike Parker <aldacron gmail.com> writes:
On Tuesday, 20 June 2017 at 11:57:55 UTC, Mike Parker wrote:
 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.
So this review period is now closed. Thanks to all who participated.
Jul 04