Should we add `a * b` for vectors?

Timon Gehr timon.gehr at gmx.ch
Thu Oct 5 09:13:58 UTC 2017


On 03.10.2017 21:25, Walter Bright wrote:
> On 10/2/2017 4:15 AM, Timon Gehr wrote:
>> On 30.09.2017 23:45, Walter Bright wrote:
>>> ...
>>> D has other ways of doing what ADL does,
>>
>> What are those ways? I'm aware of two basic strategies, both suboptimal:
>>
>> - Every module imports all other modules.
>> - Proliferation of wrapper types.
> 
> https://dlang.org/spec/operatoroverloading.html#binary
> 
> C++ does not have this notion.
> 
> 
>> It's not per se related to operator overloading:
> 
> ADL was specifically intended to address operator overloading.
> ...

It's easy to explain why: In C++, operators are the _only_ functions 
that have UFCS.

This is in stark contrast to D, where all functions _but_ operators have 
UFCS.

The proposal was allow UFCS also for overloaded operators.

Hence, this discussion is about UFCS. These are not really operator 
overloading issues.

> 
>> ---
>> module a;
>> import std.range: isInputRange;
>> auto sum(R)(R r)if(isInputRange!R){
>>      typeof(r.front) result;
>>      for(auto t=r.save;!t.empty;t.popFront())
>>          result+=t.front;
>>      return result;
>> }
>> ---
>>
>> ---
>> module b;
>> import a;
>> import std.range;
>>
>> void main(){
>>      int[] a = [1,2,3,4];
>>      import std.stdio: writeln;
>>      writeln(a.front); // ok
>>      writeln(sum(a)); // error, the type is an input range, yet has no 
>> front
>> }
>> ---
>>
>> I.e., the operations that are supported on the type differ depending 
>> on the module that it is accessed from. If there are multiple 
>> definitions of the same name, different modules might not agree which 
>> one is being referred to. (This is particularly likely for overloaded 
>> operators, as the set of names is finite and small. This is what Manu 
>> means when he says it can lead to nasty surprises. This is very 
>> plausible, but I don't have a good example.)
> 
> This gets into the anti-hijacking support D has (and C++ does not). If 
> multiple modules with declarations of `writeln` are in scope, the 
> compiler attempts a match with `writeln` in each module. If there is a 
> match with more than one module, an error is issued. The user will then 
> have to specify which one he wants.
> ...

UFCS allows hijacking. For an example, see:
https://github.com/tgehr/d-compiler/pull/1#discussion-diff-89697186L85

Commenting out 'private' caused a stack overflow.

> This is specifically designed to prevent nasty surprises. C++ has a big 
> problem with ADL in that overloading is not encapsulated and any 
> #included header can inadvertently add more overloads that may be 
> entirely unrelated. Any .h can crack open any namespace and insert more 
> overloads into it. It's completely unhygienic and uncontrollable.
> 
> As for the specific example you gave, I get:
> 
> a.d(3): Error: no property 'front' for type 'int[]'
> a.d(4): Error: no property 'save' for type 'int[]'
> b.d(8): Error: template instance a.sum!(int[]) error instantiating

(There is a comment in the code noting that it will not compile.)

The intention of the code was to demonstrate that a type can pass 
isInputRange in the same module in which it does not support front. This 
is an example of surprising name lookup behavior.

Of course there is also the opposite problem. You can have a type that 
supports all range primitives via UFCS but does not pass isInputRange, 
because Phobos does not import the module where the primitives are 
defined. (This particular case is sometimes solved by ADL, sometimes not.)

---

struct Iota{ private int a,b; }
auto iota(int a,int b){ return Iota(a,b); }

@property int front(Iota i){ return i.a; }
@property bool empty(Iota i){ return i.a>=i.b; }
void popFront(ref Iota i){ ++i.a; }

void main(){
     import std.stdio;
     for(auto r=iota(0,10);!r.empty;r.popFront){ // ok
         writeln(r.front);
     }
     import std.algorithm;
     iota(0,10).each!writeln; // error
     foreach(i;iota(0,10)) writeln(i); // error
}

---

If I copy-paste portions of std.range into the main module of the above 
program instead of importing, I will be able to use my custom type with 
those Phobos ranges.


More information about the Digitalmars-d mailing list