I dun a DIP, possibly the best DIP ever

Steven Schveighoffer schveiguy at gmail.com
Fri Apr 24 21:41:57 UTC 2020


On 4/24/20 4:55 PM, Walter Bright wrote:
> On 4/24/2020 10:10 AM, Steven Schveighoffer wrote:
>> I think the ellipsis version is superior simply because it has more 
>> expressive power (see my posts elsewhere). An ideal proposal would 
>> allow all things to be handled within one expression, but the ellipsis 
>> is better in that it is unambiguous and does not break code.
> 
> My issue is finding the best way to do this. Adding powerful syntax 
> "just because we can" leads to an unusable language. Queue my opposition 
> to AST macros.

It's not just the expressive power, but the simple expressiveness of the 
array syntax is missing. There are too many ambiguities without adding 
syntax because expressions that use tuples can already work with them.

The simplest is "make me an array of all these values"

[tup * 2]

But I don't see how the compiler knows which parts of the expression are 
part of the expansion, and which parts aren't. I would fully expect the 
above to result in:

([tup[0] * 2], [tup[1] * 2], ...)

And if it doesn't, then the compiler is making odd arbitrary decisions 
as to which part of the expression is expandable.

Which means, essentially, the odd bizarre cases are doable, but the 
useful ones are not.

The ellipsis doesn't do much in terms of functionality, but it gives you 
a place to tag "this is what I want you to expand", so the compiler 
makes the right decisions.

>> And actually, with a staticIota-like thing you could do every third 
>> tuple member quite easily.
>>
>> alias everyThird = staticIota!(2, Tup.length, 3); // start, end, step
>>
>> alias everyThirdMember = (Tup...[everyThird])...;
> 
> Touche. Well done.
> 
> Let's not fall into the mode of only looking at the way C++ did it and 
> not seeing other ways. C++ has problems (like not having arrays) that 
> lead it in different directions for solving array-like problems.

I'm fully on board with breaking with C++ when it doesn't make sense. In 
the case of the array syntax, I think it's just not workable.

> What do other languages do? How are things like this expressed in 
> mathematics?


Math is a great inspiration: https://en.wikipedia.org/wiki/Sequence

Lots of positional notation, but you can see it uses an ellipsis to 
denote "continues on".

The question is, how can we denote "apply this expression to every 
element using these tuples". That's the goal. I don't know how we can do 
it with any more brevity or readability than adding a specific operator 
for it or adding a symbol for it.

> 
> 
>> staticIota is kind of another primitive that is super-useful, and 
>> would be easy for the compiler to provide.
> 
> Where does one stop in adding operators to the language?

You can do staticIota without language help. Just like you can do ALL of 
this without language help. The point where you stop is when it doesn't 
give you a 10-50x speedup in compilation and 50% reduction in memory by 
letting the compiler take care of it.

Note that I'm not saying we can't do staticIota in library code. But 
really, "count from 0 to N" is such a simple thing for a computer to do, 
it seems really wasteful to have to do it via symbol tables and CTFE.

> Another approach to resolving the original problem (template 
> instantiation bloat) is for the compiler to recognize templates like 
> AliasSeq as "special" and implement them directly.
> 
> 
> For example, AliasSeq is defined as:
> 
>    template AliasSeq(TList...) { alias AliasSeq = TList; }
> 
> This pattern is so simple it can be recognized by the compiler. (Having 
> std.meta.AliasSeq being a special name known to the compiler is not 
> necessary.)
> The compiler already recognizes certain patterns, such as the expression 
> that does a rotate, and then generates the CPU rotate instruction for it.

Not arguing with that. The faster D can get, the better, regardless of 
whether we add any other syntax features.

One thing I would say is that AliasSeq is going to be identical for 
every instantiation, so there is no reason to store it in the symbol 
table. Just generate it every time, and then you cut down on a ton of 
template memory usage. Same thing could be said for a staticIota.

-Steve


More information about the Digitalmars-d mailing list