Re: ADL

2016-09-06 Thread Guillaume Boucher via Digitalmars-d

On Monday, 5 September 2016 at 23:50:33 UTC, Timon Gehr wrote:
One hacky way is to provide a mixin template to create a 
wrapper type within each module that needs it, with 
std.typecons.Proxy. Proxy picks up UFCS functions in addition 
to member functions and turns them into member functions. But 
this leads to a lot of template bloat, because callers that 
share the same added UFCS functions don't actually share the 
instantiation. Also, it only works one level deep and 
automatically generated Wrapper types are generally prone to be 
somewhat brittle.


I don't think cloning a type just to add functionality can 
possibly be the right way.


A C++-style of customizing behavior is using traits. Those traits 
would be a compile time argument to the algorithm function.  
Instead of arg.addone() one would use trait.addone(arg).  It is 
not hard to write a proxy that merges trait and arg into one 
entity, but this should to be done from the callee.


The default trait would be type.addone_trait if it exists, or 
else some default trait that uses all available functions and 
member function from the module of the type.  In most of the 
cases this is enough, but it enables adding traits to existing 
types and also different implementations of the same traits.


This gets really bloaty in C++, and that's why usually ADL is 
preferred, but D has the capability to reduce the overhead to a 
minimum.


It doesn't quite make it possible to separate the implementation 
of types, algorithms and traits (UFCS) into different modules 
such that they don't know each other.  Either the user has to 
specify the trait each call or either the type's module or the 
algorithm's module has to import the traits.


What I call traits is very similar to type classes in other 
languages where (among other features) the traits are 
automatically being attached to the type.  (Type classes are also 
what C++ concepts originally wanted to be.)


Re: ADL

2016-09-05 Thread Timon Gehr via Digitalmars-d

On 05.09.2016 15:35, Andrei Alexandrescu wrote:

On 9/5/16 11:43 AM, Timon Gehr wrote:

On 05.09.2016 06:05, Manu via Digitalmars-d wrote:

An algorithm that calls a function on
some T it receives just wants to look near the T; UFCS functions will
be there.


I agree with your post except for this.

In general there could be four modules: one defines the type, one
defines the UFCS function, one defines the generic algorithm, and the
last one imports the former three. The problem is that D has no standard
way to make them work together.


Do you think there should?


Probably. This kind of pattern has been very successful in other languages.


And if so, what would the algorithm be? --
Andrei


This is a good question. I'm not sure yet what the best solution is.

One hacky way is to provide a mixin template to create a wrapper type 
within each module that needs it, with std.typecons.Proxy. Proxy picks 
up UFCS functions in addition to member functions and turns them into 
member functions. But this leads to a lot of template bloat, because 
callers that share the same added UFCS functions don't actually share 
the instantiation. Also, it only works one level deep and automatically 
generated Wrapper types are generally prone to be somewhat brittle.


There is always the default option of requiring the user to create the 
Wrappers manually, but that's a lot of boilerplate. (Other languages can 
do analogous things in a very streamlined fashion, by not supporting 
arbitrary template constraints.)


Re: ADL

2016-09-05 Thread deadalnix via Digitalmars-d
On Monday, 5 September 2016 at 13:35:02 UTC, Andrei Alexandrescu 
wrote:

On 9/5/16 11:43 AM, Timon Gehr wrote:

On 05.09.2016 06:05, Manu via Digitalmars-d wrote:

An algorithm that calls a function on
some T it receives just wants to look near the T; UFCS 
functions will

be there.


I agree with your post except for this.

In general there could be four modules: one defines the type, 
one
defines the UFCS function, one defines the generic algorithm, 
and the
last one imports the former three. The problem is that D has 
no standard

way to make them work together.


Do you think there should? And if so, what would the algorithm 
be? -- Andrei


I ran into that very problem many time, and I think it is 
legitimate.


Typical use case is a type from module A that is extended by 
module B to conform to whatever typeclass is desired (a range for 
instance) via UFCS. It is not possible to pass it down to 
anything generic expecting that typeclass is not passed down.


However, it is not clear what the solution should be. Tweaking 
the identifier resolution would make template instance dependent 
on the instantiation point, which is a non starter (it would 
cause an explosion is both resources required to compile and a 
huge amount a binary bloat).


On the other hand, I was wondering if it is possible to create a 
wrapper type type, with alias this, that is dependent on the 
scope, and that do UFCS resolution is that scope. One could then 
use the template like :


myalgorithm(forwardUFCS(var));

With var the vairable of the type extended to fit the typeclass. 
I suspect this can be done with a fair amount of mixin magic, but 
I'm not 100% sure.


This way, forwarding the instancier scope would only be done on 
demand, which would mitigate the instanciation explosion problem 
one face when doing it as identifier resolution level.


Re: ADL

2016-09-05 Thread Walter Bright via Digitalmars-d

On 9/5/2016 6:34 AM, Andrei Alexandrescu wrote:

ADL would not apply here because it looks up only names in the same module as
the type.


It would work in C++ because any piece of code can insert more names into any 
namespace. Inserting names into a namespace violates about every principle of 
encapsulation I can think of, and it just ain't worth it.




It doesn't sound like a good idea. This kind of lookup that collects names from
various places and holds an auction is fraught with Byzantine failures. Good
lookup is regular and predictable.


I agree.


Re: ADL

2016-09-05 Thread Andrei Alexandrescu via Digitalmars-d

On 9/5/16 4:41 PM, Jacob Carlborg wrote:

On 2016-09-05 15:28, Andrei Alexandrescu wrote:


Yah, make front a member please. It's in the same module so you're not
breaking any encapsulation anyway. -- Andrei


I just said:

"I thought one of the reasons for UFCS was to be able to make a type
support the range interface without modifying the type" [1].

And you replied:

"That is correct" [2].

And now you're saying that it should be a member?


That's the path of last resistance.


What if it's in the
different module?


D does not support one module to expand a type defined in another module 
with 100% transparency.



Or as it is for the built-in arrays, not possible to
add a member there.


That pattern is only possible with the restrictions and limitations of 
std.array.




Andrei


Re: ADL

2016-09-05 Thread Jacob Carlborg via Digitalmars-d

On 2016-09-05 15:28, Andrei Alexandrescu wrote:


Yah, make front a member please. It's in the same module so you're not
breaking any encapsulation anyway. -- Andrei


I just said:

"I thought one of the reasons for UFCS was to be able to make a type
support the range interface without modifying the type" [1].

And you replied:

"That is correct" [2].

And now you're saying that it should be a member? What if it's in the 
different module? Or as it is for the built-in arrays, not possible to 
add a member there.


[1] http://forum.dlang.org/post/nqjbu4$1i7h$1...@digitalmars.com
[2] http://forum.dlang.org/post/nqjcj6$1j3b$1...@digitalmars.com

--
/Jacob Carlborg


Re: ADL

2016-09-05 Thread Andrei Alexandrescu via Digitalmars-d

On 9/5/16 11:43 AM, Timon Gehr wrote:

On 05.09.2016 06:05, Manu via Digitalmars-d wrote:

An algorithm that calls a function on
some T it receives just wants to look near the T; UFCS functions will
be there.


I agree with your post except for this.

In general there could be four modules: one defines the type, one
defines the UFCS function, one defines the generic algorithm, and the
last one imports the former three. The problem is that D has no standard
way to make them work together.


Do you think there should? And if so, what would the algorithm be? -- Andrei


Re: ADL

2016-09-05 Thread Andrei Alexandrescu via Digitalmars-d

On 9/5/16 11:25 AM, Jacob Carlborg wrote:

On 2016-09-05 11:06, Andrei Alexandrescu wrote:


That is correct (and btw the example should use the member call syntax).
But touching a type's module is modifying the type. -- Andrei


Not sure what that has to do with anything.

Example:

module foo;

struct Foo
{
int[] array = [1];
}

int front(Foo foo)
{
return foo.array[0];
}


Yah, make front a member please. It's in the same module so you're not 
breaking any encapsulation anyway. -- Andrei




Re: ADL

2016-09-05 Thread Marc Schütz via Digitalmars-d
On Saturday, 3 September 2016 at 11:24:01 UTC, 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);
}


Can we use a `with` statement? E.g. something along those lines

void foo(T, U, alias context = __CURRENT_MODULE__)(T t, U u) {
with(context)
return func(t, u);
}


Re: ADL

2016-09-05 Thread Ethan Watson via Digitalmars-d

On Monday, 5 September 2016 at 01:00:26 UTC, Walter Bright wrote:

What about using this template?


Sure, it'll work assuming the module imports all its symbols 
publically, but it's still not as usable as it should be. I still 
need to invoke it for a number of things, including member 
variable types.


If the member variable is templated, I need to analyse the 
template arguments for types to import them too.


If it is a function, I need to treat each argument as I treat a 
member variable.


I started a thread the other day that touches on another problem 
I have which this template won't solve: 
https://forum.dlang.org/thread/wggldyzrbwjboibin...@forum.dlang.org


At least in my use cases, it comes down to the template instance 
not inheriting the visibility of symbols from its template 
parameters. Which leads to these workarounds.


We're aiming for the goal of sub-second compile and reload times 
for rapid iteration, both with normal code and scripter code. 
Anything I have to do through templates and CTFE slows compile 
times down, in some cases significantly.


Re: ADL

2016-09-05 Thread Timon Gehr via Digitalmars-d

On 05.09.2016 06:05, Manu via Digitalmars-d wrote:

An algorithm that calls a function on
some T it receives just wants to look near the T; UFCS functions will
be there.


I agree with your post except for this.

In general there could be four modules: one defines the type, one 
defines the UFCS function, one defines the generic algorithm, and the 
last one imports the former three. The problem is that D has no standard 
way to make them work together.


Re: ADL

2016-09-05 Thread Lodovico Giaretta via Digitalmars-d
On Monday, 5 September 2016 at 08:17:15 UTC, Andrei Alexandrescu 
wrote:

Are we in agreement about the baseline solution?


Yes, but there are a bunch of cases in which the baseline 
solution is not applicable.


Disclaimer: I don't know how C++ would handle the following 
situation.


Let's say I use a library that exposes a type T. Of course the 
library does not expose a range interface for it. So I create a 
module t_range, which provides free range functions for type T. 
This is akin to what Phobos does for arrays, whose range 
functions are in std.array.


Now I want to use std.algorithm on T, as I would use it on 
arrays. But I can't, because the only reason std.algorithm works 
on arrays is because it imports std.array. But of course it 
cannot import my module t_range.


What I'd like as a solution is that the template does not only 
look in its module, but also in the instantiating module, to 
resolve symbols dependent on the template types. So std.algorithm 
should not import std.array, but should work on arrays if the 
instantiating module imports std.array, and should work on T if 
the instantiating module imports t_range, which is a sound 
behaviour that would not surprise anyone.


Incidentally, this would also solve the problem of emplacing 
objects with private or package constructors and probably also 
some of the current discussions about visibility of private 
members in templates (private members would be visible only if 
they are visible at the instantiation site)


So templates would work as if they were mixed-in the 
instantiating module, and they would work as if the user 
explicitly wrote that piece of code specialized for its case 
directly in the usage site (which is what templates are for: 
avoiding to write every possible specialization).


I know this is a big breaking change that will probably never 
happen, but I think it would be an interesting scenario and would 
open lots of possibilities. What do you think?


[OT] local overloading (Was: Re: ADL)

2016-09-05 Thread Timon Gehr via Digitalmars-d

On 05.09.2016 02:50, Walter Bright wrote:

On 9/4/2016 2:36 PM, Timon Gehr wrote:

Declare-call ordering issues for overload sets are not limited to
local scopes.
This problem needs to be solved anyway. The fact that the scope is
local adds
exactly zero additional complications.


I know that static if brings with it ordering problems. That's not a
justification for adding them to statements.
...


I didn't suggest to do that. The sequence can be:

1. Fix ordering problems for overload sets generically.
2. Allow local overloads.

or

1. Allow local overloads with some additional restrictions ensuring no 
ordering problems.

2. Fix ordering problem for overload sets generically.
3. Remove additional restrictions for local overloads.





Besides, I showed a method of how the overloads could be done with the
existing language.

That's not the point. What's perhaps more telling is that you
initially got it
wrong. It /wants/ to be valid code.


Maybe, but if I redesigned the language for every mistake I made,
nothing would get done.
...


The mistake is arguably in the language design here. (Lack of turtles.)


My point with all this is ADL-workalike behavior can be reasonably done
with existing D core features available *now* in all 3 compilers. It
means we don't have to panic and rewrite the compiler right now - Manu
can use these techniques and get his work done, even though it isn't
quite what he envisions. He's not dead in the water.


Yup.


Re: ADL

2016-09-05 Thread Jacob Carlborg via Digitalmars-d

On 2016-09-05 11:06, Andrei Alexandrescu wrote:


That is correct (and btw the example should use the member call syntax).
But touching a type's module is modifying the type. -- Andrei


Not sure what that has to do with anything.

Example:

module foo;

struct Foo
{
int[] array = [1];
}

int front(Foo foo)
{
return foo.array[0];
}

module algo;

void algorithm(Range)(Range range)
{
auto e = range.front; // Error: no property 'front' for type 'Foo'
}

module main

import foo;
import algo;

void main()
{
algorithm(Foo());
}

--
/Jacob Carlborg


Re: ADL

2016-09-05 Thread Andrei Alexandrescu via Digitalmars-d

On 9/5/16 10:55 AM, Jacob Carlborg wrote:

I thought one of the reasons for UFCS was to be able to make a type
support the range interface without modifying the type.


That is correct (and btw the example should use the member call syntax). 
But touching a type's module is modifying the type. -- Andrei


Re: ADL

2016-09-05 Thread Jacob Carlborg via Digitalmars-d

On 2016-09-05 10:17, Andrei Alexandrescu wrote:


Let me make sure I understand it. The core structure is this:

=
module bob;
struct S {}
void f(S s);

module myalgorithm;
void test(T)(T t)
{
  f(t);
}
=

The core issue here is that f is not considered for lookup. It is a free
function in the same module as S. That's not a frequent case and it
seems right to not support it in the lookup rules.

The simplest solution, which has already been discussed, is to make f a
member of S. It is important that does not affect modularity; all
protection in D has module-level granularity, so the premise of
http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197.
So it's for the most part a clerical matter: move the body of f inside
S, or if f is already generic define an alias for it inside of S.

This is the baseline solution, and it is reasonable. This needs to be
properly understood before we look into any others: by a simple
mechanical intervention, everything works properly. Every language has
such minute needs for minor scaffolding.

It must also be understood that changing the lookup rules to make this
scaffolding unnecessary bring with them a host of unpleasant consequences.

Are we in agreement about the baseline solution?


I thought one of the reasons for UFCS was to be able to make a type 
support the range interface without modifying the type.


--
/Jacob Carlborg


Re: ADL

2016-09-05 Thread Andrei Alexandrescu via Digitalmars-d

On 9/5/16 10:17 AM, Andrei Alexandrescu wrote:

so the premise of
http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197


... "does not apply". -- Andrei



Re: ADL

2016-09-05 Thread Andrei Alexandrescu via Digitalmars-d

On 9/5/16 6:23 AM, Manu via Digitalmars-d wrote:

But the point of my post is that
I feel the problem is of very high importance.


Let me make sure I understand it. The core structure is this:

=
module bob;
struct S {}
void f(S s);

module myalgorithm;
void test(T)(T t)
{
  f(t);
}
=

The core issue here is that f is not considered for lookup. It is a free 
function in the same module as S. That's not a frequent case and it 
seems right to not support it in the lookup rules.


The simplest solution, which has already been discussed, is to make f a 
member of S. It is important that does not affect modularity; all 
protection in D has module-level granularity, so the premise of 
http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197. 
So it's for the most part a clerical matter: move the body of f inside 
S, or if f is already generic define an alias for it inside of S.


This is the baseline solution, and it is reasonable. This needs to be 
properly understood before we look into any others: by a simple 
mechanical intervention, everything works properly. Every language has 
such minute needs for minor scaffolding.


It must also be understood that changing the lookup rules to make this 
scaffolding unnecessary bring with them a host of unpleasant consequences.


Are we in agreement about the baseline solution?


Andrei



Re: ADL

2016-09-04 Thread Walter Bright via Digitalmars-d

On 9/4/2016 9:23 PM, Manu via Digitalmars-d wrote:

I already worked-around my problems. But the point of my post is that
I feel the problem is of very high importance. I don't think the
situation is okay, since we're making design recommendations that lead
straight to these problems. And these modern D design patterns are the
thing in D I'm most excited about, and keen to share with
not-yet-D-users.


Try the solutions I proposed - they aren't the ones you have been using. Give 
'em a chance!


As pointed out, C++ ADL is an awkward feature with ugly corner cases. If we add 
it to D, we'll be forevermore stuck with that. The library solutions presented 
here do work, and don't suffer from those problems.




Re: ADL

2016-09-04 Thread Manu via Digitalmars-d
On 5 September 2016 at 10:50, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:
> On 9/4/2016 2:36 PM, Timon Gehr wrote:
>>
>> Declare-call ordering issues for overload sets are not limited to local
>> scopes.
>> This problem needs to be solved anyway. The fact that the scope is local
>> adds
>> exactly zero additional complications.
>
>
> I know that static if brings with it ordering problems. That's not a
> justification for adding them to statements.
>
>
>>> Besides, I showed a method of how the overloads could be done with the
>>> existing language.
>>
>> That's not the point. What's perhaps more telling is that you initially
>> got it
>> wrong. It /wants/ to be valid code.
>
>
> Maybe, but if I redesigned the language for every mistake I made, nothing
> would get done.
>
> My point with all this is ADL-workalike behavior can be reasonably done with
> existing D core features available *now* in all 3 compilers. It means we
> don't have to panic and rewrite the compiler right now - Manu can use these
> techniques and get his work done, even though it isn't quite what he
> envisions. He's not dead in the water.

I already worked-around my problems. But the point of my post is that
I feel the problem is of very high importance. I don't think the
situation is okay, since we're making design recommendations that lead
straight to these problems. And these modern D design patterns are the
thing in D I'm most excited about, and keen to share with
not-yet-D-users.


Re: ADL

2016-09-04 Thread Manu via Digitalmars-d
On 5 September 2016 at 14:05, Manu <turkey...@gmail.com> wrote:
>
> I've seen this one. Again, we're not talking about C++. It hasn't been
> explored (to my knowledge) how a similar mechanism it would look and
> affect D. I suspect (with no evidence) it would be relatively benign
> by comparison to the problems ADL introduces to C++, and D stands to
> gain a lot more from the transaction, ie, UFCS will work in generic
> functions the same as non-generic functions. D requires this much more
> than C++ does, particularly when you take this as the direction of
> forward momentum for D design.

Anyway, I've made my case. I'm watching to see where this goes. I
don't really have anything to add, so I'll let it be from here.


Re: ADL

2016-09-04 Thread Manu via Digitalmars-d
On 4 September 2016 at 07:38, Andrei Alexandrescu via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:
> On 9/3/16 11:31 AM, Manu via Digitalmars-d wrote:
>>>
>>> > In any case, these difficulties are the consequence of trying to write
>>> > C++
>>> > code in D.
>>
>> You've told me this before, and I find it kind of offensive every time
>> you do, since I've been programming D for like 7 years now, and I
>> definitely don't code D like C++.
>> If anything, I have a strong tendency to code C++ like D, and that has
>> lead to a lot of interesting changes in my C++ style.
>>
>> I'm tired of these sorts of dismissals. You insist that I'm not a
>> 'real' D programmer, or something to that effect.
>
>
> There's really no need to take offense here. We have a bit of a mimosa
> culture of easily bruised egos it seems. As the joke goes: "You take offense
> too easily." "I can't believe you just said that!"
>
> It's hard to not see your view as coming straight from the "I want to speak
> Italian by replacing English words with Italian words" box.
>
> * It hypothesizes that one entire style of design is completely impossible
> because it lacks one obscure feature from C++.

And you did it again.
It's the insistence that I'm trying to write C++ in D that's really
annoying. (it's not the first time)

I'm writing textbook modern D code. It's a style of design that is
absolutely impossible in C++, ergo, I'm writing D code!
The thing I'm trying to present is that it doesn't work intuitively.
The whole reason I came here to comment is because I realised that D's
central modern design pattern, that is, algorithms + UFCS (ie,
pipeline style; *impossible in C++*) doesn't quite work properly, and
it can be hard to understand why.
UFCS needs to 'work', not 'sometimes work'.

Let's forget I ever said C++ or ADL. I only raised it because I
wondered why this wasn't a problem in C++, and then realised that ADL
existed in C++.


> * Vast evidence to the contrary is ignored.

To the contrary of what? That UFCS from within a template doesn't work
in a practical way? Nobody has presented any solution or workaround
that doesn't require manual user intervention.
A solution like: `adl(string fn, T, Args...)(auto ref T x, auto ref
Args args)` has appeared on the table.
I find it hard to accept criticism for my opinion when that's the
proposed solution. That is a clear demonstration of the problem, not a
solution.

The logical conclusion of this is that all algorithm implementations
have to call all functions this way if you want UFCS to work the way
users would expect... but users would not expect to call functions in
this way either.


> * The fact that the feature is problematic and controversial even within C++
> circles is also neglected.

I haven't commented in ADL's merits in C++, just that it's the
mechanism by which C++ doesn't exhibit this (similar) problem which is
much more significant in D than it is in C++. D needs a solution
otherwise UFCS appears a weak language offering where it breaks down
when used in algorithm functions, and I quite strongly resist the idea
that the best solution is manual user intervention.


> * The lack of said feature is regarded as an utter disaster and no
> workaround is even close to cutting the mustard.

I do think it's an absolute disaster. It's a core premise of modern D,
algorithms and UFCS are central. I'd almost say they *are* modern D.
I don't think core language offerings and design recommendations
should require 'work arounds', that sounds like something that's just
broken by design. You're right, that doesn't cut the mustard, and I'd
be extremely disappointed to accept that as 'solved'.


> * A language change is a must; no library solution would ever be acceptable.

Library solution implies user-intervention. My argument is that I find
the notion of user intervention in this case unacceptable. I think
it's too fundamental to require work-arounds.
I'm happy to be proven wrong, but I'm not gonna concede on that point easily.


> For the most part this is a Pop a level up and figure what the needed
> accomplishment is, so other routes than the ADL bottleneck are possible.
> Let's approach this together like a problem that has a solution within D,
> and let's make that solution accessible comfortably.

Sure... but that doesn't mean I'm just going to accept the proposed
workarounds as 'fixed'. I find the proposed workarounds really
unpleasant, and I don't want to see that proliferate. I'd rather
resist an idea I hate now, than see it take root.
If I back off, and they become a thing, then who do I have to blame?


> Two apocryphal anecdotes: Koenig invented the eponymous lookup as a
> consequence to the fact that namespaces broke the "Hello, world" program
> (som

Re: ADL

2016-09-04 Thread Walter Bright via Digitalmars-d

On 9/4/2016 1:48 PM, Ethan Watson wrote:

Chipping in to say that I currently do something this with Binderoo templates...
and it sucks.


What about using this template?

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

and it only has to be written once. Yes, it uses CTFE and mixins, but it's all 
hidden away inside the template.


Re: ADL

2016-09-04 Thread Walter Bright via Digitalmars-d

On 9/4/2016 2:36 PM, Timon Gehr wrote:

Declare-call ordering issues for overload sets are not limited to local scopes.
This problem needs to be solved anyway. The fact that the scope is local adds
exactly zero additional complications.


I know that static if brings with it ordering problems. That's not a 
justification for adding them to statements.




Besides, I showed a method of how the overloads could be done with the
existing language.

That's not the point. What's perhaps more telling is that you initially got it
wrong. It /wants/ to be valid code.


Maybe, but if I redesigned the language for every mistake I made, nothing would 
get done.


My point with all this is ADL-workalike behavior can be reasonably done with 
existing D core features available *now* in all 3 compilers. It means we don't 
have to panic and rewrite the compiler right now - Manu can use these techniques 
and get his work done, even though it isn't quite what he envisions. He's not 
dead in the water.


Re: ADL

2016-09-04 Thread Timon Gehr via Digitalmars-d

On 04.09.2016 22:22, Walter Bright wrote:

On 9/4/2016 5:30 AM, Andrei Alexandrescu wrote:

Might be a sensible enhancement. Removing artificial limitations is good
programming language design. Turtles! -- Andrei


The design of executable function bodies is very much "declare before
use", quite unlike at the declaration levels which is all "order is not
relevant". Changing this will have consequences (such as our discussion
of exactly when a declaration becomes valid),


The rule should be the same as for module-level functions. Note that the 
rules for module-level functions are currently inadequate:


pragma(msg, foo(0)); // calls double overload
static if(foo(0)){ // calls double overload
bool foo(int x){ return false; }
}
bool foo(double x){ return true; }
pragma(msg, foo(0)); // calls int overload

Declare-call ordering issues for overload sets are not limited to local 
scopes. This problem needs to be solved anyway. The fact that the scope 
is local adds exactly zero additional complications.



and I just feel that
function code is just easier to understand if "declare before use" is
the rule,


Overloading does not violate declare before use. You seem to be 
conflating it with forward referencing.



because that is how we reason about how it is executed.

Besides, I showed a method of how the overloads could be done with the
existing language.


That's not the point. What's perhaps more telling is that you initially 
got it wrong. It /wants/ to be valid code.


Re: ADL

2016-09-04 Thread Ethan Watson via Digitalmars-d
On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright 
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);
}


Chipping in to say that I currently do something this with 
Binderoo templates... and it sucks.


https://github.com/Remedy-Entertainment/binderoo/blob/master/binderoo_client/d/src/binderoo/variabledescriptor.d

One example is in there, the VariableDescriptors eponymous 
template, where a template that collects every member variable of 
an object has to mix in the module names of each encountered 
member variable type to stop the compiler complaining about 
module visibility. So I'm doing the double whammy of taxing the 
template expansion engine and the CTFE engine. It could be that 
switching it to a mixin template (and working out someway to make 
it as usable as eponymous templates) will solve the problem - but 
the way this codebase is going it's going to mean every template 
needs to be a mixin.


Surely the base template system can be more flexible than this?


Re: ADL

2016-09-04 Thread Walter Bright via Digitalmars-d

On 9/4/2016 5:30 AM, Andrei Alexandrescu wrote:

Might be a sensible enhancement. Removing artificial limitations is good
programming language design. Turtles! -- Andrei


The design of executable function bodies is very much "declare before use", 
quite unlike at the declaration levels which is all "order is not relevant". 
Changing this will have consequences (such as our discussion of exactly when a 
declaration becomes valid), and I just feel that function code is just easier to 
understand if "declare before use" is the rule, because that is how we reason 
about how it is executed.


Besides, I showed a method of how the overloads could be done with the existing 
language.


Re: ADL

2016-09-04 Thread Walter Bright via Digitalmars-d

On 9/4/2016 5:31 AM, Andrei Alexandrescu wrote:

On 9/4/16 3:26 AM, Walter Bright wrote:

On 9/3/2016 4:46 PM, Andrei Alexandrescu wrote:

On 9/4/16 12:28 AM, Walter Bright wrote:

On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:

On 9/3/16 10:43 PM, Walter Bright wrote:

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


What's an elegant way to encapsulate this as a stutter-free one-liner?
-- Andrei


using a template and mixing it in


"Show me the mo^H^Hcode."


template foo(T)
{
static if (traits(compiles, ModuleOf!T.func))
foo = "alias func = " ~ ModuleOf!T.func ~ ";";
else
foo = "";
}

mixin(foo!T);


Actually "func" i.e. the function's name must be a paraneter. No? -- Andrei


Probably. The point is, the tools to do this are available features of D.


Re: ADL

2016-09-04 Thread Andrei Alexandrescu via Digitalmars-d

On 9/4/16 3:26 AM, Walter Bright wrote:

On 9/3/2016 4:46 PM, Andrei Alexandrescu wrote:

On 9/4/16 12:28 AM, Walter Bright wrote:

On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:

On 9/3/16 10:43 PM, Walter Bright wrote:

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


What's an elegant way to encapsulate this as a stutter-free one-liner?
-- Andrei


using a template and mixing it in


"Show me the mo^H^Hcode."


template foo(T)
{
static if (traits(compiles, ModuleOf!T.func))
foo = "alias func = " ~ ModuleOf!T.func ~ ";";
else
foo = "";
}

mixin(foo!T);


Actually "func" i.e. the function's name must be a paraneter. No? -- Andrei


Re: ADL

2016-09-04 Thread Andrei Alexandrescu via Digitalmars-d

On 9/4/16 2:36 AM, Timon Gehr wrote:

On 03.09.2016 13:24, Walter Bright wrote:


Something like:

void foo(T,U)(T t, U u)
{
alias func = ModuleOf!T.func;
alias func = ModuleOf!U.func;

func(t, u);
}


Does not work. Local overloads are not supported.


Might be a sensible enhancement. Removing artificial limitations is good 
programming language design. Turtles! -- Andrei


Re: ADL

2016-09-04 Thread Walter Bright via Digitalmars-d

On 9/3/2016 10:57 PM, ZombineDev wrote:

What do you think about making overloading and UFCS work with local symbols?


I'd rather not. Let's make what we have work. There's an unending demand for new 
features in the core language.



> There are workarounds, but nothing pretty.

I don't regard this as a workaround. It's how things were designed to work in D. 
The fact that it isn't the minimum number of characters doesn't make it a 
workaround.


Re: ADL

2016-09-04 Thread ZombineDev via Digitalmars-d

On Sunday, 4 September 2016 at 01:34:47 UTC, Walter Bright wrote:

On 9/3/2016 5:36 PM, Timon Gehr wrote:

Does not work. Local overloads are not supported.


Yeah, you're right, should have tested that:

  void abc(int);
  void def(uint);

  void foo()
  {
alias func = abc;
alias func = def; // error

  func(1);
  }

fails. Pushing it out a level works:

  void abc(int);
  void def(uint);

  template foo()
  {
alias func = abc;
alias func = def;

void foo()
{
func(1);
}
  }

  void main()
  {
foo();
  }


What do you think about making overloading and UFCS work with 
local symbols? There are workarounds, but nothing pretty.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 5:36 PM, Timon Gehr wrote:

Does not work. Local overloads are not supported.


Yeah, you're right, should have tested that:

  void abc(int);
  void def(uint);

  void foo()
  {
alias func = abc;
alias func = def; // error

  func(1);
  }

fails. Pushing it out a level works:

  void abc(int);
  void def(uint);

  template foo()
  {
alias func = abc;
alias func = def;

void foo()
{
func(1);
}
  }

  void main()
  {
foo();
  }


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 4:46 PM, Andrei Alexandrescu wrote:

On 9/4/16 12:28 AM, Walter Bright wrote:

On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:

On 9/3/16 10:43 PM, Walter Bright wrote:

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


What's an elegant way to encapsulate this as a stutter-free one-liner?
-- Andrei


using a template and mixing it in


"Show me the mo^H^Hcode."


template foo(T)
{
static if (traits(compiles, ModuleOf!T.func))
foo = "alias func = " ~ ModuleOf!T.func ~ ";";
else
foo = "";
}

mixin(foo!T);



Re: ADL

2016-09-03 Thread Timon Gehr via Digitalmars-d

On 03.09.2016 13:24, Walter Bright wrote:


Something like:

void foo(T,U)(T t, U u)
{
alias func = ModuleOf!T.func;
alias func = ModuleOf!U.func;

func(t, u);
}


Does not work. Local overloads are not supported.


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/4/16 12:28 AM, Walter Bright wrote:

On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:

On 9/3/16 10:43 PM, Walter Bright wrote:

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


What's an elegant way to encapsulate this as a stutter-free one-liner?
-- Andrei


using a template and mixing it in


"Show me the mo^H^Hcode."


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 3:35 AM, Walter Bright wrote:

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


That statement is incorrect. It's used as a fallback in C++. I had forgotten.



Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:

On 9/3/16 10:43 PM, Walter Bright wrote:

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


What's an elegant way to encapsulate this as a stutter-free one-liner? -- Andrei


using a template and mixing it in


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 11:31 AM, Manu via Digitalmars-d wrote:

> In any case, these difficulties are the consequence of trying to write C++
> code in D.

You've told me this before, and I find it kind of offensive every time
you do, since I've been programming D for like 7 years now, and I
definitely don't code D like C++.
If anything, I have a strong tendency to code C++ like D, and that has
lead to a lot of interesting changes in my C++ style.

I'm tired of these sorts of dismissals. You insist that I'm not a
'real' D programmer, or something to that effect.


There's really no need to take offense here. We have a bit of a mimosa 
culture of easily bruised egos it seems. As the joke goes: "You take 
offense too easily." "I can't believe you just said that!"


It's hard to not see your view as coming straight from the "I want to 
speak Italian by replacing English words with Italian words" box.


* It hypothesizes that one entire style of design is completely 
impossible because it lacks one obscure feature from C++.


* Vast evidence to the contrary is ignored.

* The fact that the feature is problematic and controversial even within 
C++ circles is also neglected.


* The lack of said feature is regarded as an utter disaster and no 
workaround is even close to cutting the mustard.


* A language change is a must; no library solution would ever be acceptable.

For the most part this is a Pop a level up and figure what the needed 
accomplishment is, so other routes than the ADL bottleneck are possible. 
Let's approach this together like a problem that has a solution within 
D, and let's make that solution accessible comfortably.


Two apocryphal anecdotes: Koenig invented the eponymous lookup as a 
consequence to the fact that namespaces broke the "Hello, world" program 
(something to do with cout and endl being migrated to namespace std), 
which surprised the inner circles of C++ the way putting mentos in coke 
surprises the unwary. Namespaces are an unusually poorly designed 
feature of C++, not second even to exceptions. By that time significant 
work had been invested in defining and implementing namespaces, so going 
back wasn't quite an option. ADL was hailed as a horrible hack but a 
lifesaving one. Compilers have been slow to implement ADL, partly 
because essentially it meant breaking a lookup algorithm that was 
relatively orderly and DWIM, and replace it with a patchwork of special 
cases. The community has since learned to live with the odd idioms that 
make code work with the new rules.


Second anecdote: Scott Meyers had an infamous talk on C++ in which the 
leitmotif was "What does f(x) mean?" Scott asks the question in the 
beginning to which the obvious response is "Call function f passing it 
argument x". By the time his talk is done, the horrified audience 
realizes they have no idea what f(x) means and where it goes.



Andrei



Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 9:24 AM, Andrei Alexandrescu wrote:

On 9/3/16 5:57 PM, Manu via Digitalmars-d wrote:

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.



One thing I've noticed in my years of programming and helping others with their 
code is that one can use a feature for decades, over and over, but are only 
using it in a certain specific way. One is lulled into thinking that it works in 
the general case because one is not aware that one's usage is constrained.


Someone else comes along, uses it slightly differently, and oops.

"But I always took the left fork!" :-)

It bites me all the time.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 6:22 AM, ZombineDev wrote:

I agree that it's not a template issue. It's more of a modules vs namespaces
issue. I think the lack of ADL is not a problem in C# because everyone can (and
everyone does) extend an existing namespace, so most user's of LINQ algorithms
just slap a `using System.Linq` on top of the file without caring in exactly
which file the functionality they're using is coming from.


Hmm, that explanation could be it. I'm not very familiar with LINQ.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 8:51 AM, Manu via Digitalmars-d wrote:

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.


This is purely a stylistic issue. I, on the other hand, think lambdas are a 
superior design, as ADL lookups never sat well with me because it can be quite 
difficult for the user to figure out where 'func' is coming from.




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


I already showed how your previous objection to this could be folded away inside 
a nice template.




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.


As mentioned, I don't see anything hard to understand about it - it makes it 
clear to the reader where names are coming from. ADL pulls names in from who 
knows where.




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.


You've used ADL for 15 years. You're obviously very comfortable with it. I 
suggest trying out using lambdas enough to feel comfortable with it before 
deciding that ADL is better. New coding styles are rarely comfortable right off 
the bat.




Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 10:43 PM, Walter Bright wrote:

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


What's an elegant way to encapsulate this as a stutter-free one-liner? 
-- Andrei


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:

And if either module doesn't have an instance of func?


static if (traits(compiles, ModuleOf!T.func))
alias func = ModuleOf!T.func;


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 10:29 PM, Walter Bright wrote:

On 9/3/2016 6:04 AM, Andrei Alexandrescu wrote:

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



That's where __traits(compiles, ...) comes in. It can be encapsulated in
another template.


Yah. That we need to plant in the standard library. -- Andrei



Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 9:01 AM, Manu via Digitalmars-d wrote:

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.


1. 'alias func = ...;' also works to bring in local definitions to an even 
footing with the other alias func statements.


2. Due to recent changes to import lookup rules, imports are searched if locals 
do not satisfy the lookup.


3. You can use traits(compiles, ...) to not insert the names in the local scope 
if it already can be looked up.


So I believe you're good to go.


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 7:38 PM, Tobias Müller wrote:

Andrei Alexandrescu  wrote:

On 9/3/16 7:08 PM, Tobias M wrote:

On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:

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


The problem I see with DbI is rather that the user of a function thinks
that an optional constraint is satisfied, while in reality it isn't, due
to a non-obvious lookup/visibility problem.


At some point there's a need to RTFM. -- Andrei


Is there one for DbI? (Sincere question)


Not yet. We have the allocators body of work, but that's too niche to 
serve as a general example. I think std.experimental.checkedint will be 
the canonical example on how to do DbI. I'll propose a blog post to 
Mike. -- Andrei




Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 6:04 AM, Andrei Alexandrescu wrote:

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



That's where __traits(compiles, ...) comes in. It can be encapsulated in another 
template.




Re: ADL

2016-09-03 Thread Tobias Müller via Digitalmars-d
ZombineDev <petar.p.ki...@gmail.com> wrote:
> So what? C#'s generics are less flexible than C++ and D templates.
> The point is that C#'s lookup does not consider only the 
> implemented interfaces, but also falls back to extensions 
> methods. If C# had ADL,
> the compiler would also look for extension methods in the 
> namespace
> of the type (in non-generic methods, when the type is "known"), 
> although the user of the type may not have imported the namespace.

ADL wouldn't change anything if you don't cast to a specific type, and if
you do, that part of the code is not generic anymore.

>>> 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.
>> 
>> With runtime reflection you can do almost anything... That's 
>> circumventing the type system and doesn't disprove anything.
> 
> There's no circumventing the type system. `typeof(obj)` is barely 
> even reflection. You can do this with regular cast or using the 
> `is` expression (http://rextester.com/CXGNK69048). I used 
> `typeof` just because it could yield better performance.

Typecasting *is* circumventing the type system.




Re: ADL

2016-09-03 Thread ZombineDev via Digitalmars-d

On Saturday, 3 September 2016 at 17:05:35 UTC, Tobias M wrote:

On Saturday, 3 September 2016 at 16:32:16 UTC, ZombineDev wrote:
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


Ok, Interfaces and other generic methods with compatible 
constraints.
But in the end you cannot do much without any interface 
constraints except writing out to the console as you do in the 
example.


But the main point still holds, name lookup is only done at 
definition time, not at instantiation time. That's why you can 
only call generic methods. Overloads don't work.


So what? C#'s generics are less flexible than C++ and D templates.
The point is that C#'s lookup does not consider only the 
implemented interfaces, but also falls back to extensions 
methods. If C# had ADL,
the compiler would also look for extension methods in the 
namespace
of the type (in non-generic methods, when the type is "known"), 
although the user of the type may not have imported the namespace.


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.


With runtime reflection you can do almost anything... That's 
circumventing the type system and doesn't disprove anything.


There's no circumventing the type system. `typeof(obj)` is barely 
even reflection. You can do this with regular cast or using the 
`is` expression (http://rextester.com/CXGNK69048). I used 
`typeof` just because it could yield better performance.


I mean, it even "works" for types that cannot be added at all, 
by just returning a default value...


? Sorry, I don't understand, what's the problem?


Re: ADL

2016-09-03 Thread Tobias Müller via Digitalmars-d
Andrei Alexandrescu  wrote:
> On 9/3/16 7:08 PM, Tobias M wrote:
>> On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:
>>> 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
>> 
>> The problem I see with DbI is rather that the user of a function thinks
>> that an optional constraint is satisfied, while in reality it isn't, due
>> to a non-obvious lookup/visibility problem.
> 
> At some point there's a need to RTFM. -- Andrei

Is there one for DbI? (Sincere question)



Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 7:08 PM, Tobias M wrote:

On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:

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


The problem I see with DbI is rather that the user of a function thinks
that an optional constraint is satisfied, while in reality it isn't, due
to a non-obvious lookup/visibility problem.


At some point there's a need to RTFM. -- Andrei


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 7:00 PM, vit wrote:

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)


Perhaps too surgical (although nice to have as an option). We need 
something that pulls the symbol for all purposes. -- Andrei




Re: ADL

2016-09-03 Thread Tobias Müller via Digitalmars-d
Tobias M  wrote:
> On Saturday, 3 September 2016 at 16:32:16 UTC, ZombineDev wrote:
>> 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.
> 
> With runtime reflection you can do almost anything... That's 
> circumventing the type system and doesn't disprove anything.
> I mean, it even "works" for types that cannot be added at all, by 
> just returning a default value...

It's not runtime reflection, sorry about that.

But Add claims to be generic but it's actually just a list of special
cases.
It compiles for all types but only works for some. And even worse,
for types that actually do support addition but are not in the list it
silently does the wrong thing.

You cannot do the same in a truly generic way.




Re: ADL

2016-09-03 Thread Tobias M via Digitalmars-d

On Saturday, 3 September 2016 at 16:32:16 UTC, ZombineDev wrote:
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


Ok, Interfaces and other generic methods with compatible 
constraints.
But in the end you cannot do much without any interface 
constraints except writing out to the console as you do in the 
example.


But the main point still holds, name lookup is only done at 
definition time, not at instantiation time. That's why you can 
only call generic methods. Overloads don't work.


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.


With runtime reflection you can do almost anything... That's 
circumventing the type system and doesn't disprove anything.
I mean, it even "works" for types that cannot be added at all, by 
just returning a default value...


Re: ADL

2016-09-03 Thread Tobias M via Digitalmars-d
On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei 
Alexandrescu wrote:
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


The problem I see with DbI is rather that the user of a function 
thinks that an optional constraint is satisfied, while in reality 
it isn't, due to a non-obvious lookup/visibility problem.


Re: ADL

2016-09-03 Thread vit via Digitalmars-d
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"));

}
}



Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

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




Re: ADL

2016-09-03 Thread ZombineDev via Digitalmars-d

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 
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 arg)
{
arg.Foo();
}
}

Instead you need a constraint like this:

interface IFoo
{
void Foo();
}

class Bar
{
void GenericMethod(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 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).


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

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



Re: ADL

2016-09-03 Thread Manu via Digitalmars-d
On 3 September 2016 at 23:04, Andrei Alexandrescu via Digitalmars-d
 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.


Re: ADL

2016-09-03 Thread Manu via Digitalmars-d
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.


Re: ADL

2016-09-03 Thread Manu via Digitalmars-d
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.


Re: ADL

2016-09-03 Thread Manu via Digitalmars-d
On 3 September 2016 at 21:24, Walter Bright via Digitalmars-d
 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?


Re: ADL

2016-09-03 Thread Tobias M via Digitalmars-d
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.


Re: ADL

2016-09-03 Thread Tobias M via Digitalmars-d

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 
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 arg)
{
arg.Foo();
}
}

Instead you need a constraint like this:

interface IFoo
{
void Foo();
}

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

Similarly for LINQ, you cannot just implement a generic "Sum" 
extension method for IEnumerable 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


Re: ADL

2016-09-03 Thread Stefan Koch via Digitalmars-d

On Saturday, 3 September 2016 at 10:11:05 UTC, Timon Gehr wrote:


If ADL is done as a fallback, then it is only slower in those 
cases where it is either actually used, or 
__traits(compiles,...) is used to determine that some function 
overload does not exist.


True.
Still it does complicate the implementation.

AFAICS the point of ADL is importing function definitions 
automatically if they are referenced, thereby practically 
circumventing the guarantees imports give you.


In particular : "I will only import what is in that module, and I 
will only transitively import what is imported publicly by this 
module"


now it becomes : "I will import what is in that module, 
transitively import public imports, and maybe more if a function 
called from that module requires it."


Please do correct me if my interpretation is wrong.
I haven't heard of adl before this post.


Re: ADL

2016-09-03 Thread ZombineDev via Digitalmars-d
On Saturday, 3 September 2016 at 10:33:22 UTC, 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.



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.


I agree that it's not a template issue. It's more of a modules vs 
namespaces issue. I think the lack of ADL is not a problem in C# 
because everyone can (and everyone does) extend an existing 
namespace, so most user's of LINQ algorithms just slap a `using 
System.Linq` on top of the file without caring in exactly which 
file the functionality they're using is coming from.


Personally I'm glad that D doesn't have ADL because ADL can make 
it very hard to find which overload is called.


Re: ADL

2016-09-03 Thread Jacob Carlborg via Digitalmars-d

On 2016-09-03 13:16, Walter Bright wrote:


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!


So, something like this:

module foo;

struct Foo {}
int front(Foo f);
void popFront(Foo f);
bool empty(Foo f);

module algo;

void algorithm(alias front, alias popFront, alias empty, T)(T t);

module user;

import foo;
import algo;

void main()
{
Foo f;
algorithm!(() => f.front, () => f.popFront(), () => f.empty)(f);
}

--
/Jacob Carlborg


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

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




Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 12:14 PM, 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.


Yah, but I don't see this as an issue. The non-members would need to be 
in the same module even with ADL, so it's just a clerical matter. -- Andrei




Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 12:03 PM, Timon Gehr wrote:

On 03.09.2016 11:37, Manu via Digitalmars-d wrote:

On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:

On 9/3/2016 1:37 AM, Walter Bright wrote:


I thought #4 in particular was rather cool, I plan to use it as an
example.



https://github.com/dlang/phobos/pull/4762


Complexity ramps up further if there are N arguments to the algorithm.
It needs to search each of the arguments modules.



template adl(string fun){ /* TODO */ }

adl!"foo"(S.init,T.init);


Nice, yah, that kinds of stuff. -- Andrei


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 3:09 AM, Walter Bright wrote:
[snip]

What would be really nice is to allow ADL easily and without fuss when 
needed. On Manu's example:


module bob;
struct S {}
void f(S s);

module joe;
struct T {}
void f(T t);

module myalgorithm;
void test(T)(T t)
{
  mixin(adl!(T, "f"));
  f(t);
}

So adl!(T, "f") expands to an import of f from T's module if it defines 
a function f, or nothing if it doesn't.


Generally I agree that there's more upside to not introducing ADL for D.


Andrei



Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

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




Re: ADL

2016-09-03 Thread ZombineDev via Digitalmars-d

On Saturday, 3 September 2016 at 10:56:20 UTC, Tobias M wrote:
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.


No, LINQ doesn't work because of interfaces, but because of 
extension methods (C#'s variant of UFCS). The IEnumerable 
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. For more info see:


1) 
https://github.com/ljw1004/csharpspec/blob/gh-pages/expressions.md#member-lookup Member Lookup
2) 
https://github.com/ljw1004/csharpspec/blob/gh-pages/expressions.md#member-access Member access and
3) 
https://github.com/ljw1004/csharpspec/blob/gh-pages/expressions.md#extension-method-invocations Extension method invocations


Re: ADL

2016-09-03 Thread Andrei Alexandrescu via Digitalmars-d

On 9/3/16 10:01 AM, Tobias Müller wrote:

On Friday, 2 September 2016 at 23:51:35 UTC, Manu wrote:

This pattern seems to bite me every direction I turn when trying to
write range or algorithm style code. C++ has ADL, and ADL works. I've
never thought about this problem in C++, or had any problems with ADL.


IMO the root of this problem is that templates are *duck typed*. All
those problems wouldn't even exist with concepts/traits/typeclasses
(done right).
ADL is only an ugly hack.

And with "Design by Introspection" it only gets worse: If an optional
operation exists, but is not found because of unexpected problems like
these, it still compiles but you only get limited functionality or bad
performance.


What problems are you referring to? -- Andrei



Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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);
}


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: ADL

2016-09-03 Thread Tobias M via Digitalmars-d
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.


Re: ADL

2016-09-03 Thread Timon Gehr via Digitalmars-d

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.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: ADL

2016-09-03 Thread John Colvin via Digitalmars-d

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?


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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.


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

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




Re: ADL

2016-09-03 Thread Timon Gehr via Digitalmars-d

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.




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++?


Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 2:37 AM, Manu via Digitalmars-d wrote:

On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:

On 9/3/2016 1:37 AM, Walter Bright wrote:


I thought #4 in particular was rather cool, I plan to use it as an
example.



https://github.com/dlang/phobos/pull/4762

> Complexity ramps up further if there are N arguments to the algorithm.
> It needs to search each of the arguments modules.

I suggest posting the actual problems you're having, because twice now you've 
gotten solutions to the problems you posted, then said they weren't your actual 
problems.



This template:

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

can be used to qualify any function with the module in which one expects to 
find it.


> Complexity ramps up further if there are N arguments to the algorithm.
> It needs to search each of the arguments modules.

Bluntly, if a library is designed around multi-argument ADL as a core 
requirement, redesign it. I.e. the same advice as for multiple inheritance. It's 
just not worth it.


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.




Re: ADL

2016-09-03 Thread Timon Gehr via Digitalmars-d

On 03.09.2016 03:12, Stefan Koch wrote:

On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright wrote:


Essentially, ADL has awkward problems when getting beyond the simple
cases. It isn't right for D.


I could not agree more strongly!

If this feature were supported, it would probably break our module system.


Break how?


Even if we could shoehorn it into the language it would make the
compiler slower.


If ADL is done as a fallback, then it is only slower in those cases 
where it is either actually used, or __traits(compiles,...) is used to 
determine that some function overload does not exist.


Re: ADL

2016-09-03 Thread Timon Gehr via Digitalmars-d

On 03.09.2016 11:37, Manu via Digitalmars-d wrote:

On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:

On 9/3/2016 1:37 AM, Walter Bright wrote:


I thought #4 in particular was rather cool, I plan to use it as an
example.



https://github.com/dlang/phobos/pull/4762


Complexity ramps up further if there are N arguments to the algorithm.
It needs to search each of the arguments modules.



template adl(string fun){ /* TODO */ }

adl!"foo"(S.init,T.init);


Re: ADL

2016-09-03 Thread Manu via Digitalmars-d
On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d
 wrote:
> On 9/3/2016 1:37 AM, Walter Bright wrote:
>>
>> I thought #4 in particular was rather cool, I plan to use it as an
>> example.
>
>
> https://github.com/dlang/phobos/pull/4762

Complexity ramps up further if there are N arguments to the algorithm.
It needs to search each of the arguments modules.


Re: ADL

2016-09-03 Thread Manu via Digitalmars-d
On 3 September 2016 at 11:09, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:
>
> First solution:
>
>module bob;
>struct S {
>void f();
>}

This is my current workaround.
I'm not happy with it at all. UFCS exists for a reason.


> Second solution:
>
> module user_code;
> import bob, joe;
> import myalgorithm;
>
> mixin myalgorithm.test!S;
> mixin myalgorithm.test!T;
>
> void main()
> {
>   test(S.init);
>   test(T.init);
> }

You're not serious, right?
I just want to call a function... functions should be functions, not
mixin templates!


> Third solution:
>
> module myalgorithm;
> void test(M,T)(T t)
> {
> M.f(t);
> }
>
> module user_code;
> import bob, joe;
> import myalgorithm;
>
> void main()
> {
>   test!bob(S.init);
>   test!joe(T.init);
> }

Another crazy workaround.
Users should not be expected to manually pass scope's around the place
to perform a name lookup.
Try and explain that to a normal programmer.


> 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_.


>> or had any problems with ADL
>
> https://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism
>
> Essentially, ADL has awkward problems when getting beyond the simple cases.
> It isn't right for D.

D requires ADL so much more than C++ does, because the things ADL does
in C++ are absolute concrete advertised core value propositions of D.


> ADL has the problems I provided a link to.

It's never caused me a problem, in like, 15 years or more. This
situation in D causes me problems all the time.


> In any case, these difficulties are the consequence of trying to write C++
> code in D.

You've told me this before, and I find it kind of offensive every time
you do, since I've been programming D for like 7 years now, and I
definitely don't code D like C++.
If anything, I have a strong tendency to code C++ like D, and that has
lead to a lot of interesting changes in my C++ style.

I'm tired of these sorts of dismissals. You insist that I'm not a
'real' D programmer, or something to that effect.

This problem consistently arises when I try to commit to go all in on
ranges+UFCS pipeline style programming. There's nothing C++-ey about
that. C++ can barely do it. It is the advertised mission of modern D
programmers to write code this way, and it's the exact area where the
problem I'm trying to express breaks down.

The interesting part is, when I do try and code this way in C++ (which
is brutal, it's all SFINAE based template constraints and stuff), it
actually _works_, simply because ADL works.

Pipeline programming is a core value proposition for D, as are
uber-powerful templates which leads to making algorithms out of
everything.


> None of the algorithms used in std.algorithm or elsewhere in
> Phobos have this particular issue. Nor have I seen ADL supported in any
> other language, despite many supporting generic algorithms.

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
t

Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/3/2016 1:37 AM, Walter Bright wrote:

I thought #4 in particular was rather cool, I plan to use it as an example.


https://github.com/dlang/phobos/pull/4762



Re: ADL

2016-09-03 Thread Walter Bright via Digitalmars-d

On 9/2/2016 9:14 PM, Manu via Digitalmars-d wrote:

They're not solutions though, they're workarounds. They're all
problematic, and highly unsavoury.


What makes them problematic or highly unsavory? I thought #4 in particular was 
rather cool, I plan to use it as an example.




Nobody is gonna go "oh, i really wish i could do those things in
C++!", because the problem is already solved :/


ADL has the problems I provided a link to.

In any case, these difficulties are the consequence of trying to write C++ code 
in D. None of the algorithms used in std.algorithm or elsewhere in Phobos have 
this particular issue. Nor have I seen ADL supported in any other language, 
despite many supporting generic algorithms.


I do understand trying to write C++ code in D, because my early FORTRAN programs 
looked like BASIC, my early C programs looked like FORTRAN, my C++ code looked 
like C, etc.


What I have provided is a generic way to make ADL work in D, which shows how 
adaptable it is.


Re: ADL

2016-09-03 Thread Tobias Müller via Digitalmars-d

On Friday, 2 September 2016 at 23:51:35 UTC, Manu wrote:
This pattern seems to bite me every direction I turn when 
trying to
write range or algorithm style code. C++ has ADL, and ADL 
works. I've
never thought about this problem in C++, or had any problems 
with ADL.


IMO the root of this problem is that templates are *duck typed*. 
All those problems wouldn't even exist with 
concepts/traits/typeclasses (done right).

ADL is only an ugly hack.

And with "Design by Introspection" it only gets worse: If an 
optional operation exists, but is not found because of unexpected 
problems like these, it still compiles but you only get limited 
functionality or bad performance.


Re: ADL

2016-09-02 Thread Manu via Digitalmars-d
On 3 September 2016 at 11:25, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:
> On 9/2/2016 6:12 PM, Stefan Koch wrote:
>>
>> If this feature were supported, it would probably break our module system.
>> Even if we could shoehorn it into the language it would make the compiler
>> slower.
>
>
> Note that C++ needs ADL in part because it cannot do options 2, 3 or 4.

They're not solutions though, they're workarounds. They're all
problematic, and highly unsavoury.
Nobody is gonna go "oh, i really wish i could do those things in
C++!", because the problem is already solved :/


Re: ADL

2016-09-02 Thread Walter Bright via Digitalmars-d

On 9/2/2016 6:12 PM, Stefan Koch wrote:

If this feature were supported, it would probably break our module system.
Even if we could shoehorn it into the language it would make the compiler 
slower.


Note that C++ needs ADL in part because it cannot do options 2, 3 or 4.


Re: ADL

2016-09-02 Thread Stefan Koch via Digitalmars-d
On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright 
wrote:


Essentially, ADL has awkward problems when getting beyond the 
simple cases. It isn't right for D.


I could not agree more strongly!

If this feature were supported, it would probably break our 
module system.
Even if we could shoehorn it into the language it would make the 
compiler slower.


Re: ADL

2016-09-02 Thread Walter Bright via Digitalmars-d

On 9/2/2016 4:51 PM, Manu via Digitalmars-d wrote:

(I should have given the example with test() outside the namespace)


It's always best to provide an example of the actual problem rather than 
something else.



module bob;
struct S {}
void f(S s);

module joe;
struct T {}
void f(T t);

module myalgorithm;
void test(T)(T t)
{
  f(t);
}


module user_code;
import bob, joe;

import myalgorithm;  // needed

void main()
{
  test(S.init);
  test(T.init);
}

This is a better example. I can't be invading test() with any aliases,
or imports. It wouldn't be an algorithm anymore if I did that.
This pattern seems to bite me every direction I turn when trying to
write range or algorithm style code. C++ has ADL, and ADL works. I've
never thought about this problem in C++,



First solution:

   module bob;
   struct S {
   void f();
   }

Second solution:

module user_code;
import bob, joe;
import myalgorithm;

mixin myalgorithm.test!S;
mixin myalgorithm.test!T;

void main()
{
  test(S.init);
  test(T.init);
}

Third solution:

module myalgorithm;
void test(M,T)(T t)
{
M.f(t);
}

module user_code;
import bob, joe;
import myalgorithm;

void main()
{
  test!bob(S.init);
  test!joe(T.init);
}

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);
}

> or had any problems with ADL

https://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism

Essentially, ADL has awkward problems when getting beyond the simple cases. It 
isn't right for D.


Re: ADL

2016-09-02 Thread Manu via Digitalmars-d
On 3 September 2016 at 08:38, Walter Bright via Digitalmars-d
<digitalmars-d@puremagic.com> wrote:
> On 9/2/2016 5:15 AM, Manu via Digitalmars-d wrote:
>>
>> In C++, there is this ADL thing (argument dependent lookup).
>
>
> Yeah, I know about Koening lookup. It was a hack added to C++ to make
> operator overloading work.

Naturally you do, and I'm sure that is why it was invented, but you
couldn't write any modern C++ without it. The reason you say it was
invented is not the reason that it's useful.


>> D doesn't seem to have this,
>
>
> That's right, and it's on purpose :-)

And that seems to be a rather big problem.

The situation is this:
In D, it is ***EXTREMELY*** common to have some argument of type T,
like, basically everything in D is a template these days... we're
talking ranges and stuff.
It is also considered un-cool in modern D to aggregate all
functionality into types themselves. We want functionality for T to be
extensible, so we use UFCS all over the place.

Template args combined with UFCS practically demand ADL or something
similar to ADL, otherwise you can't really write algorithms. It's
impossible to import all the appropriate sources into the file that
implements the algorithm. They're unrelataed, except that the
algorithm is expected to 'work' on the T it's given. So if someone
supplies a T to your algorithm, and it's a range for instance (or
something following that pattern), and some part of it's
implementation is UFCS, it all falls apart :/

We can't have the situation "UFCS works quite nicely... in this
particular subset of common situations".


>> and that is proving to be quite problematic. What's the work around?
>
>
> Not a workaround, as D does not need ADL. This is how to do it:
>
> extern (C++, bob) {
>   struct S {}
>   void f(S s);
> }
>
> extern (C++, joe) {
>   struct T {}
>   void f(T t);
>
>   void test()
>   {
> T t;
> f(t); // obviously works, T is in the local namespace
>
> alias f = bob.f;  // bring bob.s into current scope
> bob.S s;
> f(s);  // no problemo
>   }
> }
>
>
> The 'alias' construct gives good control over which symbols are visible in
> which scopes.

Now to put it in the terms I describe above, 'test()' is an algorithm,
implemented in a module unrelated to bob or joe (I should have given
the example with test() outside the namespace)... the algorithm
implementation can't go aliasing or importing anything relating to its
possible arguments T or S; it's meant to be generic.

module bob;
struct S {}
void f(S s);

module joe;
struct T {}
void f(T t);

module myalgorithm;
void test(T)(T t)
{
  f(t);
}


module user_code;
import bob, joe;
void main()
{
  test(S.init);
  test(T.init);
}

This is a better example. I can't be invading test() with any aliases,
or imports. It wouldn't be an algorithm anymore if I did that.
This pattern seems to bite me every direction I turn when trying to
write range or algorithm style code. C++ has ADL, and ADL works. I've
never thought about this problem in C++, or had any problems with ADL.


  1   2   >