October 03, 2017
On 10/3/2017 12:58 AM, Enamex wrote:
> I couldn't find anything focused on D's overloading resolution scheme (or anything with the specific term "reverse operand lookup"). Can you elaborate?

See my reply to Timon.
October 03, 2017
On Tuesday, 3 October 2017 at 19:25:32 UTC, Walter Bright wrote:
>
> 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.
>

Anti-hacking might make it tricky to have operators as free-standing functions, but I'm not sure it would be impossible. The solution for function overloading is fully qualified names. However, this does not make sense operator overloading. Mixing operator overloading and D's anti-hijacking, you would basically be prevented from having two overloaded versions of an operator. This means that if you want multiple versions of operators, you would not be able to have the operators as member functions and they would have to be free-standing functions, ideally in separate modules.

With respect to the earlier discussion of type wrappers, this can also be implemented with a template parameters that controls the operator-overloading of *, something like below.

struct Foo(bool mtimesOverload = false)
{
    template opBinary(string op = "*")(Foo foo)
    {
        static if (mtimesOverload)
        {
            return mtimes(this, foo);
        }
        else
        {
            return times(this, foo);
        }
    }
}

So you could do a Matrix alias that has mtimesOverload=true.
October 03, 2017
On 10/3/17 3:25 PM, Walter Bright wrote:
> 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.

One thing I would like to have control over is how 'this' is passed.

A nice thing about UFCS is I can use it to pass-by-value the faux 'this' parameter, whereas I cannot do this with a member function. It makes a difference in some cases, especially with const functions.

It also can be cheaper to pass a small struct by value.

None of this is possible with operator overloading. A way out could possibly be to alias a non-member function for the operator.

-Steve
October 03, 2017
On 10/3/2017 5:24 PM, Steven Schveighoffer wrote:
> It also can be cheaper to pass a small struct by value.

Should not be a problem if the compiler inlines it.
October 04, 2017
On Tuesday, 3 October 2017 at 19:25:32 UTC, Walter Bright wrote:
> 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

But you can't deny our solution eats expressive power: If you don't want to change code you're importing, you have to write a wrapper type for int[] here. Alias this helps, but because save() and slicing operators have to return the type of this, there's still manual work to do if you want Phobos algorithms to utilize it's random access.

It may be that ADL or something similar would cause too much trouble to be worth it, don't know about that. But what I'm saying that we definitely have a considerable problem here and it would solve it.
October 04, 2017
On 10/4/2017 2:28 AM, Dukc wrote:
> But you can't deny our solution eats expressive power: If you don't want to change code you're importing, you have to write a wrapper type for int[] here. 

Please present an example.
October 04, 2017
On 10/02/2017 07:15 AM, Timon Gehr wrote:
> 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.)

Indeed a good example would strengthen the argument considerably. -- Andrei
October 04, 2017
On Wednesday, 4 October 2017 at 17:56:16 UTC, Walter Bright wrote:
> On 10/4/2017 2:28 AM, Dukc wrote:
>> But you can't deny our solution eats expressive power: If you don't want to change code you're importing, you have to write a wrapper type for int[] here.
>
> Please present an example.

I think that was the point of Timon's example. If you have a module A that implements a range algorithm, a module B that defines a range-like type (but actually its member function not matching the exact signature of range primitives), you (module C) as user of modules A and B are not able to provide range primitive wrapper functions for the type defined in module B (which you can't modify). So you can't use A's range algorithm on type defined in B.

ADL solves this problem (adapting third-party libraries to your needs).

Since D's modules are closed (can't be extended from the outside like namespaces), if we want to support some form of ADL (the primary use-case being algorithm libraries), we would probably need to introduce some form of open for extension scopes like namespaces. Or change extern (C++, namespace) to behave like people coming from C++ may expect.
October 04, 2017
On 10/4/2017 3:35 PM, Petar Kirov [ZombineDev] wrote:
> On Wednesday, 4 October 2017 at 17:56:16 UTC, Walter Bright wrote:
>> On 10/4/2017 2:28 AM, Dukc wrote:
>>> But you can't deny our solution eats expressive power: If you don't want to change code you're importing, you have to write a wrapper type for int[] here.
>>
>> Please present an example.
> 
> I think that was the point of Timon's example.

An example would be appreciated. Timon's example requires guesswork as to what he intended, because it does not compile in ways unrelated to his point.
October 05, 2017
On 2017-10-05 00:59, Walter Bright wrote:

> An example would be appreciated. Timon's example requires guesswork as to what he intended, because it does not compile in ways unrelated to his point.

It's supposed to not compile, because D doesn't have ADL.

$ cat foo.d
module foo;
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;
}

$ cat main.d
import foo;
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
}

$ dmd foo.d main.d
foo.d(5): Error: no property 'front' for type 'int[]'
foo.d(6): Error: no property 'save' for type 'int[]'
main.d(8): Error: template instance foo.sum!(int[]) error instantiating

Adding the following line to the "foo" module fixes the problem:

import std.range : front, save, empty, popFront;

 $ cat foo.d
module foo;
import std.range: isInputRange;
import std.range : front, save, empty, popFront;
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;
}

$ dmd foo.d -run main.d
1
10

-- 
/Jacob Carlborg