September 05, 2016
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
September 05, 2016
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
September 05, 2016
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
September 05, 2016
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.
September 05, 2016
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?
September 05, 2016
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.
September 05, 2016
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/wggldyzrbwjboibinuwj@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.
September 05, 2016
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);
    }
September 05, 2016
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

September 05, 2016
On 9/5/16 11:39 AM, Lodovico Giaretta wrote:
> 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.

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

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

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.

To implement the range interface for a type, provide a wrapper. KISS. D is powerful and that has the danger of making people entitled to ask for clever solutions to any problem there is, but at some point it just comes down to write some code pulp.


Andrei