www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "for" statement issue

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I got a question about what happens with this code:

int j;
for({j=2; int d = 3; } j+d<7; {j++; d++;}) {
}

My first instinct was that that won't compile but it surprisingly does. 
And it loops forever.

So the grammar according to 
https://dlang.org/spec/grammar.html#ForStatement is:

ForStatement:
     for ( Initialize Testopt ; Incrementopt ) ScopeStatement

Initialize:
     ;
     NoScopeNonEmptyStatement

NoScopeNonEmptyStatement:
     NonEmptyStatement
     BlockStatement

NonEmptyStatement goes over a bunch of odd places such as case statement 
and default statement. And then BlockStatement is the matched case:

BlockStatement:
     { }
     { StatementList }

So it seems we have another case in which "{" "}" do not introduce a 
scope. Fine. The real problem is with the increment part, which is an 
expression. The code { j++; d++; } is... a lambda expression that never 
gets used, which completes a very confusing sample.

What would be a good solution to forbid certain constructs in the 
increment part of a for statement?


Thanks,

Andrei
Oct 21 2016
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 21 October 2016 at 12:34:58 UTC, Andrei Alexandrescu 
wrote:
 I got a question about what happens with this code:

 int j;
 for({j=2; int d = 3; } j+d<7; {j++; d++;}) {
 }

 [...]
We could restrict the initialze part to assignments only. But I am unsure of the implications. How did you find this case?
Oct 21 2016
prev sibling next sibling parent reply RazvanN <razvan.nitu1305 gmail.com> writes:
On Friday, 21 October 2016 at 12:34:58 UTC, Andrei Alexandrescu 
wrote:
 I got a question about what happens with this code:

 int j;
 for({j=2; int d = 3; } j+d<7; {j++; d++;}) {
 }

 My first instinct was that that won't compile but it 
 surprisingly does. And it loops forever.

 So the grammar according to 
 https://dlang.org/spec/grammar.html#ForStatement is:

 ForStatement:
     for ( Initialize Testopt ; Incrementopt ) ScopeStatement

 Initialize:
     ;
     NoScopeNonEmptyStatement

 NoScopeNonEmptyStatement:
     NonEmptyStatement
     BlockStatement

 NonEmptyStatement goes over a bunch of odd places such as case 
 statement and default statement. And then BlockStatement is the 
 matched case:

 BlockStatement:
     { }
     { StatementList }

 So it seems we have another case in which "{" "}" do not 
 introduce a scope. Fine. The real problem is with the increment 
 part, which is an expression. The code { j++; d++; } is... a 
 lambda expression that never gets used, which completes a very 
 confusing sample.

 What would be a good solution to forbid certain constructs in 
 the increment part of a for statement?


 Thanks,

 Andrei
I am the one who raised the question. I am n00b when it comes to D language (I just started reading about it a couple of days ago) and I tried the above mentioned code expecting that either the variables j and d get incremented accordingly or at least I would get a compilation error. Instead, the program compiles and when run it sticks into an infinite loop. I haven't read anything about lambda functions in D, but the current outcome is very confusing for a beginner like myself. Thanks, RazvanN
Oct 21 2016
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 21 October 2016 at 13:18:19 UTC, RazvanN wrote:
 On Friday, 21 October 2016 at 12:34:58 UTC, Andrei Alexandrescu 
 wrote:
 [...]
I am the one who raised the question. I am n00b when it comes to D language (I just started reading about it a couple of days ago) and I tried the above mentioned code expecting that either the variables j and d get incremented accordingly or at least I would get a compilation error. Instead, the program compiles and when run it sticks into an infinite loop. I haven't read anything about lambda functions in D, but the current outcome is very confusing for a beginner like myself. Thanks, RazvanN
Ah. It does create a lambda? Hmm that should not happen. I agree this is confusing and unwanted. Please feel free to post this to bugzilla. I will take a look next month. If nobody resolves it before then.
Oct 21 2016
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 It does create a lambda?
 Hmm that should not happen.
Eh, that's exactly what the language rules say should happen, and it actually does make sense to me... you might even want to use an immediately-called lambda to group several statements together into one expression. Though I have become convinced recently that we should deprecate the `{ lambdas }` in favor of `() { lambdas }`. This is the same mistake as `() => {xxx}` that we see a bunch of newbies make. If the syntax was changed to require the empty parens for the args, `() {}`, people would be a lot less likely to mess this up... and the rest of us don't seriously lose anything, adding `()` is easy enough if they aren't already there. I think deprecating `{ lambda }` is really the way to go. It'd fix this as well at that other FAQ at pretty low cost.
Oct 21 2016
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Friday, 21 October 2016 at 13:42:49 UTC, Adam D. Ruppe wrote:
 On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 It does create a lambda?
 Hmm that should not happen.
I think deprecating `{ lambda }` is really the way to go. It'd fix this as well at that other FAQ at pretty low cost.
Yes lets make it happen! { } has too many meanings. Lets deprecate this one.
Oct 21 2016
prev sibling next sibling parent reply Temtaime <temtaime gmail.com> writes:
On Friday, 21 October 2016 at 13:42:49 UTC, Adam D. Ruppe wrote:
 On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 [...]
Eh, that's exactly what the language rules say should happen, and it actually does make sense to me... you might even want to use an immediately-called lambda to group several statements together into one expression. [...]
Please, no. It's fully clear that { stmts } createa a lambda, just () is ommited. foo({ code; }); is always OK and we shouldn't deprecate it.
Oct 21 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/21/16 10:12 AM, Temtaime wrote:
 On Friday, 21 October 2016 at 13:42:49 UTC, Adam D. Ruppe wrote:
 On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 [...]
Eh, that's exactly what the language rules say should happen, and it actually does make sense to me... you might even want to use an immediately-called lambda to group several statements together into one expression. [...]
Please, no. It's fully clear that { stmts } createa a lambda, just () is ommited.
No, it's not. { int x; x = 2; } Is not a lambda. It's a scope. So the meaning changes based on where it's used. I totally agree that we should remove that feature. -Steve
Oct 21 2016
parent reply mogu <mogucpp 163.com> writes:
On Friday, 21 October 2016 at 14:22:27 UTC, Steven Schveighoffer 
wrote:
 On 10/21/16 10:12 AM, Temtaime wrote:
 On Friday, 21 October 2016 at 13:42:49 UTC, Adam D. Ruppe 
 wrote:
 On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 [...]
Eh, that's exactly what the language rules say should happen, and it actually does make sense to me... you might even want to use an immediately-called lambda to group several statements together into one expression. [...]
Please, no. It's fully clear that { stmts } createa a lambda, just () is ommited.
No, it's not. { int x; x = 2; } Is not a lambda. It's a scope. So the meaning changes based on where it's used. I totally agree that we should remove that feature. -Steve
{} in swift is a lambda too. I think swift has better lambda syntax. Maybe could help for a better syntax in d. reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } ) reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } ) reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } ) reversedNames = names.sorted(by: { $0 > $1 } ) reversedNames = names.sorted(by: >) someFunctionThatTakesAClosure(closure: { // closure's body goes here }) someFunctionThatTakesAClosure() { // trailing closure's body goes here } let strings = numbers.map { (number) -> String in var number = number var output = "" repeat { output = digitNames[number % 10]! + output number /= 10 } while number > 0 return output }
Oct 21 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/21/16 10:38 AM, mogu wrote:
 On Friday, 21 October 2016 at 14:22:27 UTC, Steven Schveighoffer wrote:
 On 10/21/16 10:12 AM, Temtaime wrote:
 On Friday, 21 October 2016 at 13:42:49 UTC, Adam D. Ruppe wrote:
 On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 [...]
Eh, that's exactly what the language rules say should happen, and it actually does make sense to me... you might even want to use an immediately-called lambda to group several statements together into one expression. [...]
Please, no. It's fully clear that { stmts } createa a lambda, just () is ommited.
No, it's not. { int x; x = 2; } Is not a lambda. It's a scope. So the meaning changes based on where it's used. I totally agree that we should remove that feature.
{} in swift is a lambda too.
Swift doesn't have arbitrary scopes. So there is no ambiguity.
 I think swift has better lambda syntax.
 Maybe could help for a better syntax in d.
We likely are not going to change the lambda syntax. However, it's possible we could remove the ambiguous cases.
 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
     return s1 > s2
 })
 reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
 return s1 > s2 } )
 reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
 reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
(s1, s2) => s1 > s2 Seems pretty good to me
 reversedNames = names.sorted(by: { $0 > $1 } )
With the original string lambdas, this was possible as "a > b". I'm not sure this case is worth much effort to add.
 reversedNames = names.sorted(by: >)
Not sure if we'll ever get this in D :)
 someFunctionThatTakesAClosure(closure: {
     // closure's body goes here
 })
 someFunctionThatTakesAClosure() {
     // trailing closure's body goes here
 }
Yes, I've seen and used this. I think this is actually a little confusing. -Steve
Oct 21 2016
parent reply mogu <mogucpp 163.com> writes:
Allow new syntax makes codes simpler in some cases:

writeln({
     int a = 5;
     return a + 5;
}());

=>

writeln{
     int a = 5;
     return a + 5;
}();

[1,2,3].fold!((a, b) => a + b).writeln;

=>

[1,2,3].fold!{a, b => a + b}.writeln;
Oct 21 2016
parent reply Nick Treleaven <nick geany.org> writes:
On Friday, 21 October 2016 at 15:26:12 UTC, mogu wrote:
 [1,2,3].fold!((a, b) => a + b).writeln;

 =>

 [1,2,3].fold!{a, b => a + b}.writeln;
Probably (a, b => a + b) could be legal. Reasoning: 1. a could be an existing symbol in scope, otherwise it's an undefined identifier error. 2. If a was interpreted as an existing symbol which is followed by the comma operator, the expression (a) wouldn't have side effects so should be a compile error. 3. The bracketed comma expression would have to return the lambda b=>a+b as a value expression, which cannot compile because there are no arguments supplied for calling the lambda to obtain a value. So this syntax seems available as it isn't currently used for working code. A small change, maybe, but it's good to reduce bracket nesting to help with reading complex nested expressions. Destroy.
Oct 22 2016
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 10/22/2016 05:53 PM, Nick Treleaven wrote:
 Probably (a, b => a + b) could be legal. Reasoning:

 1. a could be an existing symbol in scope, otherwise it's an undefined
 identifier error.
 2. If a was interpreted as an existing symbol which is followed by the
 comma operator, the expression (a) wouldn't have side effects so should
 be a compile error.
How is it guaranteed that `a` doesn't have side effects? May be a function call, since empty parentheses can be omitted in calls.
 3. The bracketed comma expression would have to return the lambda b=>a+b
 as a value expression, which cannot compile because there are no
 arguments supplied for calling the lambda to obtain a value.
The lambda itself is a value, no? ---- int a() { import std.stdio; writeln("a"); return 1; } void main() { int delegate(int) dg = (a, b => a + b); } ----
Oct 22 2016
parent Nick Treleaven <nick geany.org> writes:
On Saturday, 22 October 2016 at 17:11:26 UTC, ag0aep6g wrote:
 How is it guaranteed that `a` doesn't have side effects? May be 
 a function call, since empty parentheses can be omitted in 
 calls.
I missed that case. (Insert grumble about non-UFCS parenthesis omission being allowed).
 The lambda itself is a value, no?

 ----
 int a() { import std.stdio; writeln("a"); return 1; }

 void main()
 {
     int delegate(int) dg = (a, b => a + b);
 }
OK. Though AIUI from 2.072 a() must return void for the comma expression to compile (then a+b wouldn't compile either).
Oct 23 2016
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 22.10.2016 17:53, Nick Treleaven wrote:
 [1,2,3].fold!{a, b => a + b}.writeln;
Probably (a, b => a + b) could be legal.
It is. (It means pass a and the lambda b => a + b.)
Oct 22 2016
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/21/2016 09:42 AM, Adam D. Ruppe wrote:
 I think deprecating `{ lambda }` is really the way to go.
Another possibility is to disallow an ExpressionStatement that consists solely of a lambda. There is precedent for that, e.g. the statement "1 + 1;" is disallowed. -- Andrei
Oct 21 2016
prev sibling parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 21 October 2016 at 13:42:49 UTC, Adam D. Ruppe wrote:
 On Friday, 21 October 2016 at 13:33:26 UTC, Stefan Koch wrote:
 It does create a lambda?
 Hmm that should not happen.
Eh, that's exactly what the language rules say should happen, and it actually does make sense to me... you might even want to use an immediately-called lambda to group several statements together into one expression.
int j; for({j=2; int d = 3; } j+d<7; {j++; d++;}) { } I'm more surprised by the fact that `d` is declared inside the first curly braces, but is evidently still in scope outside of it...
Oct 22 2016
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/21/16 8:34 AM, Andrei Alexandrescu wrote:
 I got a question about what happens with this code:

 int j;
 for({j=2; int d = 3; } j+d<7; {j++; d++;}) {
 }

 My first instinct was that that won't compile but it surprisingly does.
 And it loops forever.

 So the grammar according to
 https://dlang.org/spec/grammar.html#ForStatement is:

 ForStatement:
     for ( Initialize Testopt ; Incrementopt ) ScopeStatement

 Initialize:
     ;
     NoScopeNonEmptyStatement

 NoScopeNonEmptyStatement:
     NonEmptyStatement
     BlockStatement

 NonEmptyStatement goes over a bunch of odd places such as case statement
 and default statement. And then BlockStatement is the matched case:

 BlockStatement:
     { }
     { StatementList }

 So it seems we have another case in which "{" "}" do not introduce a
 scope. Fine. The real problem is with the increment part, which is an
 expression. The code { j++; d++; } is... a lambda expression that never
 gets used, which completes a very confusing sample.

 What would be a good solution to forbid certain constructs in the
 increment part of a for statement?
How about in general forbidding lambda statements that aren't called or used anywhere? -Steve
Oct 21 2016
parent reply Kagamin <spam here.lot> writes:
On Friday, 21 October 2016 at 14:16:26 UTC, Steven Schveighoffer 
wrote:
 How about in general forbidding lambda statements that aren't 
 called or used anywhere?
How? int main() { int a; auto b = ()=>{a++;}; b(); assert(a==1); return 0; }
Oct 21 2016
next sibling parent Kagamin <spam here.lot> writes:
http://ideone.com/KBf8k9
Oct 21 2016
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/21/16 10:28 AM, Kagamin wrote:
 On Friday, 21 October 2016 at 14:16:26 UTC, Steven Schveighoffer wrote:
 How about in general forbidding lambda statements that aren't called
 or used anywhere?
How? int main() { int a; auto b = ()=>{a++;}; b(); assert(a==1); return 0; }
This lambda is both used in an assignment, and called. If I do this: 10; It's flagged as not having any effect. Similarly if I do: () => 5; Then it's not used/called. What is the point? -Steve
Oct 21 2016
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/21/16 10:28 AM, Kagamin wrote:
 On Friday, 21 October 2016 at 14:16:26 UTC, Steven Schveighoffer wrote:
 How about in general forbidding lambda statements that aren't called
 or used anywhere?
How? int main() { int a; auto b = ()=>{a++;}; b(); assert(a==1); return 0; }
Oh, I see. This error that I didn't see right away wouldn't be prevented, but that's not what I was talking about. I just meant that the original problem shouldn't have happened, since the lambda is never used. I totally agree that the above sucks and should be fixed. -Steve
Oct 21 2016
prev sibling parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Friday, 21 October 2016 at 12:34:58 UTC, Andrei Alexandrescu 
wrote:
 What would be a good solution to forbid certain constructs in 
 the increment part of a for statement?
For this specific case, a clear solution would be to forbid evaluating lambdas as a boolean expression, because they will always be true, and thus almost always an error. Same as with assignments in if statements. If intended, it can be worked around with "!is null".
Oct 21 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 10/21/2016 12:39 PM, Vladimir Panteleev wrote:
 On Friday, 21 October 2016 at 12:34:58 UTC, Andrei Alexandrescu wrote:
 What would be a good solution to forbid certain constructs in the
 increment part of a for statement?
For this specific case, a clear solution would be to forbid evaluating lambdas as a boolean expression, because they will always be true, and thus almost always an error.
Read the example again, the lambda is not evaluated as a bool. -- Andrei
Oct 21 2016
parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Friday, 21 October 2016 at 16:46:08 UTC, Andrei Alexandrescu 
wrote:
 Read the example again, the lambda is not evaluated as a bool. 
 -- Andrei
My bad. In that case, what Steven said.
Oct 21 2016