Short list with things to finish for D2

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Wed Nov 18 20:51:00 PST 2009


grauzone wrote:
> Andrei Alexandrescu wrote:
>> grauzone wrote:
>>> Andrei Alexandrescu wrote:
>>>> The rewrite is done long after lexing, so no low-level problems there.
>>>
>>> Oh, I thought it would let you introduce new operators. But it's only 
>>> about the existing ones.
>>>
>>> I find the idea to identify the operator using a string very sloppy 
>>> and sillyl just like using string mixins for small delegates in 
>>> std.algorithm etc.; but you'd probably say "it works and is useful" 
>>> and "it's short" and "it solves the current problem", so... whatever.
>>
>> We're trying to improve on the current situation, which forces the 
>> user to manually define a lot of small functions. If you have 
>> convincing reasons to argue that the current state of affairs is 
>> actually better, I'm all ears - both Walter and I could use less work, 
>> particularly if the outcome sucks (see e.g. T[new]). Also, if you have 
>> ideas on how things could be done in a way that you'd find not sloppy 
>> and not silly, that would be even better.
> 
> If I had a better proposal, I'd post it. I'm just saying that's it's a 
> bad hack, that _although_ solves the problem, will have negative side 
> effects for other reasons.

What are those other reasons? I'd be grateful if you could spell a 
complete argument.

> Does the current proposal make things simpler at all? All you're doing 
> is to enable the programmer to "fix" the clumsy semantics by throwing 
> lots of CTFE onto the problem. Why not generate the operator functions 
> with CTFE in the first place...

I'm afraid there is a confusion, in which case let me dispel it. There's 
no CTFE involved. The way people would use the feature would be in one 
of the following ways:

1. Just use "if" or "static if" the old-school:

T opBinary(string op)(T rhs) {
     static if (op == "+") return data + rhs.data;
     else static if (op == "-") return data - rhs.data;
     ...
     else static assert(0, "Operator "~op~" not implemented");
}

This has not a lot of advantage beyond the fact that you don't need to 
remember the mappings from symbols to their names.

2. Use one mixin expression:

T opBinary(string op)(T rhs) {
     return mixin("data "~op~" rhs.data");
}

3. Combine the two in various ways, also use restricted templates, 
forward to virtual functions, etc. etc.

The improvements are the following:

1. There is no need to memorize or look up a long table of symbol-name 
correspondence (e.g. + is opAdd, etc.)

2. There are source code savings because most often types want to 
overload operators en masse, not just a couple of them. In that case 
there is a code explosion of many functions. Worse, if the code needs to 
do something clever (e.g. Variant or expression templates), the 
cleverness must be spread all over, or effort needs to be spent into 
mapping back names to their symbols, as Variant actually does. It's not 
a pretty sight, so I'd rather have the language facilitate good usage 
instead of it facilitating bad usage that must be reverted to good usage.

3. The syntax is extensible, for example Walter wanted for the longest 
time to make !<>= etc. overridable but couldn't bring himself to write 
abominable names like opNotGreaterThanOrWhatever. Now it's very simple 
to integrate all symbols properly and without aggravation.

The cons I see are:

1. The user who writes even the old-school operators must have a basic 
understanding of compile-time parameterized functions.

2. Existing code gets broken.

3. The template functions can't be virtual (fixable with a forwarding 
thunk written once and for all).

Any more thoughts, please let them known. Again, this is the ideal time 
to contribute. But "meh, it's a hack" is difficult to discuss.


Andrei



More information about the Digitalmars-d mailing list