www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 9786] New: Allow [non-member|UFCS] implementation of operators

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786

           Summary: Allow [non-member|UFCS] implementation of operators
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody puremagic.com
        ReportedBy: monarchdodra gmail.com



UFCS has made it possible for anyone to "expand" a type, by giving it new
functions, or new properties. One of the rationales is that it improves
encapsulation by not having to worry on if or if not some function is member or
global. It also improves encapsulation by allowing expanding a type externally.

However, the line is drawn at operators: These may *only* be implemented as
member functions. I see no real reason for this limitation.

Issue 7177 has shown a (very) strong need for the ability to implement opDollar
via non-member:
http://d.puremagic.com/issues/show_bug.cgi?id=7177

One of the reasons for not implementing non-member operators is that it would
grant the ability to add operators on built in type. I think that:
a) If user wants to do something stupid, that's his problem.
b) If we so desire, I'm sure the compiler could be more than powerful enough to
implement " disable opBinary" for any operator it does NOT want implemented
ever (eg string + string). Since the lookup rules are chose member function
first, this would not even be a "special case".

Anyways, yeah: non-member operators. We need them, and they make sense (IMO).

In particular, opDollar, since it is a property operator.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 22 2013
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786




Extracted from 7177, this is an example of how and why we'd want this:

//--------
import std.stdio;
import std.range;

//opDollar for any range with length
auto opDollar(R)(auto ref R r)
    if (isInputRange!R && hasLength!R)
{
    return r.length;
}

//RA range that does not define opDollar
struct S
{
     property
    {
        enum empty = false;
        int front(){return 1;}
        size_t length(){return 10;}
    }
    void popFront(){}
    int opIndex(size_t i){return i;}
}

void main()
{
    S s;
    writeln(s[$]); //Works
}
//--------

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 22 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786


Jonathan M Davis <jmdavisProg gmx.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jmdavisProg gmx.com



PDT ---
Personally, I think that it's a terrible idea to be able to add overloaded
operators to a type with 3rd party code, but with UFCS, it would be even worse,
because as soon as you had a conflict, there would be no way to resolve it.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 22 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786





 Personally, I think that it's a terrible idea to be able to add overloaded
 operators to a type with 3rd party code
Why would it be any worst than what we have with UFCS?
 but with UFCS, it would be even worse,
 because as soon as you had a conflict, there would be no way to resolve it.
That's a good point. Unless you call the operator directly, eg: lib.opBinary!"+"(a, b), but that's ugly as sin. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Mar 22 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786


Andrej Mitrovic <andrej.mitrovich gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrej.mitrovich gmail.com



15:28:02 PDT ---
 a) If user wants to do something stupid, that's his problem.
That's not a good way to look at it, a 3rd party library could implement a non-member operator without the user knowing. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Mar 22 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786


Maxim Fomin <maxim maxim-fomin.ru> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |maxim maxim-fomin.ru



---
Operator overloaded methods are special functions. There are several problems
with UFCSising them.

1) When there is conflict between different functions across several modules,
it is easy to overcome the problem by renaming functions. If you have two
opCalls for structs (for example) in two different modules and if you try to
use them simultaneously, any code like S() is broken and have to be rewritten.
Worse may happen with opBinary like in 
   S a, b;
   .. 
   a = a + b;
Now opBinary is hijacked by something else and code is fundametally broken.
Rewriting a+b is not the same as rewriting a.foo(). And in presence of regular
function name conflict it is possible to rename for ex. foo() to myFoo() and be
happy, but it is not posssible to have a +myOpBin b, so overloading operator
immediatly loses its sense.

2) Druntime may dependend on operator overloading functions. When you try to
UFCSizly overload operator, and get return value from function which was
compiled when operator was not overloaded (due to separate compilation model),
you may have logically incorrect value: you expect that operation was performed
with overloaded operators but actually it was not the case.

In general, I prefer to leave overloading decision to owner of the type.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 23 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786


timon.gehr gmx.ch changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |timon.gehr gmx.ch




 Operator overloaded methods are special functions.
No, they are specially named normal functions with some syntax sugar support. They can just as well be called as normal functions/methods. In this case, UFCS already works.
 There are several problems with UFCSising them.
 ...
There are no issues not common to other functions. Operator overloading is all about syntax. It is completely pointless to single out the operator overloading functions in any other way. The objections are simply invalid: 1) (template) functions can always be renamed, even if they happen to be named eg. opBinary. 2) Druntime will _never_ catch up UFCS (template) functions anyway. It does not matter if they are called eg. opUnary or toString. BTW, I do not think this is an enhancement. According to the online docs and TDPL, it should work. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Mar 30 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786




---


 Operator overloaded methods are special functions.
No, they are specially named normal functions with some syntax sugar support. They can just as well be called as normal functions/methods. In this case, UFCS already works.
This is exactly why they are special - because some language constructs are transformed to them (they have "some syntax sugar support"). Other methods do not have such feature. Besides, UFCS allows simply to transform function call to method call, it does not promise to support "some syntax sugar" of some "specially named normal functions" beyond its scope.
 
 There are several problems with UFCSising them.
 ...
There are no issues not common to other functions. Operator overloading is all about syntax. It is completely pointless to single out the operator overloading functions in any other way.
Nobody tries to single out them. They are already singled outed by the fact of their existence.
 The objections are simply invalid:
 
 1) (template) functions can always be renamed, even if they happen to be named
 eg. opBinary.
.. in general case when you have ordinal method. Renaming foo() to myFoo() is not a big deal. Renaming a[i[$]] = b[] in terms of explicit opSlices and opIndexes is not that fruitful. What's more, in case of foo() => myFoo() UFCS is still useful, while in case of a[i[$]] = b[] => opSlice etc. you cannot write nice expressions (no way for +mybin operator), so supporting this feature loses sense. In short, making UFCS to support "some syntax sugar" beyond its purpose is a feature which can be easily abused and broken, so why should it be supported?
 
 2) Druntime will _never_ catch up UFCS (template) functions anyway. It does not
 matter if they are called eg. opUnary or toString.
 
 BTW, I do not think this is an enhancement. According to the online docs and
 TDPL, it should work.
This was not supposed to work. UFCS was made to perform simple method transforms. Transforming expressions info function calls is separate issue. Some programmers demand this feature because they considered it as a "cool" due to "some syntax sugar support". The problem is that some others also think so and start to overload. And since the amount of just "specially named normal functions" is fixed and limited, probability to run into conflict with opWhatever is higher than in case of ordinal method. Nicest case happens when they overload operator methods simultaneously in independent modules and somebody decided to use each of the modules. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Mar 30 2013
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9786




std.typecons.Proxy is incorrect because of this inconsistent behaviour of DMD:

import std.conv, std.typecons;

struct S{
    int x;
}

int front(ref S s){ return s.x; }
bool empty(ref S s){ return s.x>10; }
void popFront(ref S s){ s.x++; }

auto opBinary(string op)(S a, S b){ return S(mixin("a.x "~op~"b.x"));}

struct Capture(T){
    private T payload;
    mixin Proxy!payload;
}

auto ufcs(T)(T arg){
    Capture!T r;
    r.payload = arg;
    return r;
}

import std.stdio;

void main(){
    auto s = S(3);
    writeln(s.ufcs); // [3, 4, 5, 6, 7, 8, 9, 10]
    writeln(s.opBinary!"+"(s)); // S(6)
    //writeln(s.ufcs.opBinary!"+"(s.ufcs)); // error!
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Mar 31 2013