September 05, 2016
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
September 05, 2016
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
September 05, 2016
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
September 05, 2016
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.
September 05, 2016
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.
September 06, 2016
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.)
September 06, 2016
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.)
1 2 3 4 5 6 7 8 9 10 11
Next ›   Last »