www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP 1010--Static foreach--Formal Review

reply Mike Parker <aldacron gmail.com> writes:
As promised, since there has been zero feedback on DIP 1010, "Static 
foreach", in either the Draft or Preliminary review rounds, I'm going to 
skip the normal two-week feedback cycle on the Formal review. If there 
are no major criticisms or objections raised in this thread, then 
sometime on Thursday of this week I'll send Walter & Andrei an email 
kicking off the decision process.

So, if you have any thoughts on the DIP, now is the time to express them.

Thanks!

https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
Jul 10
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
On Monday, 10 July 2017 at 08:53:42 UTC, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, 
 "Static foreach", in either the Draft or Preliminary review 
 rounds, I'm going to skip the normal two-week feedback cycle on 
 the Formal review. If there are no major criticisms or 
 objections raised in this thread, then sometime on Thursday of 
 this week I'll send Walter & Andrei an email kicking off the 
 decision process.

 So, if you have any thoughts on the DIP, now is the time to 
 express them.

 Thanks!

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
I have two somewhat related questions. In the "Generating fields" section, does it have to be a static struct? I see another example with an abstract class with a static for each, but I don't see simpler struct/class examples. I ask this because it seems like static foreach can be used to provide the same functionality as inout, e.g. class Foo { static foreach(T; AliasSeq!(int,const(int),immutable(int))) { void bar(T t) { } } }
Jul 10
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 10.07.2017 20:07, jmh530 wrote:
 On Monday, 10 July 2017 at 08:53:42 UTC, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, "Static 
 foreach", in either the Draft or Preliminary review rounds, I'm going 
 to skip the normal two-week feedback cycle on the Formal review. If 
 there are no major criticisms or objections raised in this thread, 
 then sometime on Thursday of this week I'll send Walter & Andrei an 
 email kicking off the decision process.

 So, if you have any thoughts on the DIP, now is the time to express them.

 Thanks!

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
I have two somewhat related questions. In the "Generating fields" section, does it have to be a static struct?
No. (They are static structs because they are static structs in Vladimir's code.)
 I see another example with an abstract class with a static for each, but 
 I don't see simpler struct/class examples.
 
 I ask this because it seems like static foreach can be used to provide 
 the same functionality as inout, e.g.
 
 class Foo
 {
      static foreach(T; AliasSeq!(int,const(int),immutable(int)))
      {
          void bar(T t)
          {
          }
      }
 }
 
 
Yes, this code works and does what you want. (The difference to inout is that you actually get three different implementations and you are able to vary the implementation based on T.)
Jul 11
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 11 July 2017 at 07:42:22 UTC, Timon Gehr wrote:
 Yes, this code works and does what you want. (The difference to 
 inout is that you actually get three different implementations 
 and you are able to vary the implementation based on T.)
Clearly, this has more power than inout. I suppose what I'm wondering if it makes inout superfluous. For instance, we should be able to add something like template Inout(T) { alias Inout = AliasSeq(T, const(T), immutable(T)); } and use static foreach on it to replicate one of inouts most useful features. The other major use of inout is something like class Foo { void bar(int t) inout { } } which I imagine could be replaced now with SetFunctionAttributes, though my method was a little ugly and I don't know if there's a more elegant approach with static foreach.
Jul 11
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/11/17 10:08 AM, jmh530 wrote:
 On Tuesday, 11 July 2017 at 07:42:22 UTC, Timon Gehr wrote:
 Yes, this code works and does what you want. (The difference to inout 
 is that you actually get three different implementations and you are 
 able to vary the implementation based on T.)
Clearly, this has more power than inout.
It's not as good actually. In the mutable form, you can modify the data. It all depends on if you care about const guarantees or bloat. If you don't and just want to handle all forms, you can use a template (much easier IMO than using static foreach).
 I suppose what I'm wondering if it makes inout superfluous.
I would say no, inout has a very well defined and convenient mechanism to deal with properties on all const flavors of objects. There are many who say templates are good enough, so YMMV. However, nothing so far has successfully implemented the requirement that during the inout function execution, the compiler enforces no mutability.
 The other major use of inout is something like
 
 class Foo
 {
      void bar(int t) inout
hm..., this should probably return inout for it to make sense.
      {
      }
 }
 
 which I imagine could be replaced now with SetFunctionAttributes, though 
 my method was a little ugly and I don't know if there's a more elegant 
 approach with static foreach.
There is a way to do it with 'this' template parameter: int * member; auto bar(this T)() { return member; // returns const, immutable, or mutable depending on modifier for 'this' } Still is bloated into multiple instantiations, and still allowed to mutate for mutable call. Plus it's confusing. -Steve
Jul 11
parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 11 July 2017 at 14:23:27 UTC, Steven Schveighoffer 
wrote:
 It's not as good actually. In the mutable form, you can modify 
 the data.
[snip] Clears things up. Thanks.
Jul 11
prev sibling next sibling parent reply Daniel N <no public.email> writes:
On Monday, 10 July 2017 at 08:53:42 UTC, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, 
 "Static foreach", in either the Draft or Preliminary review 
 rounds, I'm going to skip the normal two-week feedback cycle on 
 the Formal review. If there are no major criticisms or 
 objections raised in this thread, then sometime on Thursday of 
 this week I'll send Walter & Andrei an email kicking off the 
 decision process.

 So, if you have any thoughts on the DIP, now is the time to 
 express them.

 Thanks!

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
how is __local handled with nested static foreach?
Jul 11
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11.07.2017 09:50, Daniel N wrote:
 On Monday, 10 July 2017 at 08:53:42 UTC, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, "Static 
 foreach", in either the Draft or Preliminary review rounds, I'm going 
 to skip the normal two-week feedback cycle on the Formal review. If 
 there are no major criticisms or objections raised in this thread, 
 then sometime on Thursday of this week I'll send Walter & Andrei an 
 email kicking off the decision process.

 So, if you have any thoughts on the DIP, now is the time to express them.

 Thanks!

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
how is __local handled with nested static foreach?
The following code works: --- static foreach(i;0..4){ static if(i) __local alias outerPrevious = __previous; static foreach(j;0..4){ static if(i&&j){ pragma(msg, outerPrevious.i," ",__previous.j); } } } --- It prints: --- 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 --- (However, note that DIP 1010 does not propose adding __local to the language.)
Jul 12
prev sibling next sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Monday, 10 July 2017 at 08:53:42 UTC, Mike Parker wrote:
 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
Sorry for not getting in on the original review, but I've submitted a PR for the DIP in regards to formatting.
Jul 11
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/10/17 4:53 AM, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, "Static 
 foreach", in either the Draft or Preliminary review rounds, I'm going to 
 skip the normal two-week feedback cycle on the Formal review. If there 
 are no major criticisms or objections raised in this thread, then 
 sometime on Thursday of this week I'll send Walter & Andrei an email 
 kicking off the decision process.
 
 So, if you have any thoughts on the DIP, now is the time to express them.
 
 Thanks!
 
 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
 
A few things: 1. I can't wait for this to get in. I'm anticipating success :) 2. I see no point in having foreach(enum i; 0.. 3) when static foreach(i; 0 .. 3) works just fine? 3. The only controversial part I see is that `break` doesn't break from the foreach loop. While I agree with the reasoning, and support that concept, the truth is we currently have a "poor man's" static foreach using a foreach over a tuple, and that DOES break from the loop. For instance: size_t idx; switch(someval) { case something: foreach(v; AliasSeq!(1, 2, 3)) { if(shouldBreak(v)) break; // today, this jumps to moreProcessing() line } moreProcessing(); break; } If I replaced foreach with static foreach(v; 1 .. 4), it now breaks out of the switch? As much as I would rather see the proposed behavior, I feel it's too confusing given the existing foreach behavior. I think in this case, you have to require a break label. A possible deprecation path: 1. In the case where "static foreach" is inside another construct that allows breaking, require a break label. However, foreach over a tuple would continue to exhibit today's behavior. 2. print warning message for foreach over a tuple that contains a break without a label. Warn users that a future version will not allow breaking from the foreach. 3. disallow breaking out of the foreach directly (i.e. jumping to the closing brace of the loop), even if there is a label (you can surround foreach with a do{} while(false) if you need this behavior). 4. Remove the requirement for labeling. Both static foreach and foreach on a tuple do not break from the loop construct. -Steve
Jul 11
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Jul 11, 2017 at 07:18:51PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 3. The only controversial part I see is that `break` doesn't break
 from the foreach loop. While I agree with the reasoning, and support
 that concept, the truth is we currently have a "poor man's" static
 foreach using a foreach over a tuple, and that DOES break from the
 loop.
This is a false impression. It actually does not break from the loop, but inserts a break in the generated code, and continues to unroll the rest of the loop. It's only at codegen that the subsequent iterations are detected as dead code and elided. See: https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#.22static.22_foreach_does_NOT_interpret_break_and_continue T -- Любишь кататься - люби и саночки возить.
Jul 11
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/11/17 7:21 PM, H. S. Teoh via Digitalmars-d wrote:
 On Tue, Jul 11, 2017 at 07:18:51PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
 [...]
 3. The only controversial part I see is that `break` doesn't break
 from the foreach loop. While I agree with the reasoning, and support
 that concept, the truth is we currently have a "poor man's" static
 foreach using a foreach over a tuple, and that DOES break from the
 loop.
This is a false impression. It actually does not break from the loop, but inserts a break in the generated code, and continues to unroll the rest of the loop. It's only at codegen that the subsequent iterations are detected as dead code and elided. See: https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#.22static.22_foreach_does_NOT_interpret_break_and_continue
Yes, I know that it still generates all the code, but the break is still interpreted as breaking out of the loop. Timon's proposal says it "does not interact with break", so I interpret that as meaning it should break out of whatever construct is surrounding the loop, not the loop itself. Currently this: foreach(j; 0 .. 2) foreach(i; AliasSeq!(0, 1)) { writeln(i); static if(i == 0) break; } will print 0 0 Whereas with my understanding, this: foreach(j; 0 .. 2) static foreach(i; 0 .. 2) { writeln(i); static if(i == 0) break; } would print 0 This seems too confusing. -Steve
Jul 11
parent John Colvin <john.loughran.colvin gmail.com> writes:
On Tuesday, 11 July 2017 at 23:50:26 UTC, Steven Schveighoffer 
wrote:
 On 7/11/17 7:21 PM, H. S. Teoh via Digitalmars-d wrote:
 On Tue, Jul 11, 2017 at 07:18:51PM -0400, Steven Schveighoffer 
 via Digitalmars-d wrote:
 [...]
 3. The only controversial part I see is that `break` doesn't 
 break
 from the foreach loop. While I agree with the reasoning, and 
 support
 that concept, the truth is we currently have a "poor man's" 
 static
 foreach using a foreach over a tuple, and that DOES break 
 from the
 loop.
This is a false impression. It actually does not break from the loop, but inserts a break in the generated code, and continues to unroll the rest of the loop. It's only at codegen that the subsequent iterations are detected as dead code and elided. See: https://wiki.dlang.org/User:Quickfur/Compile-time_vs._compile-time#.22static.22_foreach_does_NOT_interpret_break_and_continue
Yes, I know that it still generates all the code, but the break is still interpreted as breaking out of the loop. Timon's proposal says it "does not interact with break", so I interpret that as meaning it should break out of whatever construct is surrounding the loop, not the loop itself. Currently this: foreach(j; 0 .. 2) foreach(i; AliasSeq!(0, 1)) { writeln(i); static if(i == 0) break; } will print 0 0 Whereas with my understanding, this: foreach(j; 0 .. 2) static foreach(i; 0 .. 2) { writeln(i); static if(i == 0) break; } would print 0 This seems too confusing. -Steve
break inside a case inside a static foreach inside a switch is an interesting case for this sort of reasoning
Jul 12
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12.07.2017 01:18, Steven Schveighoffer wrote:
 On 7/10/17 4:53 AM, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, "Static 
 foreach", in either the Draft or Preliminary review rounds, I'm going 
 to skip the normal two-week feedback cycle on the Formal review. If 
 there are no major criticisms or objections raised in this thread, 
 then sometime on Thursday of this week I'll send Walter & Andrei an 
 email kicking off the decision process.

 So, if you have any thoughts on the DIP, now is the time to express them.

 Thanks!

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
A few things: 1. I can't wait for this to get in. I'm anticipating success :) 2. I see no point in having foreach(enum i; 0.. 3) when static foreach(i; 0 .. 3) works just fine? ...
foreach(enum i; 0..3) does not currently compile and is not proposed in this DIP, but it would be useful for the case where you need an unrolled foreach loop.
 3. The only controversial part I see is that `break` doesn't break from 
 the foreach loop. While I agree with the reasoning, and support that 
 concept, the truth is we currently have a "poor man's" static foreach 
 using a foreach over a tuple, and that DOES break from the loop.
 
 For instance:
 
 size_t idx;
 switch(someval)
 {
     case something:
        foreach(v; AliasSeq!(1, 2, 3))
        {
            if(shouldBreak(v))
                break; // today, this jumps to moreProcessing() line
        }
        moreProcessing();
        break;
 }
 
 If I replaced foreach with static foreach(v; 1 .. 4), it now breaks out 
 of the switch?
 ...
Yes.
 As much as I would rather see the proposed behavior, I feel it's too 
 confusing given the existing foreach behavior. I think in this case, you 
 have to require a break label.
 
 A possible deprecation path:
 
 1. In the case where "static foreach" is inside another construct that 
 allows breaking, require a break label. However, foreach over a tuple 
 would continue to exhibit today's behavior.
 2. print warning message for foreach over a tuple that contains a break 
 without a label. Warn users that a future version will not allow 
 breaking from the foreach.
 3. disallow breaking out of the foreach directly (i.e. jumping to the 
 closing brace of the loop), even if there is a label (you can surround 
 foreach with a do{} while(false) if you need this behavior).
 4. Remove the requirement for labeling. Both static foreach and foreach 
 on a tuple do not break from the loop construct.
 
 -Steve
I don't see why for foreach, break behaviour should depend on the aggregate, that is much more confusing. static foreach is not a loop with a loop body, it generates multiple versions of the given code. There is no reason why static foreach and foreach should have "the same" (whatever that even means!) behaviour with respect to runtime break, and if it is really considered too confusing, static foreach should just always require an explicit label. (But this is painful for me to implement, as it is unnatural.)
Jul 12
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/12/17 4:23 AM, Timon Gehr wrote:
 On 12.07.2017 01:18, Steven Schveighoffer wrote:
 On 7/10/17 4:53 AM, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, "Static 
 foreach", in either the Draft or Preliminary review rounds, I'm going 
 to skip the normal two-week feedback cycle on the Formal review. If 
 there are no major criticisms or objections raised in this thread, 
 then sometime on Thursday of this week I'll send Walter & Andrei an 
 email kicking off the decision process.

 So, if you have any thoughts on the DIP, now is the time to express 
 them.

 Thanks!

 https://github.com/dlang/DIPs/blob/master/DIPs/DIP1010.md
A few things: 1. I can't wait for this to get in. I'm anticipating success :) 2. I see no point in having foreach(enum i; 0.. 3) when static foreach(i; 0 .. 3) works just fine? ...
foreach(enum i; 0..3) does not currently compile and is not proposed in this DIP, but it would be useful for the case where you need an unrolled foreach loop.
It's in the DIP, regardless of whether it's critical to the acceptance of the DIP. As it's something on the table for W&A to look at, I wanted to voice my opinion on it.
 I don't see why for foreach, break behaviour should depend on the 
 aggregate, that is much more confusing.
Perhaps. My point of view is that I'm generally using foreach over a tuple to generate switch cases. I always think that the break is going to apply to the switch statement, and it gets me every time. In fact, in writing this post, I just found a (harmless?) bug in my code: switch(str) { foreach(n; names) { case n: ... break; } default: break; } It's harmless because the default case does nothing. But it certainly isn't what I should have written! I think of foreach over a tuple as separate from foreach over a runtime type. To me, it's already a different syntax and more akin to static foreach. One may argue that static foreach looks just like foreach, and so all of them should behave the same.
 static foreach is not a loop 
 with a loop body, it generates multiple versions of the given code. 
 There is no reason why static foreach and foreach should have "the same" 
 (whatever that even means!) behaviour with respect to runtime break, and 
 if it is really considered too confusing, static foreach should just 
 always require an explicit label. (But this is painful for me to 
 implement, as it is unnatural.)
From a user perspective, whether I reach for one tool or the other, the behavior shouldn't be subtly different. If I'm looking for a tool to unroll loops, I can use foreach with a tuple, or static foreach. Both should behave the same. Perhaps the deprecation path should include a removal of straight foreach over a tuple working (use static foreach explicitly). This would make the distinction even more obvious. -Steve
Jul 12
parent Adrian Matoga <dlang.spam matoga.info> writes:
On Wednesday, 12 July 2017 at 10:57:37 UTC, Steven Schveighoffer 
wrote:
 Perhaps the deprecation path should include a removal of 
 straight foreach over a tuple working (use static foreach 
 explicitly). This would make the distinction even more obvious.
I'd also vote for gradual removal of foreach over a tuple. It would be one less awkward moment when teaching D.
Jul 12
prev sibling parent Mike Parker <aldacron gmail.com> writes:
On Monday, 10 July 2017 at 08:53:42 UTC, Mike Parker wrote:
 As promised, since there has been zero feedback on DIP 1010, 
 "Static foreach", in either the Draft or Preliminary review 
 rounds, I'm going to skip the normal two-week feedback cycle on 
 the Formal review. If there are no major criticisms or 
 objections raised in this thread, then sometime on Thursday of 
 this week I'll send Walter & Andrei an email kicking off the 
 decision process.
Thanks for all the feedback. I don't see anything that is a blocker from moving forward, so I'll cut it off here and move along.
Jul 13