static map as a type function

Jackel jackel894_394 at gmail.com
Thu Sep 24 02:09:31 UTC 2020


On Thursday, 24 September 2020 at 00:44:21 UTC, Walter Bright 
wrote:
> On 9/23/2020 4:59 PM, Bruce Carneal wrote:
>> On Wednesday, 23 September 2020 at 22:36:55 UTC, Walter Bright 
>> wrote:
>>> The other way to do it is just have the compiler recognize 
>>> recursive templates and implement them directly. This would 
>>> provide the speedup, while not adding new features or 
>>> requiring any user code changes - it'll just run faster.
>> 
>> I see a few reasons to prefer type functions, wherever 
>> applicable (they cant do everything):
>> 
>> 1) type functions admit a simpler/bounded compiler 
>> implementation
>> 
>> 2) type functions admit simpler meta programs and the related
>> 
>> 3) type functions should be easier to debug, eager rather than 
>> lazy/latent compile time errors for one thing
>> 
>> 4) type functions exhibit better locality than templates
>> 
>> 5) to achieve type function like simplicity/debuggability with 
>> templates you need to rely more heavily on "best practices"
>
> I don't really understand these points.
>
> Using the existing recursive template calls is well-known and 
> well-understood functional style programming. It even 
> aesthetically looks and acts like function calls, except with a 
> bang (!).
>
> Making existing constructs work better is better than adding an 
> ever-expanding list of new constructs.

It's basically what D is to C++ template metaprogramming.

Compare the staticMap implementation with a type function 
implementation and it's pretty clear which one is more readable 
and easier to maintain. The current D implementation will also 
create a bunch of template bloat with numerous instantiations 
that aren't actually required.

StaticMap, and many other templates like it also need a 
workaround to reduce the number of instances, otherwise they 
fail. Which isn't as intuitive and not something someone will 
really know to do.

Behold:

template staticMap(alias F, T...)
{
     static if (T.length == 0)
     {
         alias staticMap = AliasSeq!();
     }
     else static if (T.length == 1)
     {
         alias staticMap = AliasSeq!(F!(T[0]));
     }
     /* Cases 2 to 8 improve compile performance by reducing
      * the number of recursive instantiations of staticMap
      */
     else static if (T.length == 2)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]));
     }
     else static if (T.length == 3)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]));
     }
     else static if (T.length == 4)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), 
F!(T[3]));
     }
     else static if (T.length == 5)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), 
F!(T[3]), F!(T[4]));
     }
     else static if (T.length == 6)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), 
F!(T[3]), F!(T[4]), F!(T[5]));
     }
     else static if (T.length == 7)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), 
F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6]));
     }
     else static if (T.length == 8)
     {
         alias staticMap = AliasSeq!(F!(T[0]), F!(T[1]), F!(T[2]), 
F!(T[3]), F!(T[4]), F!(T[5]), F!(T[6]), F!(T[7]));
     }

     else
     {
         /* While:
          *   alias staticMap = AliasSeq!(F!T[0], staticMap!(F, 
T[1 .. $]));
          * does fewer template instantiations, the compiler 
implements
          * recursive template instantiations with recursion, and 
long
          * sequences overflow the compiler's stack.
          * The divide-and-conquer approach uses log_2(n) stack 
frames.
          */
         alias staticMap =
             AliasSeq!(
                 staticMap!(F, T[ 0  .. $/2]),
                 staticMap!(F, T[$/2 ..  $ ]));
     }
}


More information about the Digitalmars-d mailing list