Operator overloading -- lets collect some use cases

aarti_pl aarti at interia.pl
Mon Dec 29 11:25:27 PST 2008


Andrei Alexandrescu pisze:
> aarti_pl wrote:
>> Andrei Alexandrescu pisze:
>>> downs wrote:
>>>> aarti_pl wrote:
>>>>> Andrei Alexandrescu pisze:
>>>>>> aarti_pl wrote:
>>>>>>> Don pisze:
>>>>>>>> There's been some interesting discussion about operator overloading
>>>>>>>> over the past six months, but to take the next step, I think we 
>>>>>>>> need
>>>>>>>> to ground it in reality. What are the use cases?
>>>>>>>>
>>>>>>>> I think that D's existing opCmp() takes care of the plethora of
>>>>>>>> trivial cases where <, >= etc are overloaded. It's the cases where
>>>>>>>> the arithmetic and logical operations are overloaded that are
>>>>>>>> particularly interesting to me.
>>>>>>>>
>>>>>>>> The following mathematical cases immediately spring to mind:
>>>>>>>> * complex numbers
>>>>>>>> * quaternions (interesting since * is anti-commutative, a*b = -b*a)
>>>>>>>> * vectors
>>>>>>>> * matrices
>>>>>>>> * tensors
>>>>>>>> * bigint operations (including bigint, bigfloat,...)
>>>>>>>> I think that all of those are easily defensible.
>>>>>>>>
>>>>>>>> But I know of very few reasonable non-mathematical uses.
>>>>>>>> In C++, I've seen them used for iostreams, regexps, and some stuff
>>>>>>>> that is quite frankly bizarre.
>>>>>>>>
>>>>>>>> So, please post any use cases which you consider convincing.
>>>>>>>>
>>>>>>> DSL support in mother language. As an example I can give SQL in D or
>>>>>>> mockup tests description language (usually also in D - not as a
>>>>>>> separate script language).
>>>>>>>
>>>>>>> In my previous posts I already put few arguments why it is sometimes
>>>>>>> much more handy to use "DSL in mother language" approach rather than
>>>>>>> string mixins with DSL language itself.
>>>>>> I saw that, but few, if any, of the arguments you made apply to
>>>>>> operators vs. named methods. You argued on string mixins vs. using
>>>>>> named symbols. In fact one argument of yours works against you as
>>>>>> there's no completion for operators.
>>>>>>
>>>>>> Andrei
>>>>> I put my argument much earlier in this discussion:
>>>>> eg. here:
>>>>> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81040 
>>>>>
>>>>>
>>>>>
>>>>> To sum up: using methods to emulate operators is just completely
>>>>> unreadable and looks awful. It's very easy to make an error using 
>>>>> such a
>>>>> technique.
>>>>>
>>>>> Later, I just replayed to posts convincing that solution for 
>>>>> supporting
>>>>> DSL languages in D is using string mixins. I believe I could provide a
>>>>> few weighty arguments to support my opinion that it is not always best
>>>>> solution for this problem. In many cases it is much better to 
>>>>> integrate
>>>>> DSL into D as a kind of API, not separate sub-language. Then we can 
>>>>> get
>>>>> some support from IDE, while using string mixins we probably would get
>>>>> nothing...
>>>>>
>>>>> Best Regards
>>>>> Marcin Kuszczak
>>>>> (aarti_pl)
>>>>
>>>> Scrapple.Tools uses operator overloading to provide fake infix 
>>>> keywords along the lines of [2, 3, 4] /map/ (int i) { return 
>>>> format(i); }, with a simple and convenient syntax for defining them 
>>>> (mixin(Operator!("map", "something something use lhs and rhs; ")); ).
>>>>
>>>> Forcing the end user to write mixin(function()) for such keywords is 
>>>> *NOT* the way to go, for several reasons: a) it's hard to extend, 
>>>> and b) it's needlessly verbose.
>>>>
>>>> The reason infix keywords are useful, is because they can be used as 
>>>> a simple way to chain bijective operations together, i.e. a /foo/ b 
>>>> /map/ c /select/ d. Without infix keywords, this would take the form 
>>>> of select(map(foo(a, b), c), d), which is an atrocity because it has 
>>>> to be read in two directions - middle-leftwards for the operations, 
>>>> and middle-rightwards for the parameters.
>>>>
>>>> (and yes, I know it's wrong to rely on operator evaluation order. So 
>>>> sue me. )
>>>>
>>>> Of course, a more convenient solution would be the ability to extend 
>>>> the D syntax manually, but that's unlikely to appear in our lifetime.
>>>
>>> I sometimes think of a subtoken-based approach, e.g. any function 
>>> name starting and ending with an underscore is by definition infix. 
>>> It's the kind of solution that turns Walter's nose so I never brought 
>>> it up to him.
>>>
>>> Andrei
>>
>> Maybe:
>>
>> R op[Infix|Postfix]_Name(A a, B b ...);
> 
> That's not quite elegant. What if there is a symbol called Name in 
> scope? This will confuse the parser to no end. (I forgot to mention that 
> in the sub-token approach you'd still have to write the underscore when 
> issuing a call.)
> 
> Andrei

Rules for calling side is another subject. I didn't even try to address 
this part of problem. Additionally if we could get operator overloads as 
free functions, then there is another factor of complication. But let me 
try to put now few thoughts from top of my head:

1. Operator overloads are valid in the scope of 'import' validity. So, 
when import occurs on module level, then the scope is module. When 
import is in class, then scope is class. Depending on import qualifier 
operator overloads can be propagated or not. Maybe someday we will also 
get imports on function level, so there will be another level of 
operators validity. And maybe someday even imports on block level? :-)

{
   import std.complex;
   Complex c = 2i;
}

2. In the scope of import validity overloaded operators are taken before 
built-in operators without issuing errors. When there are other symbols 
in the scope with the same name as overloaded operator it should be an 
error. To use operators anyway you should resolve conflict using 
standard D methods:
a. with FQN syntax:

import doost.db.sql;

void main() {
   int LIKE = 5;

   //Query query = Select(Table).Where(Table.Name LIKE "A*"); //Error
   Query query = Select(Table).Where(Table.Name doost.db.sql.LIKE "A*");
}

b. with renamed imports

import doost.db.sql : SQL_LIKE = LIKE;


3. There still might be need for using built-in operators, even in 
narrow scope of validity of overloaded operator. Then it should be 
possible to escape overloaded operators with '.' e.g.

module doost.db.sql;

SqlExpression opInfix_==(Column col, Column col) {}

module main;

void main() {
   import doost.db.sql;
   Column col1, col2;

   if (col1 .== col2) {...}
}

In these case overload for == should be chosen, but it was escaped with 
'.', so built-in comparison should be used.

BR
Marcin Kuszczak
(aarti_pl)



More information about the Digitalmars-d mailing list