October 05, 2017
I suggest we continue where I made a more extensive reply to you.
October 05, 2017
After reading Timon's message, I understand your point a lot better. See my reply to Timon.

> and you have to rewrite many wrappers for Crng functions despite the alias this because they either require return Crng or gequire a pointer to one.

This is a good point. Let me think about it.
October 05, 2017
On Thursday, 5 October 2017 at 08:27:14 UTC, Dukc wrote:
> and you have to rewrite many wrappers for Crng functions despite the alias this because they either require return Crng or gequire a pointer to one. This needs to be defined manually for example:
>
> Crng initByTime(int time){return Crng(crng.initByTime(time))};
>
> With ADL it would be enough to extend the original struct with range primitives:
>
> auto front(Crng range){return current(*range);}
> void popFront(ref Crng range){toNextValue(*range);}
> //...
>
> With current semantics the latter example will work only with locally defined range function templates. Not with Phobos ones, because they cannot see the extension functions.

Am I missing something? You can already extend the original struct:

extern(c) struct Crng
{   int seedA;
    int seedB;
    ubyte[] restOfTheData;

    extern (D) {
        // or without extern (D)...
        auto front() { return current(&this); }
        void popFront() { toNextValue(&this); }
    }
}

What does it matter if you put your extension functions inside the struct or outside of it? (same question to Timon). Unless you also propose "extending" the modules themselves (like reopening namespaces in C++? This is a whole another can of worms...)
Looks like what most people want are extension methods, not ADL?
October 05, 2017
On Thursday, 5 October 2017 at 10:07:31 UTC, nkm1 wrote:
> Am I missing something? You can already extend the original struct:
>
> extern(c) struct Crng
> {   int seedA;
>     int seedB;
>     ubyte[] restOfTheData;
>
>     extern (D) {
>         // or without extern (D)...
>         auto front() { return current(&this); }
>         void popFront() { toNextValue(&this); }
>     }
> }
>

Does that work? If so, good. But still not optimal, because you should be able to extend the functionality of code without changing it.

> Looks like what most people want are extension methods, not ADL?

We already have extension methods, but rather we want extension methods that work with other extension methods they don't know of. But I think that's what you meant and in this case the answer at least in my case is yes.

Unrelated: Since when have C struct had array members? I'm not so smart in my examples X).
October 05, 2017
On Thursday, 5 October 2017 at 10:23:17 UTC, Dukc wrote:
> Does that work? If so, good. But still not optimal, because you should be able to extend the functionality of code without changing it.

It works, yes. The point is, additional methods in the struct body, and free standing functions outside of the body, but in the same module, is basically the same thing in D (I believe Andrei already mentioned that).

>> Looks like what most people want are extension methods, not ADL?
>
> We already have extension methods, but rather we want extension methods that work with other extension methods they don't know of. But I think that's what you meant and in this case the answer at least in my case is yes.

Ah, right. Well, anyway, that's not ADL by itself, since ADL only looks in the namespace of the argument type (so it won't find your extension methods if they're in some different namespace/module).

> Unrelated: Since when have C struct had array members? I'm not so smart in my examples X).

Isn't that the "struct hack" (aka "flexible array member")? :)
October 05, 2017
On Thursday, 5 October 2017 at 11:22:27 UTC, nkm1 wrote:
> Isn't that the "struct hack" (aka "flexible array member")? :)

(I mean, it would be in C :)


October 05, 2017
On 05.10.2017 11:52, Walter Bright wrote:
> On 10/5/2017 2:13 AM, Timon Gehr wrote:
>> 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.
> 
> Ok, but I'm not sure what the proposal was.
> 
> 
>> UFCS allows hijacking. For an example, see:
>> https://github.com/tgehr/d-compiler/pull/1#discussion-diff-89697186L85
> 
> That may be a bug in the compiler. Can you produce a small test case?

struct S{
    // string foo(){ return "hijacked!"; } // uncomment to hijack
}

string foo(S s){ return "not hijacked!"; }

void main(){
    S s;
    import std.stdio;
    writeln(s.foo());
}


> I know that some of the UFCS code was written without regard to hijacking.
> ...

I think it is by design. Lookup first tries to find members of the type, and only if they don't exist applies UFCS lookup. Therefore, if members are added to the type or made visible, this may hijack existing UFCS usages.

The way to fix it is to do UFCS lookup always and then error out if both UFCS and member lookup match, but there is probably quite some code relying on the fact that you can provide a custom implementation of a general UFCS function by just adding a member. Also, with the hijacking error if you actually meant the member function the only way out I see is to use __traits(getMember, ...) for disambiguation.

(BTW: Local imports allow hijacking too. Maybe this one could be fixed?)

> 
>> 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.
> 
> I submit it is surprising only if you're used to ADL :-)
> ...

I'm not used to ADL. I think it is surprising because input ranges support front. ;) (It's not surprising to _me_, I'm rather familiar with D's features, especially those added before last year or so. My statement is more that it could be surprising to many, and that it is not necessarily reasonable to blame them for being surprised.)

> ...
> 
> D's name lookup rules were quite simple, and deliberately set up that way. Unfortunately, most people thought they were unintuitive. It turns out that simple algorithms are not intuitive, and we now have a fairly complex lookup system. Martin and I implemented it, and probably neither of us completely understands it. I find it regrettable that things have gotten to that state.
> ...

It might make sense to sit down at some point and see what goals the complex rules try to achieve and then come up with simpler rules that achieve the same (or better) goals.

> 
>> 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.)
>>  ...
> 
> I suggest for this case writing a wrapper type for Iota, with front/empty/popFront as members of that wrapper, then it should be good to go. It's hardly any more work than writing the free functions.

One thing that currently works is having the following string constant in a util.d file:

enum ufcs_=q{
    private{
        import std.typecons: Proxy;
        struct UFCS(T){ T payload; mixin Proxy!payload; }
        auto ufcs(T)(T arg)@trusted{ return *cast(UFCS!T*)&arg; }
    }
};

Then, the following code compiles and runs:

import util: ufcs_;
mixin(ufcs_);

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.algorithm, std.stdio;
    iota(0,10).ufcs.each!writeln; // ok
}

This exploits what seems to be a serious encapsulation-breaking bug in std.typecons.Proxy in order to pick up all UFCS functions visible from the current module, but a safe version of this could be made.

> Doing this kind of wrapper doesn't work for operator overloading, which brings us back to ADL is for operator overloading.

Why does it not work for operator overloading?
October 05, 2017
On 05.10.2017 11:52, Walter Bright wrote:
> On 10/5/2017 2:13 AM, Timon Gehr wrote:
>> 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.
> 
> Ok, but I'm not sure what the proposal was.

I forgot to answer to this.


> On 27 September 2017 at 17:41, Ilya Yaroshenko via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> I would prefer outer operator overloading be added to D instead of type wrappers. So a user can import a library for operations, rather then library of wrappers. --Ilya
> 

"outer operator overloading" is UFCS for operators. I.e.:

struct S{ int x; }
S opBinary(string op:"+")(S a,S b){ return S(a.x+b.x); }

void main(){
    auto s=S(3), t=S(4);
    import std.stdio;
    writeln(s+t); // S(7)
}

Starting from:

s+t

It rewritten to (as per the spec):
s.opBinary!"+"(t)

and then UFCS is applied (as per the spec):
opBinary!"+"(s,t)


I'm very much in favor of this. Also, those rewrites should be consistently applied for all types, even built-ins (the compiler implementation can be more complex, but the language rules would be simplified).
One immediate benefit would be that opCmp could be reliably used for all types that support comparison, for example 2.opCmp(3).
Another benefit would be that operators such as += can reassign class references, for example when a value type is implemented as a unique reference to immutable data.
October 05, 2017
On Thursday, 5 October 2017 at 11:22:27 UTC, nkm1 wrote:

> It works, yes. The point is, additional methods in the struct body, and free standing functions outside of the body, but in the same module, is basically the same thing in D (I believe Andrei already mentioned that).

But I meant extending the struct remotely from a different module.

> Ah, right. Well, anyway, that's not ADL by itself, since ADL only looks in the namespace of the argument type (so it won't find your extension methods if they're in some different namespace/module).

Can well be, I don't know ADL precisely. Probably it should not be used as-is then, if at all.


October 05, 2017
On 10/3/17 10:00 PM, Walter Bright wrote:
> 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.

That's not always possible.

-Steve