September 03, 2016
On Saturday, 3 September 2016 at 12:40:26 UTC, ZombineDev wrote:
> No, LINQ doesn't work because of interfaces, but because of extension methods (C#'s variant of UFCS). The IEnumerable<T> interface defines only a single method. All the useful functionality is implemented as extension methods which are only available if the user specifically imports the namespace in which where they're defined (just like D's ranges and range primitive implementations for arrays). Those extension methods are used as a fallback, similarly to UFCS in D: every type can override the extension methods by implementing the method itself. Also more inner namespaces (more closer to the method invocation) override more outer namespaces.

I know extension methods, that's not the point.
The point is, that you cannot have a generic method like this in C#, it won't compile:

class Bar
{
    void GenericMethod<T>(T arg)
    {
        arg.Foo();
    }
}

Instead you need a constraint like this:

interface IFoo
{
    void Foo();
}

class Bar
{
    void GenericMethod<T>(T arg) where T: IFoo
    {
        arg.Foo();
    }
}

Similarly for LINQ, you cannot just implement a generic "Sum" extension method for IEnumerable<T> that works for all T, because you cannot just use the + operator in that method. It is not defined on T if there are no respective constraints.

Look at how it is implemented separately for every type T that supports +:
https://msdn.microsoft.com/de-de/library/system.linq.enumerable.sum(v=vs.110).aspx
September 03, 2016
On Saturday, 3 September 2016 at 12:25:11 UTC, Andrei Alexandrescu wrote:
> What problems are you referring to? -- Andrei

The problems discussed here in the thread related to name lookup at template instantiation time.
And also similar problems related to visibility (public/private) that were discussed in a different thread recently.
September 04, 2016
On 3 September 2016 at 21:24, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/3/2016 3:12 AM, Walter Bright wrote:
>>
>> If you are still determined to use it, you can use:
>>
>>    __traits(compiles, ...)
>>
>> like you would SFINAE in C++ to select which of the modules from the
>> argument
>> types selects a function that compiles.
>
>
> Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like:
>
> Something like:
>
> void foo(T,U)(T t, U u)
> {
>     alias func = ModuleOf!T.func;
>     alias func = ModuleOf!U.func;
>
>     func(t, u);
> }

And if either module doesn't have an instance of func?
September 04, 2016
On 3 September 2016 at 21:16, Walter Bright via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/3/2016 3:51 AM, Timon Gehr wrote:
>>
>> By default, name lookup does not work in a way that would allow you to
>> actually
>> extend types using UFCS, and therefore none of Phobos works that way.
>
>
> Lambdas!

This is exactly the difference between std.algorithm, and what I was trying to express as an algorithm that 'does work'. It's not the business of the API for the user to supply the work to do (ie, via lambda); the function is meant to do the work, which means it needs to call other functions. There are no lambdas to be seen in this situation.

> (Besides, I showed how other scopes can be imported based on a type, and then things can be looked up in those scopes, and UFCS applied.)

I still think that's unnecessarily complicated, and multiple arguments
leads to static if-ing and __traits(compiles,...). The fact the user
needs to intervene at all is already too much.
Apparently I need to stress again, this is a *core value proposition
of D*... It's presented as "this is modern D code", and yet it's
awkward and requires careful handling or you get hard to understand
name-resolution issues.
UFCS *is* modern D. Algorithms and ranges *is* modern D.
Seriously, this is the style that modern D aspires to, and it doesn't
'just work'. There should be ear-piercing alarms and flashing red
everywhere.
This comes up for me frequently, yet ADL has never caused me a single
moments trouble, in 15+ years. I didn't even know ADL existed until I
started running into this problem in D and then wondered to myself why
I never encountered the same issue in C++. It worked so seamlessly and
intuitively, I didn't even know it was there.

I don't care if the solution is ADL like C++, or something else that works, just that this problem is real; it's a massive fly in the ointment of modern D style, and I don't think it's acceptable. It needs a seamless solution, not manual intervention at every case. D depends on this so much more than C++ does.
September 04, 2016
On 3 September 2016 at 22:42, Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/3/16 1:51 AM, Manu via Digitalmars-d wrote:
>>
>> I've
>> never thought about this problem in C++, or had any problems with ADL.
>
>
> How do you swap two objects of a generic type that may or may not define its own swap? -- Andrei

It's not a problem I've ever had. I'm not actually quite sure I understand your question... I guess this is sort of like the issue I was alluding to in my other thread though; there exists a generic implementation, but some type requires to specialise for itself, which then needs to trump the otherwise ambiguous conflict with the catch-all? I think that's an interesting problem, but it's quite easy to solve with an (ugly) forwarding template; but that forwawrding template leads right back here, where the worker `doThingImpl()` as implemented for each type that supports 'doThing' depends on ADL to be callable from the master `doThing()` function.
September 04, 2016
On 3 September 2016 at 23:04, Andrei Alexandrescu via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 9/3/16 1:24 PM, Walter Bright wrote:
>>
>> On 9/3/2016 3:12 AM, Walter Bright wrote:
>>>
>>> If you are still determined to use it, you can use:
>>>
>>>    __traits(compiles, ...)
>>>
>>> like you would SFINAE in C++ to select which of the modules from the
>>> argument
>>> types selects a function that compiles.
>>
>>
>> Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like:
>>
>> Something like:
>>
>> void foo(T,U)(T t, U u)
>> {
>>     alias func = ModuleOf!T.func;
>>     alias func = ModuleOf!U.func;
>>
>>     func(t, u);
>> }
>
>
> This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- Andrei

Right, and it also has to not conflict with possible local
definitions, or instances supplied by imports in the local namespace.
Ie, the module where T came from is *an additional* place to look, not
*the* place to look.
I expect that local definitions may exist for things like generic
fallbacks, or primitive/builtin type implementations.
September 03, 2016
On 9/3/16 5:57 PM, Manu via Digitalmars-d wrote:
> On 3 September 2016 at 22:42, Andrei Alexandrescu via Digitalmars-d
> <digitalmars-d@puremagic.com> wrote:
>> On 9/3/16 1:51 AM, Manu via Digitalmars-d wrote:
>>>
>>> I've
>>> never thought about this problem in C++, or had any problems with ADL.
>>
>>
>> How do you swap two objects of a generic type that may or may not define its
>> own swap? -- Andrei
>
> It's not a problem I've ever had.

A problem you didn't know you have. It's a classic C++ conundrum combining theory and practice.

> I'm not actually quite sure I
> understand your question...

The task is to swap two objects of generic type T. If T's namespace defines an overload of swap with the appropriate signature, use it. Otherwise, fall back to std::swap.

For bonus points: if T defines swap as a _member_ function, use that.

> I guess this is sort of like the issue I
> was alluding to in my other thread though; there exists a generic
> implementation, but some type requires to specialise for itself, which
> then needs to trump the otherwise ambiguous conflict with the
> catch-all? I think that's an interesting problem, but it's quite easy
> to solve with an (ugly) forwarding template; but that forwawrding
> template leads right back here, where the worker `doThingImpl()` as
> implemented for each type that supports 'doThing' depends on ADL to be
> callable from the master `doThing()` function.

Post the code.


Andrei

September 03, 2016
On Saturday, 3 September 2016 at 14:05:11 UTC, Tobias M wrote:
> On Saturday, 3 September 2016 at 12:40:26 UTC, ZombineDev wrote:
>> No, LINQ doesn't work because of interfaces, but because of extension methods (C#'s variant of UFCS). The IEnumerable<T> interface defines only a single method. All the useful functionality is implemented as extension methods which are only available if the user specifically imports the namespace in which where they're defined (just like D's ranges and range primitive implementations for arrays). Those extension methods are used as a fallback, similarly to UFCS in D: every type can override the extension methods by implementing the method itself. Also more inner namespaces (more closer to the method invocation) override more outer namespaces.
>
> I know extension methods, that's not the point.
> The point is, that you cannot have a generic method like this in C#, it won't compile:
>
> class Bar
> {
>     void GenericMethod<T>(T arg)
>     {
>         arg.Foo();
>     }
> }
>
> Instead you need a constraint like this:
>
> interface IFoo
> {
>     void Foo();
> }
>
> class Bar
> {
>     void GenericMethod<T>(T arg) where T: IFoo
>     {
>         arg.Foo();
>     }
> }

No you're wrong. There's no need for interfaces or for generic constraints. It's not static vs duck typing. It's just a method lookup issue. See for yourself: http://rextester.com/GFKNSK99121

> Similarly for LINQ, you cannot just implement a generic "Sum" extension method for IEnumerable<T> that works for all T, because you cannot just use the + operator in that method. It is not defined on T if there are no respective constraints.
>
> Look at how it is implemented separately for every type T that supports +:
> https://msdn.microsoft.com/de-de/library/system.linq.enumerable.sum(v=vs.110).aspx

Sum is implemented in that stupid way, because unlike C++, in C# operators need to be implemented as static methods, so you can't abstract them with an interface. If they were instance methods, you could implement them outside of the class as extension methods and there would be no need to write a distinct method for each type. Here's an example: http://rextester.com/PQFPC46087
The only thing missing is syntax sugar to forward the '+' operator to 'Add' in my example.

I'm guessing that operator overloading was designed that way because: 1) they're worried about boxing and virtual call overhead 2) operator overloading was designed before generics (IIRC).
September 03, 2016
On 9/3/16 4:09 PM, Tobias M wrote:
> On Saturday, 3 September 2016 at 12:25:11 UTC, Andrei Alexandrescu wrote:
>> What problems are you referring to? -- Andrei
>
> The problems discussed here in the thread related to name lookup at
> template instantiation time.
> And also similar problems related to visibility (public/private) that
> were discussed in a different thread recently.

I see. This is a matter orthogonal to DbI - introspection should be able to figure out whether a member can be found, or a nonmember if the design asks for it. I wouldn't like "tricking" DbI into thinking a member is there when there isn't. -- Andrei

September 03, 2016
On Saturday, 3 September 2016 at 13:04:30 UTC, Andrei Alexandrescu wrote:
> On 9/3/16 1:24 PM, Walter Bright wrote:
>> On 9/3/2016 3:12 AM, Walter Bright wrote:
>>> If you are still determined to use it, you can use:
>>>
>>>    __traits(compiles, ...)
>>>
>>> like you would SFINAE in C++ to select which of the modules from the
>>> argument
>>> types selects a function that compiles.
>>
>> Eh, I realized it's simpler than that. Based on the code I already
>> presented, each argument can be used to generate an import for its
>> corresponding version of the function. Then, overloading rules apply and
>> it works. Something like:
>>
>> Something like:
>>
>> void foo(T,U)(T t, U u)
>> {
>>     alias func = ModuleOf!T.func;
>>     alias func = ModuleOf!U.func;
>>
>>     func(t, u);
>> }
>
> This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- Andrei


perhaps this:

auto adl(string fn, T, Args...)(auto ref T x, auto ref Args args){
    import std.traits : moduleName, hasMember;
    import std.meta : Filter, NoDuplicates, staticMap;
    import std.array : join;

    static if(hasMember!(T, fn)){
        mixin("return x." ~ fn ~ "(args);");
    }
    else{
        enum toImportString(T) = "import " ~ moduleName!(T) ~ " : " ~ fn ~ ";";
        enum hasModuleFN(T) = __traits(compiles, mixin("(){" ~ toImportString!T ~ "}"));
        alias Types = Filter!(hasModuleFN, NoDuplicates!(T, Args));

        static assert(Types.length, "no property '" ~ fn ~ "' for type '" ~ __traits(identifier, T)~ "'");

        mixin([staticMap!(toImportString, Types),"return " ~ fn ~ "(x, args);"].join("\n"));
    }
}