On type functions

Steven Schveighoffer schveiguy at gmail.com
Sun May 3 15:12:32 UTC 2020


On 5/3/20 5:35 AM, Stefan Koch wrote:
> 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!
> 
>      }
> }


This is cool, but we can already do this (as Adam says). Would it not be 
more prudent for this to just be a UDA that says "don't put in symbol 
table"?

What I want is symbol manipulation like variables.

i.e.:

alias types = AliasSeq!(int, char, bool);

alias[] ctSort(alias[] items) { return sort!(t => t.name)(types); } // 
or something like this

static assert(is(ctSort(types) == AliasSeq!(bool, char, int)));

I can already do this too, but painfully.

-Steve


More information about the Digitalmars-d mailing list