On type functions

Stefan Koch uplink.coder at googlemail.com
Sun May 3 09:35:34 UTC 2020


First things first, discussions about syntax are NOT WELCOME in 
this topic!
Type functions are something which I have had floating around my 
mind since early 2018.
While working on newCTFE I did performance measurements on code 
which people send me and which they believed was slow because of 
CTFE.
It turned out that actually recursive introspection templates and 
"unrolled tuple foreach" where actually at faut, most of the time.
Because they littered the code with many many statements and 
symbols that were only temporarily needed at compile time. And 
yet were incorporated into the generated code.

I told people to stay away from templates if they somehow could.
However they could not since introspection capabilities 
(__traits(allMembers) and such) only work if you can work with 
types.
Since templates are the only construct in the language which can 
take type parameters.
You're stuck with them and the associated overhead.

The template overhead:
   - needs to create persistent immutable types to maintain 
language invariants such as (is(T!void == T!void))
   - inherently constraint to strongly pure functional style and 
therefore locked into recursive patterns. (which are inherently 
slow (!) (Even if some language manage to optimize based on the 
stronger guarantees, that optimization itself takes time (which 
we don't have at compile time)))
   - because of the recursive style they are hard to understand 
once they are meant to change behavior based on different types.

About a year ago it struck me that nobody actually _wanted_ to 
use templates, for introspection.
Using templates for metaprogramming is like using a screwdriver 
to hammer nails into a wall.
If you had a hammer you'd use it, but if you don't the 
screwdriver is still a better option than doing it with your bear 
hands.
templates were invented to provide type parameterization not to 
express computation.

Let's take something that was designed to express computation, 
functions, and extend it with the ability to take types as 
objects.
The type function is born. (I thought I came up with it, but 
recently remembered that I first saw it in idris 
(https://www.idris-lang.org/))

D already have a way of expressing a "type variable" the `alias` 
keyword.
so having a function such as

bool isInt(alias T)
{
     return is(T == int);
}

as a completely natural thing to write.

let's now say we wanted to code to serialize a struct to a human 
readable string.
Avoiding templates unnecessary templates. (i.e.) the maximum 
number of template-instances creating during this task should be 
LINEAR to the number of structs printed.

string NameOfField(alias T, size_t fIdx)
{
     assert(is(typeof(T.tupleof), T.stringof ~ " has no .tupleof 
property maybe it's not an aggregate?");
     return __traits(identifier, T.tupleof[fIdx]);
}


string structToString(T)(T struct_)
{

     char[] result;
     result ~= __traits(identifier, T) ~ " :: {\n";
     foreach(fIdx, field; struct_.tupleof)
     {
         result ~= NameOfField!(T, fIdx) ~ " : "; // looks like a 
templates but it's not!

     }
}


More information about the Digitalmars-d mailing list