www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - most non-function templates could be advantageously replaced by

reply Timothee Cour <thelastmammoth gmail.com> writes:
Phobos has a number of *non-function template* traits (std.traits,
std.typetuple etc), eg:

---- in std.traits:
template hasMember(T, string name) {
    static if (is(T == struct) || is(T == class) || is(T == union) || is(T
== interface))
        enum bool hasMember =
            staticIndexOf!(name, __traits(allMembers, T)) != -1 ||
            __traits(compiles, { mixin("alias Identity!(T."~name~") Sym;");
});
    else
        enum bool hasMember = false;
}
----

I see those *disadvantages* with non-function templates:

* syntax is less clear than regular function templates (there's no way to
tell the return type without looking at the code or using some convention
relating to the template name)

* they're not DRY, as the template name is typically repeated (maybe in
multiple places) inside the template body (hasMember appears 3 times here)

* behavior of templates is actually weird, for example:

----
 template A1(T){enum A1=0;}
template A2(T){enum B=0;}
template A3(T){enum A3=0;enum B=0;}
void main(){
    enum a1=A1!int;    //ok
    enum a2=A2!int.B;    //ok
    //    enum a3=A3!int.B;    //Error: no property 'B' for type 'int'
}
----

Maybe this was for historical reasons (ie CTFE might've come after those
design patterns were invented).

I would propose to use *regular template functions* instead, whenever
possible (essentially for the ones that return a value):

For our example, this would become:
----
 bool hasMember(T, string name)(){
    static if (is(T == struct) || is(T == class) || is(T == union) || is(T =
= interface))
        return staticIndexOf!(name, __traits(allMembers, T)) != -1 ||
                __traits(compiles, { mixin("alias Identity!(T."~name~") Sym;
"); });
    else return false;
}
// can be used in exactly the same way:
static assert(hasMember!(A,"field"));
----

*Advantages*:
* clear syntax: returns bool, and body is simpler to read as well
* DRY: hasMember only mentioned once
* no weird behavior as above
* doesn't break any code as usage is the same (backwards compatible)

In fact, I was able to convert with no problems a number of other such
templates:
 staticIndexOf
 isSame
 expectType
 genericIndexOf
etc...

For the latter (genericIndexOf), the template body contained a clause like
that:
 enum next  = genericIndexOf!(e, tail).index;
enum index = (next == -1) ? -1 : 1 + next;
during conversion, I just had to add the line:
return Tuple!(int, "index",int, "next")(index,next);
which could further be simplified using a helper function as its a common
idiom, into:
return TupleNamed!("index","next")(index,next);


*Limitations*:
One typical case where this isn't possible is when a template defines an
type alias, eg:
----
template Iota(int stop) {
    static if (stop <= 0)
        alias Iota=TypeTuple!();
    else
        alias Iota=TypeTuple!(Iota!(stop-1), stop-1);
}
----
May 13 2013
next sibling parent reply "Dicebot" <m.strashun gmail.com> writes:
On Tuesday, 14 May 2013 at 06:56:23 UTC, Timothee Cour wrote:
 ...
My conclusion as well. Currently I follow simple rule when doing meta-programming in D: * If stuff returns type, make it a template and write in functional recursive flavor * If stuff returns data, make it a function (template function if needed) and just let CTFE do the magic. One major advantage is that it helps to reduce template instance count and thus compiler memory consumption.
May 14 2013
parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, May 14, 2013 10:27:38 Dicebot wrote:
 On Tuesday, 14 May 2013 at 06:56:23 UTC, Timothee Cour wrote:
 ...
My conclusion as well. Currently I follow simple rule when doing meta-programming in D: * If stuff returns type, make it a template and write in functional recursive flavor * If stuff returns data, make it a function (template function if needed) and just let CTFE do the magic. One major advantage is that it helps to reduce template instance count and thus compiler memory consumption.
I believe that the typical approach at this point is that if it's something that will only ever be done at compile time and/or is querying about a type, an eponymous template is used, whereas if it's something that might be useful at runtime, then a normal function is used which is then CTFEable. I've never really thought about making the type stuff into functions, but I don't really agree that it's a problem that they aren't, particularly since it makes no sense to use them at runtime, and at least with eponymous templates, even if you try and use them at runtime, the result is still calculated at compile time, unlike with CTFE. I'm not sure that it actually matters much, but that _would_ result in a slight performance penalty for any code that was using traits in runtime code for some reason (though again, I don't know why you would), and as Steven pointed out, CTFE carries a definite performance penalty at compile time at the moment (though hopefully that's temporary). My gut reaction is definitely to keep doing traits as we have been, and it does seem off to me to make it so that something that's purely meant for compile time can be used at runtime, but it may make sense to change them. - Jonathan M Davis
May 14 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 14 May 2013 02:56:09 -0400, Timothee Cour  
<thelastmammoth gmail.com> wrote:

 *Limitations*:
 One typical case where this isn't possible is when a template defines an
 type alias, eg:
 ----
 template Iota(int stop) {
     static if (stop <= 0)
         alias Iota=TypeTuple!();
     else
         alias Iota=TypeTuple!(Iota!(stop-1), stop-1);
 }
 ----
Another is that CTFE can hit a brick wall and fail to compile. It's an implementation bug, but something to consider before changing everything to CTFE. AFAIK, straight templates don't have this limitation. There was quite a bit of talk at the conference about how CTFE is awesome, but too limited based on the current D front end. Otherwise, I like the idea. If nothing else, it gives more flexibility. -Steve
May 14 2013