September 04, 2016
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?
September 04, 2016
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.
September 04, 2016
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.
September 04, 2016
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.
September 05, 2016
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 (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.

I understand these things, but D isn't C++ and modules aren't
namespaces. I think something that might appear ADL-like would look
different and be simpler in D. An algorithm that calls a function on
some T it receives just wants to look near the T; UFCS functions will
be there.
What you're effectively saying is they introduced ADL as a hack
because the language was impractical without it. I'm saying the same
situation exists in D right now. I don't think something ADL-like
would have the same extent of negative impact on D as it does in C++.
Modules are well-defined, and 'looking beside T for UFCS functions' is
a much narrower and more well defined search scope.


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

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.
September 05, 2016
On 5 September 2016 at 14:05, Manu <turkeyman@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.
September 05, 2016
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.
September 04, 2016
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.

September 05, 2016
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

September 05, 2016
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