static map as a type function

Steven Schveighoffer schveiguy at gmail.com
Thu Sep 24 12:39:57 UTC 2020


On 9/23/20 11:51 PM, Paul Backus wrote:
> On Thursday, 24 September 2020 at 03:17:44 UTC, Steven Schveighoffer wrote:
>> The recursive version is not nice code. It's hard to understand and 
>> difficult to follow. It's main advantage is that because it's 
>> essentially a functional language, and all templates are cached, in 
>> certain cases, you can see a performance gain because the compiler can 
>> avoid actually redoing what it did. In practice, for things like 
>> staticMap, this doesn't ever pan out.
>>
>> I don't see how you solve the tail recursion thing anyway -- each 
>> template instance cannot rely on the work that has already been done, 
>> because of static if.
>>
>> The static map type function is a loop over an array. Easy to 
>> understand, easy to write. It's actually boring code. Static map 
>> shouldn't be interesting code at all.
>>
> 
> I feel the same way about the naive recursive version:
> 
> template staticMap!(alias F, Args...) {
>      static if (Args.length == 0)
>          alias staticMap = AliasSeq!();
>      else
>          alias staticMap = AliasSeq!(F!(Args[0]), staticMap!(F, Args[1 
> .. $]));
> }
> 
> One base case, one recursive case, one line for each. The code 
> practically writes itself. How could it be any clearer or more obvious?

It fails to explicitly say what the resulting size of the AliasSeq will 
be, unlike the type function code (result.length = types.length)

The transformation call to F is buried in the logic to construct the 
result. Compare to:

result[i] = F(t);

One has to mentally work through how the recursion relationship works to 
ensure it terminates properly, I have to read every line to see how the 
thing plays out. Vs. reading each line of the typefunction code and 
deciding "yes, this does what it's supposed to".

Yes, the code writes itself. Reading and understanding it isn't as 
straightforward.

BTW, I don't see how tail-recursion can be done still. Even with your 
tail-recursive version, you still have to rebuild each instance of the 
template, because you don't know if it's going to generate different 
code via static if. Whereas runtime tail recursion builds one version of 
the function, and just jumps back into the same function with modified 
input.

> Of course, for someone who lacks that background, it might very well 
> look bewildering--in the same way that, say, the Visitor pattern might 
> look bewildering to someone unfamiliar with the idioms of OOP. Does that 
> mean it's a bad pattern? Or does it just mean it's a pattern they 
> haven't learned yet?

Not bewildering, but requires more thought to understand. The array 
version requires little thought, almost every line is independent, and 
does not require the context of the whole construct to verify what it 
does or that it is correct.

It also helps that the "simple naive" version of the typefunction code 
is the best version. You don't have to write it in a special way to make 
the compiler perform well.

One of the reasons D's code generation capabilities are so approachable 
vs. C++ is because it does not need to be written in functional style. 
You can write code that does it exactly how you would do it if you were 
doing it by hand.

-Steve


More information about the Digitalmars-d mailing list