Thread overview | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
August 21, 2013 A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
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. |
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dylan Knutson | 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.
|
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dylan Knutson | 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);
}
}
}
|
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dylan Knutson | 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");
}
}
//====
|
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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.
|
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dylan Knutson | 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.
|
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Kiith-Sa | 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); } |
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dylan Knutson | 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. |
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dylan Knutson | 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 |
August 21, 2013 Re: A possible suggestion for the Foreach loop | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | 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"
|
Copyright © 1999-2021 by the D Language Foundation