www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP 57: static foreach

reply Timon Gehr <timon.gehr gmx.ch> writes:
http://wiki.dlang.org/DIP57/

Thoughts?
Mar 09 2014
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/09/2014 10:31 PM, Timon Gehr wrote:
 http://wiki.dlang.org/DIP57/

 Thoughts?

broken link. http://wiki.dlang.org/DIP57
Mar 09 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Timon Gehr:

 http://wiki.dlang.org/DIP57

 With these semantics, tuple foreach, eg:
 
 void main(){
     foreach(x;Seq!(1,2)){ ... }
 }
 
 is equivalent to static foreach with an additional pair of 
 curly brackets, eg:
 
 void main(){
     static foreach(x;Seq!(1,2)){ { ... } }
 }

Having three kinds of foreach in a language is too much. So I suggest to add to DIP57 one more thing: that the introduction of static foreach should come with a warning against the usage of not-static foreach on tuples (and eventually this warning should become a deprecation message). Bye, bearophile
Mar 09 2014
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 9 March 2014 at 21:47:17 UTC, bearophile wrote:
 suggest to add to DIP57 one more thing: that the introduction 
 of static foreach should come with a warning against the usage 
 of not-static foreach on tuples (and eventually this warning 
 should become a deprecation message).

I don't agree because foreach on a tuple is just plain foreach. That it unrolls is just an implementation detail that doesn't change much else. I think considering it to be a separate kind of loop is like considering foreach over arrays, ranges, and opApply items separate loops. Those are just different implementation details of the same user concept.
Mar 09 2014
prev sibling next sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Sunday, 9 March 2014 at 21:46:59 UTC, Andrej Mitrovic wrote:
 On 3/9/14, Timon Gehr <timon.gehr gmx.ch> wrote:
 On 03/09/2014 10:31 PM, Timon Gehr wrote:
 http://wiki.dlang.org/DIP57/

 Thoughts?

broken link. http://wiki.dlang.org/DIP57

Off-topic but this isn't the first time I ended up at the wrong page due to trailing "/". Can we fix this for the DWiki?

I could add an Apache rewrite rule to strip trailing slashes. I've done something similar for unbalanced parens, since trailing closing parens are usually not considered as a part of URL by URL-matching algorithms. An alternative approach is to just create a redirect page. I've heard these are cheap, as far as database resources are concerned.
Mar 09 2014
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 I don't agree because foreach on a tuple is just plain foreach. 
 That it unrolls is just an implementation detail that doesn't 
 change much else.

How can it be an implementation detail if the compiler accepts code like this? You can't do anything like this with a dynamic foreach, it must to be unrolled at compile-time: import std.typetuple, std.string; void main() { foreach (x; TypeTuple!(0, 1, 2)) { mixin(format("int x%d;", x)); } } Bye, bearophile
Mar 09 2014
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 9 March 2014 at 23:14:06 UTC, bearophile wrote:
 How can it be an implementation detail if the compiler accepts 
 code like this?

That's really just a CTFE limitation, not an entirely different kind of loop. If ctfe carried across just a little bit more, this could work too: foreach(immutable a; [1,2,3]) mixin(a); The major, fundamental difference between static foreach as proposed and foreach is: * static foreach does not introduce a new scope. If you take your code and try to access x0 outside the loop, it will fail. With a static foreach, that would be allowed (just like how static if can declare variables) * static foreach would be valid at module level and inside templates (like static if), whereas regular foreach is only permitted in a function (like regular if).
 You can't do anything like this with a dynamic foreach, it must 
 to be unrolled at compile-time:

Note that the optimizer is allowed to unroll any loop it desires. You can have local variables in a regular runtime loop too, but you can't refer to them outside, so the unrolling here is a code generator implementation detail rather than a fundamental difference in the language.
Mar 09 2014
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 Note that the optimizer is allowed to unroll any loop it 
 desires. You can have local variables in a regular runtime loop 
 too, but you can't refer to them outside, so the unrolling here 
 is a code generator implementation detail rather than a 
 fundamental difference in the language.

OK. So if this DIP gets accepted I will close down my issue 4085, because it's obsolete. All your extra explanations could be useful for future (probable) questions in D.learn. Bye, bearophile
Mar 09 2014
prev sibling parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 9 March 2014 at 21:53:45 UTC, Adam D. Ruppe wrote:
 On Sunday, 9 March 2014 at 21:47:17 UTC, bearophile wrote:
 suggest to add to DIP57 one more thing: that the introduction 
 of static foreach should come with a warning against the usage 
 of not-static foreach on tuples (and eventually this warning 
 should become a deprecation message).

I don't agree because foreach on a tuple is just plain foreach. That it unrolls is just an implementation detail that doesn't change much else. I think considering it to be a separate kind of loop is like considering foreach over arrays, ranges, and opApply items separate loops. Those are just different implementation details of the same user concept.

Can't agree. You can't call it implementation detail if it is a property that leaks into user code and can be relied upon. I sometimes hear statements akin to "tuple is like container and tuple foreach is just like foreach" but it is a very idealistic view that simply does not match current D state. Despite all behavior hacks that try to make it look so. So right now it _is_ a separate and distinctive kind of loop. At the same time it is a very specialized tool and deprecating it does not sound like a practical approach for reducing language complexity. Probably some years later if we eventually find out no one uses it anymore.
Mar 10 2014
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/9/14, Timon Gehr <timon.gehr gmx.ch> wrote:
 On 03/09/2014 10:31 PM, Timon Gehr wrote:
 http://wiki.dlang.org/DIP57/

 Thoughts?

broken link. http://wiki.dlang.org/DIP57

Off-topic but this isn't the first time I ended up at the wrong page due to trailing "/". Can we fix this for the DWiki?
Mar 09 2014
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 9 March 2014 at 21:31:40 UTC, Timon Gehr wrote:
 http://wiki.dlang.org/DIP57/

 Thoughts?

I like it.
Mar 09 2014
prev sibling next sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
--f46d043c814c82752204f43adf0f
Content-Type: text/plain; charset=UTF-8

2014-03-10 6:31 GMT+09:00 Timon Gehr <timon.gehr gmx.ch>:

 http://wiki.dlang.org/DIP57

 Thoughts?

From the "Semantics" section:

 For static foreach statements, break and continue are supported and

This is questionable sentence. On the foreach with tuple iteration, break and continue have no effect for the unrolling. void main() { import std.typetuple, std.stdio; foreach (i; TypeTuple!(1, 2, 3)) { static if (i == 2) continue; else static if (i == 3) break; pragma(msg, "CT: i = ", i); // prints 1, 2, and 3 in CT writeln("RT: i = ", i); // prints only 1 in RT } } So, I think that static foreach *cannot* support break and continue as same as foreach with tuples. Kenji Hara --f46d043c814c82752204f43adf0f Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr"><div class=3D"gmail_extra"><div class=3D"gmail_quote">2014= -03-10 6:31 GMT+09:00 Timon Gehr <span dir=3D"ltr">&lt;<a href=3D"mailto:ti= mon.gehr gmx.ch" target=3D"_blank">timon.gehr gmx.ch</a>&gt;</span>:<br><bl= ockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1px #= ccc solid;padding-left:1ex"> <a href=3D"http://wiki.dlang.org/DIP57/" target=3D"_blank">http://wiki.dlan= g.org/DIP57</a><br> <br> Thoughts?<br> </blockquote></div><br></div><div class=3D"gmail_extra">From the &quot;Sema= ntics&quot; section:<br></div><div class=3D"gmail_extra"><div class=3D"gmai= l_extra"><br></div><div class=3D"gmail_extra">&gt; For static foreach state= ments, break and continue are supported and treated like for foreach statem= ents over tuples.</div> <div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">This is que= stionable sentence. On the foreach with tuple iteration, break and continue= have no effect for the unrolling.</div><div class=3D"gmail_extra"><br></di= v> <div class=3D"gmail_extra">void main()</div><div class=3D"gmail_extra">{</d= iv><div class=3D"gmail_extra">=C2=A0 =C2=A0 import std.typetuple, std.stdio= ;</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">=C2= =A0 =C2=A0 foreach (i; TypeTuple!(1, 2, 3))</div> <div class=3D"gmail_extra">=C2=A0 =C2=A0 {</div><div class=3D"gmail_extra">= =C2=A0 =C2=A0 =C2=A0 =C2=A0 static if (i =3D=3D 2) continue;</div><div clas= s=3D"gmail_extra">=C2=A0 =C2=A0 =C2=A0 =C2=A0 else static if (i =3D=3D 3) b= reak;</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra"> =C2=A0 =C2=A0 =C2=A0 =C2=A0 pragma(msg, &quot;CT: i =3D &quot;, i); // prin= ts 1, 2, and 3 in CT</div><div class=3D"gmail_extra">=C2=A0 =C2=A0 =C2=A0 = =C2=A0 writeln(&quot;RT: i =3D &quot;, i); =C2=A0 =C2=A0 // prints only 1 i= n RT</div><div class=3D"gmail_extra">=C2=A0 =C2=A0 }</div><div class=3D"gma= il_extra"> }</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">So, = I think that static foreach *cannot* support break and continue as same as = foreach with tuples.</div><div class=3D"gmail_extra"><br></div><div class= =3D"gmail_extra"> Kenji Hara</div><div><br></div></div></div> --f46d043c814c82752204f43adf0f--
Mar 09 2014
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/10/2014 07:40 AM, Kenji Hara wrote:
 2014-03-10 6:31 GMT+09:00 Timon Gehr <timon.gehr gmx.ch
 <mailto:timon.gehr gmx.ch>>:

     http://wiki.dlang.org/DIP57 <http://wiki.dlang.org/DIP57/>

     Thoughts?


  From the "Semantics" section:

  > For static foreach statements, break and continue are supported and
 treated like for foreach statements over tuples.

 This is questionable sentence. On the foreach with tuple iteration,
 break and continue have no effect for the unrolling.
 ...

That's what is meant, and indeed this is visible in the examples section.
 void main()
 {
      import std.typetuple, std.stdio;

      foreach (i; TypeTuple!(1, 2, 3))
      {
          static if (i == 2) continue;
          else static if (i == 3) break;

          pragma(msg, "CT: i = ", i); // prints 1, 2, and 3 in CT
          writeln("RT: i = ", i);     // prints only 1 in RT
      }
 }

 So, I think that static foreach *cannot* support break and continue as
 same as foreach with tuples.

 Kenji Hara

Yes it can. What is your suggestion? Influencing the unrolling?
Mar 10 2014
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Sunday, 9 March 2014 at 21:31:40 UTC, Timon Gehr wrote:
 http://wiki.dlang.org/DIP57/

 Thoughts?

1)
 Additionally, CTFE is invoked on all expressions occurring in 
 the ForeachAggregate

I think it can be phrased more universally "ForeachTypeList symbols must be evaluated as compile-time entities, if it is not possible, implementation-defined compilation error happens". 2) Saying that it does not introduce a new scope is not entirely true as symbols from ForeachTypeList should not be available outside of static foreach. You mention it later in the same block but it is important concept to define as we currently don't have such pseudo-scopes (do we?) 3)
 The body of the static foreach statement or static foreach 
 declaration is duplicated once for each iteration which the 
 corresponding foreach statement with an empty body would 
 perform when executed in CTFE

I don't understand the reason behind limiting static foreach to CTFE semantics. Simply evaluating and pasting the body for each iteration should be enough. It is much closer to mixin template instances in that regard. This will also remove necessity to rely on shadowing rules to re-define ForeachTypeList symbols as at the time of pasting the body those won't exist anymore. 4)
  Declarations introduced in the body itself are inserted into 
 this enclosing scope

Isn't "enclosing" term used only for scope-to-scope relations or it is applicable to any language construct? (I don't know) 5)
 For static foreach statements, break and continue are supported 
 and treated like for foreach statements over tuples.

It is impossible as far as I understand existing semantics. Currently placed continue/break refer to created scope and don't stop iteration over remaining template argument list members. This is not applicable to generic foreach. 6) In "Iterating over members of a scope" example there is a strange Python-like colon after `static if` condition. Typo? :) 7) In "Relation to tuple foreach" stating equivalency is not correct. It is more of subset and even not a strict one as semantics will differ in some corner cases. For example, iterating over expression list will create a local copy right now if `ref` is not used. I'd really want this to not be the case for static foreach. Overall provided examples seem to much my expectations but semantics description can be more structured and detailed.
Mar 10 2014
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/10/2014 02:08 PM, Dicebot wrote:
 On Sunday, 9 March 2014 at 21:31:40 UTC, Timon Gehr wrote:
 http://wiki.dlang.org/DIP57/

 Thoughts?

1)
 Additionally, CTFE is invoked on all expressions occurring in the
 ForeachAggregate

I think it can be phrased more universally "ForeachTypeList symbols must be evaluated as compile-time entities, if it is not possible, implementation-defined compilation error happens". ...

I don't see how this is more universal.
 2) Saying that it does not introduce a new scope is not entirely true as
 symbols from ForeachTypeList should not be available outside of static
 foreach. You mention it later in the same block but it is important
 concept to define as we currently don't have such pseudo-scopes
 ...

The description only says that the usual scope for foreach statements is not introduced.
 (do we?)
 ...

Nope.
 3)
 The body of the static foreach statement or static foreach declaration
 is duplicated once for each iteration which the corresponding foreach
 statement with an empty body would perform when executed in CTFE

I don't understand the reason behind limiting static foreach to CTFE semantics. Simply evaluating and pasting the body for each iteration should be enough. It is much closer to mixin template instances in that regard. ...

I don't understand how the DIP is 'limiting static foreach to CTFE semantics' and/or why this is a bad thing or how your suggestion is different.
 This will also remove necessity to rely on shadowing rules to re-define
 ForeachTypeList symbols as at the time of pasting the body those won't
 exist anymore.
 ...

I have no idea what this means.
 4)

  Declarations introduced in the body itself are inserted into this
 enclosing scope

Isn't "enclosing" term used only for scope-to-scope relations or it is applicable to any language construct? (I don't know) ...

There is no formal language spec. What is meant is the scope `hosting' the static foreach construct.
 5)

 For static foreach statements, break and continue are supported and
 treated like for foreach statements over tuples.

It is impossible as far as I understand existing semantics. Currently placed continue/break refer to created scope and don't stop iteration over remaining template argument list members. This is not applicable to generic foreach. ...

This is not 'impossible', it is trivial to implement. Is your point that you would prefer break and continue to affect static foreach expansion?
 6)

 In "Iterating over members of a scope" example there is a strange
 Python-like colon after `static if` condition. Typo? :)
 ...

Nope. This is a language feature. See: http://dlang.org/version.html
 7)

 In "Relation to tuple foreach" stating equivalency is not correct.

I have removed the section.
 It is more of subset and even not a strict one as semantics will differ in
 some corner cases.

I think as described they would not need to.
 For example, iterating over expression list will
 create a local copy right now if `ref` is not used. I'd really want this
 to not be the case for static foreach.
 ...

I think the description is actually not detailed enough to warrant this critique. (In particular, it is not clear what 'ref' should do.) I.e., I think currently the following code is ambiguous: int y,z; static foreach(x;Seq!(y,z)) x = 2; // what is the value of y and z now?
 Overall provided examples seem to much my expectations but semantics
 description can be more structured and detailed.

Agreed. I will do another iteration when I can find the time. Maybe I will have to re-specify the behaviour of foreach though.
Mar 10 2014
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/10/14, Kenji Hara <k.hara.pg gmail.com> wrote:
 This is questionable sentence. On the foreach with tuple iteration, break
 and continue have no effect for the unrolling.

Whatever is implemented, we need to make sure the current code is possible. in std.conv.to: ----- switch(value) { foreach (I, member; NoDuplicates!(EnumMembers!S)) { case member: return to!T(enumRep!(immutable(T), S, I)); } default: } -----
Mar 10 2014
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Monday, 10 March 2014 at 15:38:09 UTC, Timon Gehr wrote:
 I think it can be phrased more universally "ForeachTypeList 
 symbols must
 be evaluated as compile-time entities, if it is not possible,
 implementation-defined compilation error happens".
 ...

I don't see how this is more universal.

CTFE stands for "Compile-Time Function Evaluation". When you don't have functions or expressions (i.e. pure type list) referring to it looks very confusing to me and I start guessing if you mean some specific CTFE semantics I don't know about or it is just a wording for "accessible during compile-time". Also spec does not say that const-folding happens via CTFE as a rule, thus you are exposing implementation details that way. Looks like your intentions here are same as mine but exact phrasing creates misunderstanding :)
 5)

 For static foreach statements, break and continue are 
 supported and
 treated like for foreach statements over tuples.

It is impossible as far as I understand existing semantics. Currently placed continue/break refer to created scope and don't stop iteration over remaining template argument list members. This is not applicable to generic foreach. ...

This is not 'impossible', it is trivial to implement. Is your point that you would prefer break and continue to affect static foreach expansion?

Impossible in a sense that those are not applicable in declaration context. Also break/continue do look weird in absence of scope, I don't like it. Would prefer those to either affect static foreach expansion or refer to outer loop (compilation error if there is none).
 6)

 In "Iterating over members of a scope" example there is a 
 strange
 Python-like colon after `static if` condition. Typo? :)
 ...

Nope. This is a language feature. See: http://dlang.org/version.html

What a horrible creation! :O It is first time I see such static if syntax in actual D code. Why was it introduced?
 I think the description is actually not detailed enough to 
 warrant this critique. (In particular, it is not clear what 
 'ref' should do.)

 I.e., I think currently the following code is ambiguous:

 int y,z;
 static foreach(x;Seq!(y,z)) x = 2;
 // what is the value of y and z now?

With existing foreach y and z will remain int.init If you change it to be `foreach(ref x; Seq..`, y and z will change to 2. For static foreach I want it to be 2 and `ref x` rejected at compilation time as nonsense.
Mar 11 2014
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Timon Geh:

 http://wiki.dlang.org/DIP57/

 Thoughts?

Is it good to support this syntax too? static foreach (enum i; 0 .. 10) {} Bye, bearophile
Mar 26 2014
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/26/2014 01:33 PM, bearophile wrote:
 Timon Geh:

 http://wiki.dlang.org/DIP57/

 Thoughts?

Is it good to support this syntax too? static foreach (enum i; 0 .. 10) {} Bye, bearophile

Yes, I think so. I'll try to finish the DIP this weekend.
Mar 26 2014