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