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);
}

Reply via email to