September 03, 2016
On 9/3/2016 2:31 AM, Manu via Digitalmars-d wrote:
>> Fourth solution:
>>
>>     module myalgorithm;
>>
>>     void test(T)(T t)
>>     {
>>         import std.traits;
>>         mixin("import " ~ std.traits.moduleName!T ~ ";");
>>         mixin("alias M = " ~ std.traits.moduleName!T ~ ";");
>>         // The above could be encapsulated into an eponymous template
>>         // that takes T as a parameter and returns the alias
>>
>>         M.f(t);
>>     }
>>
>> What makes them problematic or highly unsavory? I thought #4 in particular
>> was rather cool, I plan to use it as an example.
>
> I also had this idea as workaround, but you can't seriously think this is okay?
> Importing an entire module at the point I want to call a function is crazy.
> I don't want to import _everything_ from T's module into my local
> namespace; that could easily lead to conflicting names in the local
> scope which would now require disambiguation.
> This surely represents a far higher probability of name collisions
> than the theoretical accidental collision that could come from ADL.
> The ADL style collision isn't accidental though, that's _the whole point_.

// Find module in which T was defined
template ModuleOf(alias T)
{
    import std.traits : moduleName;
    mixin("import " ~ moduleName!T ~ ";");
    mixin("alias ModuleOf = " ~ moduleName!T ~ ";");
}

The import is scoped inside ModuleOf, and so doesn't cause collisions.

Besides,

    import foo : bar;

only imports the symbol 'bar' from 'foo', no matter how many symbols there are in 'foo'.


> Write an algorithm that does _work_, rather than does algorithm logic,
> and you can't miss this problem. You need to call associated functions
> to do work.

Why does no other language adopt ADL? ADL has been around in C++ for 25 years at least. Or maybe I missed that it does exist in other languages?

(The usual way I've seen associated functions made available to generic algorithms is via alias parameters or lambdas that enclose the calls to those functions.)

September 03, 2016
On 9/3/2016 3:14 AM, Timon Gehr wrote:
> On 03.09.2016 10:37, Walter Bright wrote:
>> None of the algorithms used in std.algorithm or elsewhere in Phobos have
>> this particular issue.
>
> Yes they do. It is not possible to implement the range functions as non-members.

It's done for arrays via std.array.


>> Nor have I seen ADL supported in any other
>> language, despite many supporting generic algorithms.
>
> Which other such languages have templates like D or C++?

I don't think it is a template issue. It's a name lookup issue. There's LINQ in C#, for example.
September 03, 2016
On Saturday, 3 September 2016 at 09:31:59 UTC, Manu wrote:
> std.algorithm is extremely simple, it doesn't do anything except raw algorithm-ey stuff. It doesn't attempt to invoke functionality on the data it's working on.
>
> Right now I'm working on image processing. There are lots of image
> data types, and they all have things like interpolation and blending
> functions. Write an image processing algorithm that calls out to lerp
> or blend, and you'll run into these problems instantly.
> I was writing some audio software some time back, again, trying to use
> stream processing extensively because it's a perfect match for that
> workload, but same problem!
>
> Write an algorithm that does _work_, rather than does algorithm logic, and you can't miss this problem. You need to call associated functions to do work.

I have had problems with not having C++ style ADL before, but in the end I'm much happier without it.

At the risk of repeating previous posts, Is it specifically this?

auto someAlgorithm(T0, T1)(T0 arg0, T1 arg1)
{
    // do some work
    blend(arg0, arg1);
    // more work
}

And you want some way for the author of the types being passed in to define an overload of blend somewhere else that that someAlgorithm is not explicitly aware of? What possible lookup rules would you want to make that work? I can think of a lot of different schemes depending on what you want/need for the situation. D goes the simple, conservative route by default and just looks in progressively wider scopes, which is uncontroversial and good enough in most cases. More complicated cases can be done with various other methods, all of which will then necessarily involve some explicit choice (e.g. passing the scope, passing the function, importing scope etc...) in order to know which one you're using. This seems to me to be a good thing?

P.s. your ordinary programmer argument: can you imagine that same ordinary programmer understanding how to properly use and avoid abusing C++ lookup rules?
September 03, 2016
On 9/3/2016 3:11 AM, Timon Gehr wrote:
> If ADL is done as a fallback, then it is only slower in those cases where it is
> either actually used,


That isn't how it works in C++. It's done right up front in finding the candidates for overloading, not as a fallback.

Given Manu's other posts where he wants a generic template to be used as the fallback, I don't think ADL as a fallback will work for him, either.
September 03, 2016
On 9/3/2016 2:31 AM, Manu via Digitalmars-d wrote:
> std.algorithm is extremely simple,

That's actually a nice compliment! (Though credit for that goes to Andrei, not me.)


> it doesn't do anything except raw
> algorithm-ey stuff. It doesn't attempt to invoke functionality on the
> data it's working on.

Sure it does, usually via a lambda passed to it.

Recall that C++ ADL predates C++ lambdas by more than a decade, which may explain why C++ has come to rely on ADL.
September 03, 2016
On 03.09.2016 12:33, Walter Bright wrote:
> On 9/3/2016 3:14 AM, Timon Gehr wrote:
>> On 03.09.2016 10:37, Walter Bright wrote:
>>> None of the algorithms used in std.algorithm or elsewhere in Phobos have
>>> this particular issue.
>>
>> Yes they do. It is not possible to implement the range functions as
>> non-members.
>
> It's done for arrays via std.array.
> ...

This is not at all relevant when talking about 'this particular issue' that Manu brought up. std.range and std.algorithm import std.array.

>
>>> Nor have I seen ADL supported in any other
>>> language, despite many supporting generic algorithms.
>>
>> Which other such languages have templates like D or C++?
>
> I don't think it is a template issue. It's a name lookup issue.

It's both. ADL is mostly useless outside of generic code. It's mostly about how templates specify what interface they require and how the requirements are satisfied by the caller. ADL is a workaround for the lack of a convenient enough such protocol in templates. Other approaches to generics solve this particular issue quite elegantly. (E.g. type classes implicitly pass along the required free-function functionality.) D's templates don't, this is why it is a template issue.

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.

Note that I'm not saying ADL should be implemented, but the problem it addresses is real, and it exists in D.

> There's LINQ in C#, for example.

C# does not have templates.
September 03, 2016
On Saturday, 3 September 2016 at 10:33:22 UTC, Walter Bright wrote:
> I don't think it is a template issue. It's a name lookup issue. There's LINQ in C#, for example.

I think it is.

The problem is lookup of dependent symbols (see C++ two phase lookup). Without real templates, all lookup can be done at definition time.
I'm not very familiar with LINQ, but generally C# uses uses interfaces as constraints on generics, similar to traits/type classes. Lookup is then done once, considering only the interfaces, not for each the concrete type.
September 03, 2016
On 9/3/2016 2:31 AM, Manu via Digitalmars-d wrote:
> I'm tired of these sorts of dismissals. You insist that I'm not a
> 'real' D programmer, or something to that effect.

Not at all. No insult was intended. People often find better, more D idiomatic ways of writing code and tell me I need to change my style to use it.

For example, there was a long discussion about the right way to use @trusted in templates a while back.

The way I had considered "best practice" was inferior, and I simply could no longer defend it and adopted the way others had developed.

Just because I have been writing D longer than anyone doesn't mean I automatically am imbued with wisdom of the best way to do things. We're all learning. The way I write D code constantly evolves.

If we're going to find the best way to do things in D, we're going to have to be willing to check our pride at the door and be willing to reexamine any assumption and practice, no matter how long it has been in use.

My purpose in participating in this thread is to help you be successful using D, not denigrate you. I'm fully aware that I'm often rather artless in the way I make points, social skills are one of my challenges. So I ask you to be tolerant of my shortcomings in that area, and try to look past it.
September 03, 2016
On 9/3/2016 3:51 AM, Timon Gehr wrote:
>> I don't think it is a template issue. It's a name lookup issue.
> It's both. ADL is mostly useless outside of generic code.

It was initially justified as a solution for operator overloading, which has no necessary relationship to templates or generic programming.


> It's mostly about how
> templates specify what interface they require and how the requirements are
> satisfied by the caller. ADL is a workaround for the lack of a convenient enough
> such protocol in templates. Other approaches to generics solve this particular
> issue quite elegantly. (E.g. type classes implicitly pass along the required
> free-function functionality.) D's templates don't, this is why it is a template
> issue.
>
> 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!

(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.)


>> There's LINQ in C#, for example.
> C# does not have templates.

It has generics:

  https://msdn.microsoft.com/en-us/library/512aeb7t.aspx

and iterators and algorithms and LINQ.
September 03, 2016
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);
}