Operator/concept interoperability

Mason McGill via Digitalmars-d digitalmars-d at puremagic.com
Tue Jun 3 15:40:06 PDT 2014


On Tuesday, 3 June 2014 at 22:05:29 UTC, TheFlyingFiddle wrote:
> Well one reason for this is that unlike methods it is hard to
> resolve ambiguity between diffrent operator overloads that have
> been defined in diffrent modules.
>
> Example: 2D-vectors
> //vector.d
> struct Vector
> {
>     float x, y;
> }
>
> //cartesian.d
> Vector opBinary(string op : "+")(ref Vector lhs, ref Vector rhs)
> {
>    //Code for adding two cartesian vectors.
> }
>
> //polar.d
> Vector opBinary(string op : "+")(ref Vector lhs, ref Vector rhs)
> {
>     //Code for adding two polar vectors.
> }
>
> //main.d
> import polar, vector;
> void main()
> {
>     auto a = Vector(2, 5);
>     auto b = Vector(4, 10);
>
>     auto c = a + b; //What overload should we pick here?
>
>     //This ambiguity could potentially be resolved like this:
>     auto d = polar.opBinary!"+"(a, b);
>     //But... This defeats the whole purpose of operators.
> }

Interesting point. However, I believe this is also the case for 
ordinary functions used with UFCS:

   // cartesian.d
   Vector add(ref Vector lhs, ref Vector rhs)
     { /* Code for adding two cartesian vectors. */ }

   // polar.d
   Vector add(ref Vector lhs, ref Vector rhs)
     { /* Code for adding two polar vectors. */ }

   // main.d
   import polar, vector;
   void main()
   {
       auto a = Vector(2, 5);
       auto b = Vector(4, 10);
       auto c = a.add(b); // What overload should we pick here?

       // The fully qualified form looks quite different,
       // but it's rare, and intentionally explicit about what's
       // really going on, so that's OK.
       auto d = polar.add(a, b);
   }

It strikes me that ambiguous cases are not nearly as common as 
unambiguous cases, especially if you use selective imports.

I believe Julia uses a re-write rule for its operators: the first 
thing the compiler does when evaluating an operator expression is 
re-write it as a function call; after that, all the language's 
resolution rules apply as they would to any other function call. 
D could easily take this approach, simplifying the language while 
maintaining backwards-compatibility.

> Side note:
>
> You can achieve what you want to do with template mixins.
>
> ...
>
> While this implementation is not as clean as global operator
> overloading it works today and it makes it very simple to add
> operators to new types of grids.

Thanks for the example. This is actually my current solution :) 
However, as you allude to, I'm not a huge fan of it because

1) It requires boilerplate on the part of library users.
2) It makes you scratch your head and go "Huh; operator and 
non-operator functions have different compile-time polymorphism 
capabilities; why is that?"


More information about the Digitalmars-d mailing list