www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A possible suggestion for the Foreach loop

reply "Dylan Knutson" <tcdknutson gmail.com> writes:
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
next sibling parent "Tyler Jameson Little" <beatgammit gmail.com> writes:
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
prev sibling next sibling parent reply "Tommi" <tommitissari hotmail.com> writes:
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
parent "Dylan Knutson" <tcdknutson gmail.com> writes:
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
prev sibling next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent reply "Kiith-Sa" <kiithsacmp gmail.com> writes:
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:
 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"); } } //====
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.
Aug 21 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
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:
 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"); } } //====
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.
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); }
Aug 21 2013
prev sibling next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/21/13 4:52 AM, John Colvin wrote:
 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.
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". Andrei
Aug 21 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/21/2013 07:45 PM, Andrei Alexandrescu wrote:
 On 8/21/13 4:52 AM, John Colvin wrote:
 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.
Not necessarily. We could allow "static foreach" as a functional construct that binds in turn a symbol to each element in a collection. ...
Yup.
 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.
 Andrei
We 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
parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
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
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
parent reply "Dicebot" <public dicebot.lv> writes:
On Wednesday, 21 August 2013 at 13:18:22 UTC, bearophile wrote:
 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
This bugzilla entry is on slightly related but different topic. "declaration foreach" != "static foreach"
Aug 21 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
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
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Wednesday, 21 August 2013 at 18:52:56 UTC, bearophile wrote:
 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
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.
Aug 21 2013
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
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
prev sibling parent reply "Dylan Knutson" <tcdknutson gmail.com> writes:
On Wednesday, 21 August 2013 at 18:52:56 UTC, bearophile wrote:
 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
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 21 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/21/2013 10:53 PM, Dylan Knutson wrote:

 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.
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 }
Aug 22 2013