digitalmars.D - A possible suggestion for the Foreach loop
- Dylan Knutson (43/43) Aug 20 2013 Hello,
- Tyler Jameson Little (18/62) Aug 20 2013 Why not just do this?
- Tommi (22/45) Aug 21 2013 Why not just do this:
- Dylan Knutson (11/32) Aug 21 2013 Well, that's one way to go about doing it. But, this seems
- monarch_dodra (43/87) Aug 21 2013 This makes sense to me. After all, a static foreach no different
- Kiith-Sa (6/101) Aug 21 2013 In a previous project I needed exactly this (I needed to declare
- monarch_dodra (41/156) Aug 21 2013 I wish I could tell you a template mixin would have done the job,
- John Colvin (49/53) Aug 21 2013 I would *LOVE* to have this.
- Andrei Alexandrescu (7/16) Aug 21 2013 Not necessarily. We could allow "static foreach" as a functional
- Timon Gehr (25/44) Aug 22 2013 AFAIK one only reason why it hasn't made it in yet were implementation
- Timon Gehr (2/5) Aug 22 2013 fixed.
- Dicebot (32/32) Aug 21 2013 While you example can be re-written in a similar fashion with no
- bearophile (5/9) Aug 21 2013 See:
- Dicebot (3/12) Aug 21 2013 This bugzilla entry is on slightly related but different topic.
- bearophile (6/8) Aug 21 2013 I think a well implemented "static foreach" is able to do
- Dicebot (8/17) Aug 21 2013 They are orthogonal and not exclusive.
- bearophile (4/11) Aug 21 2013 They are the same thing, in Issue 4085 the step n.6 covers that.
- Timon Gehr (3/9) Aug 22 2013 I disagree. I think the relationship between foreach and static foreach
- Dylan Knutson (7/16) Aug 21 2013 I do like the idea of it being called 'static foreach' instead of
- Timon Gehr (16/22) Aug 22 2013 It's important to keep them separate regardless. static foreach is close...
Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier. Take, for instance, this hypothetical example: ---------------------------------------------------------------------- T foo(T)(ref T thing) { thing++; return thing * 2; } foreach(Type; TupleType!(int, long, uint)) { unittest { Type tmp = 5; assert(foo(tmp) == 12); } unittest { Type tmp = 0; foo(tmp); assert(tmp == 1); } } ---------------------------------------------------------------------- Without the ability to wrap all of the unittests in a template, one would have to wrap the bodies of each unittest in an individual foreach loop. This is not only repetitive and tedious, but error prone, as changing the types tested then requires the programmer to change *every* instance of the foreach(Type; TupleType). A similar pattern already exists in Phobos, for testing all variants of strings (string, dstring, and wstring) and char types, as eco brought to my attention. After taking a look at some of the unittests that employ this pattern, I'm certain that code clarity and unittest quality could be improved by simply wrapping all of the individual unittests themselves in a foreach as described above. Now, I'm certainly no D expert, but I can't think of any breakages this change might impose on the language itself. So, I'd like to hear what the benevolent overlords and community think of the idea.
Aug 20 2013
On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier. Take, for instance, this hypothetical example: ---------------------------------------------------------------------- T foo(T)(ref T thing) { thing++; return thing * 2; } foreach(Type; TupleType!(int, long, uint)) { unittest { Type tmp = 5; assert(foo(tmp) == 12); } unittest { Type tmp = 0; foo(tmp); assert(tmp == 1); } } ---------------------------------------------------------------------- Without the ability to wrap all of the unittests in a template, one would have to wrap the bodies of each unittest in an individual foreach loop. This is not only repetitive and tedious, but error prone, as changing the types tested then requires the programmer to change *every* instance of the foreach(Type; TupleType). A similar pattern already exists in Phobos, for testing all variants of strings (string, dstring, and wstring) and char types, as eco brought to my attention. After taking a look at some of the unittests that employ this pattern, I'm certain that code clarity and unittest quality could be improved by simply wrapping all of the individual unittests themselves in a foreach as described above. Now, I'm certainly no D expert, but I can't think of any breakages this change might impose on the language itself. So, I'd like to hear what the benevolent overlords and community think of the idea.Why not just do this? T foo(T)(ref T thing) { thing++; return thing * 2; } unittest { void test(T)(T thing, T exp) { assert(foo(thing) == exp); } foreach(Type; TypeTuple!(int, long, uint)) { test!Type(5, 12); test!Type(0, 1); } } Unless you imagine doing this for something other than unittests.
Aug 20 2013
On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:[..] ---------------------------------------------------------------------- T foo(T)(ref T thing) { thing++; return thing * 2; } foreach(Type; TupleType!(int, long, uint)) { unittest { Type tmp = 5; assert(foo(tmp) == 12); } unittest { Type tmp = 0; foo(tmp); assert(tmp == 1); } } ---------------------------------------------------------------------- [..]Why not just do this: import std.typetuple; T foo(T)(ref T thing) { thing++; return thing * 2; } unittest { foreach(Type; TypeTuple!(int, long, uint)) { { Type tmp = 5; assert(foo(tmp) == 12); } { Type tmp = 0; foo(tmp); assert(tmp == 1); } } }
Aug 21 2013
On Wednesday, 21 August 2013 at 10:02:33 UTC, Tommi wrote:Why not just do this: import std.typetuple; T foo(T)(ref T thing) { thing++; return thing * 2; } unittest { foreach(Type; TypeTuple!(int, long, uint)) { { Type tmp = 5; assert(foo(tmp) == 12); } { Type tmp = 0; foo(tmp); assert(tmp == 1); } } }Well, that's one way to go about doing it. But, this seems sub-optimal because a defining trait of unittests is that they're as small and focused on a single behavior for an object/function/whatever as possible. Grouping all testing into a single unittest breaks this convention. Not to mention, we've got 'static if', and 'static assert', which can exist outside of function bodies and operate on compile time determinable values. It seems like a strange exception for foreach (which can already operate on compile time values) to be excluded from this group.
Aug 21 2013
On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier. Take, for instance, this hypothetical example: ---------------------------------------------------------------------- T foo(T)(ref T thing) { thing++; return thing * 2; } foreach(Type; TupleType!(int, long, uint)) { unittest { Type tmp = 5; assert(foo(tmp) == 12); } unittest { Type tmp = 0; foo(tmp); assert(tmp == 1); } } ---------------------------------------------------------------------- Without the ability to wrap all of the unittests in a template, one would have to wrap the bodies of each unittest in an individual foreach loop. This is not only repetitive and tedious, but error prone, as changing the types tested then requires the programmer to change *every* instance of the foreach(Type; TupleType). A similar pattern already exists in Phobos, for testing all variants of strings (string, dstring, and wstring) and char types, as eco brought to my attention. After taking a look at some of the unittests that employ this pattern, I'm certain that code clarity and unittest quality could be improved by simply wrapping all of the individual unittests themselves in a foreach as described above. Now, I'm certainly no D expert, but I can't think of any breakages this change might impose on the language itself. So, I'd like to hear what the benevolent overlords and community think of the idea.This makes sense to me. After all, a static foreach no different in its result from a static if. Here is an example usecase: //---- foreach(T)(TypeTuple!(float, double, real)) { void someFunction(T val) {some_body;} } //---- This, contrary to making someFunction a template, eagerly compiles someFunction. This makes it "ship-able" in a library. Also, it avoid "over instantiations": More often than not, for example, a template will be instantiated with "double", but also "const double" and "immutable double". It also avoids having to over-think the template restraints. This is just one example, but I can *definitly* see it making sense in over ways. ======== Also, I find it strange that the above is not legal, but that this works: //==== import std.stdio, std.typecons; alias cases = TypeTuple!(2, 3, 4, 7, 8); void main() { int i = 7; switch(i) { //cases defined foreach (v; cases) { case v: } { writeln("match"); } break; default: writeln("no match"); } } //====
Aug 21 2013
On Wednesday, 21 August 2013 at 10:40:10 UTC, monarch_dodra wrote:On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:In a previous project I needed exactly this (I needed to declare various class data members based on a large tuple of types.) I ended up having to use string mixins, which was pretty unreadable. So I think it is a good idea, although I have no idea how viable/nonintrusive is it to add this to the language.Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier. Take, for instance, this hypothetical example: ---------------------------------------------------------------------- T foo(T)(ref T thing) { thing++; return thing * 2; } foreach(Type; TupleType!(int, long, uint)) { unittest { Type tmp = 5; assert(foo(tmp) == 12); } unittest { Type tmp = 0; foo(tmp); assert(tmp == 1); } } ---------------------------------------------------------------------- Without the ability to wrap all of the unittests in a template, one would have to wrap the bodies of each unittest in an individual foreach loop. This is not only repetitive and tedious, but error prone, as changing the types tested then requires the programmer to change *every* instance of the foreach(Type; TupleType). A similar pattern already exists in Phobos, for testing all variants of strings (string, dstring, and wstring) and char types, as eco brought to my attention. After taking a look at some of the unittests that employ this pattern, I'm certain that code clarity and unittest quality could be improved by simply wrapping all of the individual unittests themselves in a foreach as described above. Now, I'm certainly no D expert, but I can't think of any breakages this change might impose on the language itself. So, I'd like to hear what the benevolent overlords and community think of the idea.This makes sense to me. After all, a static foreach no different in its result from a static if. Here is an example usecase: //---- foreach(T)(TypeTuple!(float, double, real)) { void someFunction(T val) {some_body;} } //---- This, contrary to making someFunction a template, eagerly compiles someFunction. This makes it "ship-able" in a library. Also, it avoid "over instantiations": More often than not, for example, a template will be instantiated with "double", but also "const double" and "immutable double". It also avoids having to over-think the template restraints. This is just one example, but I can *definitly* see it making sense in over ways. ======== Also, I find it strange that the above is not legal, but that this works: //==== import std.stdio, std.typecons; alias cases = TypeTuple!(2, 3, 4, 7, 8); void main() { int i = 7; switch(i) { //cases defined foreach (v; cases) { case v: } { writeln("match"); } break; default: writeln("no match"); } } //====
Aug 21 2013
On Wednesday, 21 August 2013 at 11:34:29 UTC, Kiith-Sa wrote:On Wednesday, 21 August 2013 at 10:40:10 UTC, monarch_dodra wrote:I wish I could tell you a template mixin would have done the job, but these tend to have trouble once overloads come into play. //---- mixin template someFunctionDeclare(T) { void someFunction(T val) {} } mixin someFunctionDeclare!float; mixin someFunctionDeclare!double; mixin someFunctionDeclare!real; void main() { someFunction(5.5); } //---- main.someFunctionDeclare!double.someFunction at hello.d(7) conflicts with main.someFunctionDeclare!real.someFunction at hello.d(7) main.someFunctionDeclare!double.someFunction at hello.d(7) conflicts with main.someFunctionDeclare!float.someFunction at hello.d(7) //---- That said, now that we have parameterizable enums, and with compile time format and token strings, the syntax to do things with string mixins isn't *that* horrible: enum someFunctionDeclare(T) = format(q{ void someFunction(%1$s val) { writeln(val); } }, T.stringof); mixin(someFunctionDeclare!float); mixin(someFunctionDeclare!double); mixin(someFunctionDeclare!real); void main() { someFunction(5.5); }On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:In a previous project I needed exactly this (I needed to declare various class data members based on a large tuple of types.) I ended up having to use string mixins, which was pretty unreadable. So I think it is a good idea, although I have no idea how viable/nonintrusive is it to add this to the language.Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier. Take, for instance, this hypothetical example: ---------------------------------------------------------------------- T foo(T)(ref T thing) { thing++; return thing * 2; } foreach(Type; TupleType!(int, long, uint)) { unittest { Type tmp = 5; assert(foo(tmp) == 12); } unittest { Type tmp = 0; foo(tmp); assert(tmp == 1); } } ---------------------------------------------------------------------- Without the ability to wrap all of the unittests in a template, one would have to wrap the bodies of each unittest in an individual foreach loop. This is not only repetitive and tedious, but error prone, as changing the types tested then requires the programmer to change *every* instance of the foreach(Type; TupleType). A similar pattern already exists in Phobos, for testing all variants of strings (string, dstring, and wstring) and char types, as eco brought to my attention. After taking a look at some of the unittests that employ this pattern, I'm certain that code clarity and unittest quality could be improved by simply wrapping all of the individual unittests themselves in a foreach as described above. Now, I'm certainly no D expert, but I can't think of any breakages this change might impose on the language itself. So, I'd like to hear what the benevolent overlords and community think of the idea.This makes sense to me. After all, a static foreach no different in its result from a static if. Here is an example usecase: //---- foreach(T)(TypeTuple!(float, double, real)) { void someFunction(T val) {some_body;} } //---- This, contrary to making someFunction a template, eagerly compiles someFunction. This makes it "ship-able" in a library. Also, it avoid "over instantiations": More often than not, for example, a template will be instantiated with "double", but also "const double" and "immutable double". It also avoids having to over-think the template restraints. This is just one example, but I can *definitly* see it making sense in over ways. ======== Also, I find it strange that the above is not legal, but that this works: //==== import std.stdio, std.typecons; alias cases = TypeTuple!(2, 3, 4, 7, 8); void main() { int i = 7; switch(i) { //cases defined foreach (v; cases) { case v: } { writeln("match"); } break; default: writeln("no match"); } } //====
Aug 21 2013
On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies.I would *LOVE* to have this. however, once we go down that route, it would be very tempting to allow a whole lot of other compile-time imperative style programming. Which would be awesome. I don't know how the compiler works with templates, but if it could be made to move linearly through them then we could potentially do this: template allSatisfy(alias F, T...) { foreach(t; T) { static if(!F!t) { static return false; //alternatively: // enum allSatify = False; // static return; } } static return true; } which could be extended to allow static break, static continue etc. This does rather make the argument for allowing "static foreach" as valid, but not required syntax. Or... even allow enum and alias values to be manipulated at CT: template staticMap(alias F, T...) { static if(T.length == 0) { alias staticMap = TypeTuple!(); static return; } alias map = F!(T[0]); foreach(t; T) { map = TypeTuple!(map, F!t); } alias staticMap = map; static return; } I'm favouring the 2-step eponymous thing in order to prevent any alias/enum ambiguities. Another alternative would be static return alias blah; static return enum blah; Apologies for the bike-shedding, but if some thing like this worked properly, it would be a complete game-changer. We'd be light-years ahead of anyone else’s compile-time reflection.
Aug 21 2013
On 8/21/13 4:52 AM, John Colvin wrote:On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:Not necessarily. We could allow "static foreach" as a functional construct that binds in turn a symbol to each element in a collection. A static foreach in conjunction with string mixins would be great for simplifying code generation, e.g. "for each method in that class generate a method here". AndreiHello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies.I would *LOVE* to have this. however, once we go down that route, it would be very tempting to allow a whole lot of other compile-time imperative style programming. Which would be awesome.
Aug 21 2013
On 08/21/2013 07:45 PM, Andrei Alexandrescu wrote:On 8/21/13 4:52 AM, John Colvin wrote:Yup.On Wednesday, 21 August 2013 at 02:46:06 UTC, Dylan Knutson wrote:Not necessarily. We could allow "static foreach" as a functional construct that binds in turn a symbol to each element in a collection. ...Hello, I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies.I would *LOVE* to have this. however, once we go down that route, it would be very tempting to allow a whole lot of other compile-time imperative style programming. Which would be awesome.A static foreach in conjunction with string mixins would be great for simplifying code generation, e.g. "for each method in that class generate a method here".AFAIK one only reason why it hasn't made it in yet were implementation issues related to DMD internals? The design was discussed in 2007's dconf IIRC.AndreiWe really need to define a consistent semantics for compile time symbol manipulation though. Eg: class C{ int a; static foreach(x;__traits(allMembers,C)){ mixin("int "~__traits(identifier,x)~"b;"); } } What is this supposed to do? "class C{ int a,ab; }"? Non-terminating compilation? Error? My best guess is that the above code should be illegal, but there is a lot of similar code already out in the wild that works by chance. Eg. I guess some of Manu's bindings only work by luck. Thrift for D also contains one or two questionable constructs, as David has shown me recently. (It is possible that this is related to the recent breakage, DMD does not necessarily behave consistently w.r.t. to this kind of code across versions.) Most of the times this has been brought up it was simply ignored, but it is a glaring hole in D's design.
Aug 22 2013
On 08/22/2013 02:56 PM, Timon Gehr wrote:AFAIK one reason why it hasn't made it in yet were implementation issues related to DMD internals? The design was discussed in 2007's dconf IIRC.fixed.
Aug 22 2013
While you example can be re-written in a similar fashion with no major issues, it would have been a very useful tool to solve the problem with recursive template instantiation to embed declarations. Compare those two and count template instances: ------------------------------------------- mixin template head(T) { mixin(generateCode!T); } mixin template list(T...) if (T.length >= 1) { mixin head!(T[0]); static if (T.length > 1) mixin list!(T1..$]); } struct Test { mixin list!(int, string, double); } ------------------------------------------- struct Test { foreach (T; TypeTuple!(int, string, double)) { mixin(generateCode!T)); } } ------------------------------------------- I remember Andrei reacting quite positively to this proposal when talking on #d channel.
Aug 21 2013
Dylan Knutson:I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier.See: http://d.puremagic.com/issues/show_bug.cgi?id=4085 Bye, bearophile
Aug 21 2013
On Wednesday, 21 August 2013 at 13:18:22 UTC, bearophile wrote:Dylan Knutson:This bugzilla entry is on slightly related but different topic. "declaration foreach" != "static foreach"I'd like to open up discussion regarding allowing foreach loops which iterate over a tuple of types to exist outside of function bodies. I think this would allow for templating constants and unittests easier.See: http://d.puremagic.com/issues/show_bug.cgi?id=4085 Bye, bearophile
Aug 21 2013
Dicebot:This bugzilla entry is on slightly related but different topic. "declaration foreach" != "static foreach"I think a well implemented "static foreach" is able to do anything a "declaration foreach" could do, and more. (If you don't think so, please show what's missing). Bye, bearophile
Aug 21 2013
On Wednesday, 21 August 2013 at 18:52:56 UTC, bearophile wrote:Dicebot:They are orthogonal and not exclusive. "declaration foreach" can appear whenever declaration can appear and insert new declarations, contrary to statements of normal foreach. "static foreach" is simply an improvement over existing "tuple foreach concept" which allows it to work with a wider variety of input. Former is about context of foreach itself, latter - about behavior of the loop.This bugzilla entry is on slightly related but different topic. "declaration foreach" != "static foreach"I think a well implemented "static foreach" is able to do anything a "declaration foreach" could do, and more. (If you don't think so, please show what's missing). Bye, bearophile
Aug 21 2013
Dicebot:They are orthogonal and not exclusive. "declaration foreach" can appear whenever declaration can appear and insert new declarations, contrary to statements of normal foreach. "static foreach" is simply an improvement over existing "tuple foreach concept" which allows it to work with a wider variety of input. Former is about context of foreach itself, latter - about behavior of the loop.They are the same thing, in Issue 4085 the step n.6 covers that. Bye, bearophile
Aug 21 2013
On 08/21/2013 09:17 PM, Dicebot wrote:They are orthogonal and not exclusive. "declaration foreach" can appear whenever declaration can appear and insert new declarations, contrary to statements of normal foreach. "static foreach" is simply an improvement over existing "tuple foreach concept" which allows it to work with a wider variety of input. Former is about context of foreach itself, latter - about behavior of the loop.I disagree. I think the relationship between foreach and static foreach should essentially mirror that of if and static if.
Aug 22 2013
On Wednesday, 21 August 2013 at 18:52:56 UTC, bearophile wrote:Dicebot:I do like the idea of it being called 'static foreach' instead of 'foreach', to keep in step with how the rest of the language handles other compile time constructs (static assert). Plus, as you said in your bugreport, visual disambiguation between that and a runtime foreach is nice feedback for the programmer to pick up instantly.This bugzilla entry is on slightly related but different topic. "declaration foreach" != "static foreach"I think a well implemented "static foreach" is able to do anything a "declaration foreach" could do, and more. (If you don't think so, please show what's missing). Bye, bearophile
Aug 21 2013
On 08/21/2013 10:53 PM, Dylan Knutson wrote:It's important to keep them separate regardless. static foreach is close to useless if it introduces a new scope for its body. I.e.: int main(){ foreach(_;Seq!int){ int x; } return x; // error } int main(){ static foreach(_;Seq!int){ int x; } return x; // ok }I do like the idea of it being called 'static foreach' instead of 'foreach', to keep in step with how the rest of the language handles other compile time constructs (static assert). Plus, as you said in your bugreport, visual disambiguation between that and a runtime foreach is nice feedback for the programmer to pick up instantly.
Aug 22 2013