A possible suggestion for the Foreach loop

monarch_dodra monarchdodra at gmail.com
Wed Aug 21 05:14:53 PDT 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:
>> 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);
}


More information about the Digitalmars-d mailing list