June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to KennyTM~ | KennyTM~ wrote:
>
> Maybe better
>
> auto ref opDispatch(string name, T...)(auto ref T args) {
> mixin("return ." ~ name ~ "(this, args);");
> }
>
> so that ref-returns and ref-parameters can be handled as well. Doesn't work for 'lazy' though. It also cannot preserve 'pure'-ity, 'nothrow'-ness and '@safe'-ty of the original function.
Yes, but this is an unresolved problem of the language in general. We can have the template mixin just mixin all 8 versions with different template constraints until that gets fixed (thats what will be done anyways, at least purity is reflected in the mangled name AFAIK). The most important feature needed to make it work correctly, "auto ref" is already there.
Timon
| |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to KennyTM~ | 2011/6/6 KennyTM~ <kennytm@gmail.com>:
> Maybe better
>
> auto ref opDispatch(string name, T...)(auto ref T args) {
> mixin("return ." ~ name ~ "(this, args);");
> }
>
> so that ref-returns and ref-parameters can be handled as well. Doesn't work for 'lazy' though. It also cannot preserve 'pure'-ity, 'nothrow'-ness and '@safe'-ty of the original function.
>
>
As far as I understand, auto ref is supposed to mean "always ref, and accept r-values". So we would need to allow tuples to contain ref types and construct it from the target function.
Torarin
| |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Michel Fortin | On Mon, 06 Jun 2011 15:32:38 -0400, Michel Fortin <michel.fortin@michelf.com> wrote: > On 2011-06-06 15:00:13 -0400, "Steven Schveighoffer" <schveiguy@yahoo.com> said: > >> And it does indeed work (2.053)... >> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities. >> The opDispatch could even be a mixin itself (I think). >> What do you think? > > Clever. But how does it work for properties? Pure/safe/nothrow functions? Ref and out parameters? Note that properties specifically are already problem for the compiler-implemented array-member syntax. I think properties can be done, but we can't do it by marking the function @property (which would mean something different to the compiler). Something like this might work: struct ufcs { @property auto opDispatch(string s)() // TODO: appropriate constraint { mixin("return ."prop_" ~ s ~ "(this);"); } @property auto opDispatch(string s, T)(T arg) // TODO: appropriate constraint { mixin("return ."prop_" ~ s ~ "(this, arg);"); } } It's very sketchy, you need to appropriately name your global functions, because naming them with @property doesn't really work. Custom annotations could work well here. As far as pure/safe/nothrow, I think templates need general work in this area anyways. IFTI in particular is woefully inadequate for wrapping functions. I've proposed an enhancement to assist in this in some cases, perhaps it can be tweaked to allow all sorts of "forwarding" behavior. Essentially, I think you should be able to use another function to guide IFTI in deciding what parameter types to use. http://d.puremagic.com/issues/show_bug.cgi?id=4998 > > Bottom line: there's a lot of work to do to make UFCS work right. And it'll require some language-level changes anyway if we want it to work right. > Yes, but I also think if we can make UFCS optional, we give some power back to the author of the struct/class. He can choose not to participate in ufcs, and then he can control the API to his type. Plus, this can be a good interim step! It works right now! -Steve | |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Monkol | Am 06.06.2011 21:38, schrieb Monkol:
> On Mon, 06 Jun 2011 22:00:13 +0300, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>
>> Someone wrote a very compelling argument for ufcs (uniform function
>> call syntax) for ranges, and that is, given a slew of range functions,
>> and a slew of ranges, it is nice to use a fluent programming syntax to
>> specify wrappers for ranges without having to extend each range type.
>> For example:
>>
>> take(10,stride(2,cycle([3,2,5,3])));
>>
>> vs.
>>
>> [3,2,5,3].cycle().stride(2).take(10);
>>
>> And I thought damn it would be nice if ranges could implement ufcs,
>> but other types that you didn't want to allow infinite extendability
>> could avoid it. That gave me an idea :)
>>
>>
>> import std.stdio;
>>
>> struct ufcs
>> {
>> auto opDispatch(string name, T...)(T args) // appropriate if compiles
>> constraint here
>> {
>> mixin("return ." ~ name ~ "(this, args);");
>> }
>> }
>>
>> int foo(ufcs x, int y)
>> {
>> writefln("it works! %d", y);
>> return y+1;
>> }
>>
>> void main()
>> {
>> ufcs u;
>> auto x = u.foo(1);
>> assert(x == 2);
>> }
>>
>> And it does indeed work (2.053)...
>>
>> So we can have ufcs without any changes to the compiler, and we also
>> make it a *choice* for people who don't want to allow infinite
>> extendability, and don't want to deal with possible compiler ambiguities.
>>
>> The opDispatch could even be a mixin itself (I think).
>>
>> What do you think?
>>
>> -Steve
>
>
> what this code must to do?
opDispatch is special template member a class or struct can have. When using the dot-operator to acces members and these aren't there, opDispatch!"memmbername" is tried.
Now when ou try to use uniform function call syntax (UFC) on a struct, the function-as-method you want to use is not in the struct but in the module-scope. The compiler therefore can't find any memeber and tries opDispatch which itself tries to resolve to a module-scope function using the leading dot. Through ct-string-operations and mixin the name of the function gets injected into the code.
I hope I could help you.
Mafi
| |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer wrote:
> Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type. For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it. That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
> auto opDispatch(string name, T...)(T args) // appropriate if compiles
> constraint here
> {
> mixin("return ." ~ name ~ "(this, args);");
> }
> }
>
> int foo(ufcs x, int y)
> {
> writefln("it works! %d", y);
> return y+1;
> }
>
> void main()
> {
> ufcs u;
> auto x = u.foo(1);
> assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
> -Steve
Nice and clever! However, I don't think it's good as an alternative for ufcs in the language, unless that is going to be dropped because of too many ambiguities.
ufcs should imho be a decision on the caller side or the side of the function extending a datatype (like extension methods in C#), not the implementor of a datatype. It should ideally be available out of the box, to be used with any type.
| |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer wrote:
> Yes, but I also think if we can make UFCS optional, we give some power back to the author of the struct/class. He can choose not to participate in ufcs, and then he can control the API to his type.
>
> Plus, this can be a good interim step! It works right now!
>
> -Steve
Just realized that actually it doesn't. It does not work across module boundaries... =/
Timon
| |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Lutger Blijdestijn | On Mon, 06 Jun 2011 17:07:25 -0400, Lutger Blijdestijn <lutger.blijdestijn@gmail.com> wrote: > Nice and clever! However, I don't think it's good as an alternative for ufcs > in the language, unless that is going to be dropped because of too many > ambiguities. I admit I'm not warm and fuzzy on arbitrarily extending interface. For one, having functions in one place makes docs/members easy to look up. i.e. I can look at one file and know all the operations for a datatype, and I can look at a function call and know where to find it in the documentation. I'm a firm believer that an object's author should be in charge of the interface to his object. Hence my position on enforcing strict @property syntax. However, ranges are really not in that league -- all operations on ranges are pretty much external. Only the primitives live in the type. This is something that is accepted and understood. The same thing with slices. So it's natural to want to use ufcs there. BTW, we've had several people who think capacity should be in std.array, but it is actually in core.object, this is due to the disjoint nature of slice members -- they live everywhere. This will only get worse. > ufcs should imho be a decision on the caller side or the side of the > function extending a datatype (like extension methods in C#), not the > implementor of a datatype. It should ideally be available out of the box, to > be used with any type. I am OK with ucfs being available everywhere, but the ambiguities are difficult to work around. Especially for properties. My preference is that UFCS be supported only for builtin language types (slices, fixed-size arrays, primitives) because there is no other option for extension, and we should work on allowing author-sponsored extendability in custom types. Type wrapping in general needs a lot more support from the compiler and IFTI. The author can even explicitly only allow extendability of certain function names, with the ability to use template constraints on opDispatch. I just think it's an area we *already* need to work on, and given this solution, it naturally allows better ufcs as you can wrap functions better. -Steve | |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer |
--
-------------------------------
Not sent from an iPhone.
"Steven Schveighoffer" <schveiguy@yahoo.com> wrote in message news:op.vwn4enw5eav7ka@localhost.localdomain...
> Someone wrote a very compelling argument for ufcs (uniform function call syntax) for ranges, and that is, given a slew of range functions, and a slew of ranges, it is nice to use a fluent programming syntax to specify wrappers for ranges without having to extend each range type. For example:
>
> take(10,stride(2,cycle([3,2,5,3])));
>
> vs.
>
> [3,2,5,3].cycle().stride(2).take(10);
>
> And I thought damn it would be nice if ranges could implement ufcs, but other types that you didn't want to allow infinite extendability could avoid it. That gave me an idea :)
>
>
> import std.stdio;
>
> struct ufcs
> {
> auto opDispatch(string name, T...)(T args) // appropriate if compiles
> constraint here
> {
> mixin("return ." ~ name ~ "(this, args);");
> }
> }
>
> int foo(ufcs x, int y)
> {
> writefln("it works! %d", y);
> return y+1;
> }
>
> void main()
> {
> ufcs u;
> auto x = u.foo(1);
> assert(x == 2);
> }
>
> And it does indeed work (2.053)...
>
> So we can have ufcs without any changes to the compiler, and we also make it a *choice* for people who don't want to allow infinite extendability, and don't want to deal with possible compiler ambiguities.
>
> The opDispatch could even be a mixin itself (I think).
>
> What do you think?
>
I just hope it doesn't cause real ufcs to become an even lower priority than it already is.
| |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer wrote: > On Mon, 06 Jun 2011 17:07:25 -0400, Lutger Blijdestijn <lutger.blijdestijn@gmail.com> wrote: > >> Nice and clever! However, I don't think it's good as an alternative for >> ufcs >> in the language, unless that is going to be dropped because of too many >> ambiguities. > > I admit I'm not warm and fuzzy on arbitrarily extending interface. For one, having functions in one place makes docs/members easy to look up. i.e. I can look at one file and know all the operations for a datatype, and I can look at a function call and know where to find it in the documentation. I'm a firm believer that an object's author should be in charge of the interface to his object. Hence my position on enforcing strict @property syntax. I understand. What I find most attractive about ufcs is not really about extending interfaces, but making function composition saner to read. In some libraries such as jquery and linq it's very clean to compose operations, because they work well with chaining. Although it's not the same, something similar (and actually much better) could be achieved with ranges + ufcs. Ranges want to be composed, yet I find myself often introducing temporary variables to avoid a mess of nesting. Libraries with chaining are more pleasant, imho. I'll readily believe that the problems are significant though. Another issue may be function hijacking: when I call bar.foo() where foo is my extension, and the author of bar adds a foo method to it's type, the behavior silently changes. Your proposal does give some control over that. > However, ranges are really not in that league -- all operations on ranges are pretty much external. Only the primitives live in the type. This is something that is accepted and understood. The same thing with slices. So it's natural to want to use ufcs there. To me, this would be a big improvement on the usability of ranges. Not saying ranges aren't usable, but this would make for much shorter and readable code. > BTW, we've had several people who think capacity should be in std.array, but it is actually in core.object, this is due to the disjoint nature of slice members -- they live everywhere. This will only get worse. > >> ufcs should imho be a decision on the caller side or the side of the >> function extending a datatype (like extension methods in C#), not the >> implementor of a datatype. It should ideally be available out of the >> box, to >> be used with any type. > > I am OK with ucfs being available everywhere, but the ambiguities are difficult to work around. Especially for properties. > > My preference is that UFCS be supported only for builtin language types (slices, fixed-size arrays, primitives) because there is no other option for extension, and we should work on allowing author-sponsored extendability in custom types. Type wrapping in general needs a lot more support from the compiler and IFTI. > > The author can even explicitly only allow extendability of certain function names, with the ability to use template constraints on opDispatch. I just think it's an area we *already* need to work on, and given this solution, it naturally allows better ufcs as you can wrap functions better. > > -Steve | |||
June 06, 2011 Re: possible "solution" for ufcs | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Mon, 06 Jun 2011 17:18:27 -0400, Timon Gehr <timon.gehr@gmx.ch> wrote:
> Steven Schveighoffer wrote:
>> Yes, but I also think if we can make UFCS optional, we give some power
>> back to the author of the struct/class. He can choose not to participate
>> in ufcs, and then he can control the API to his type.
>>
>> Plus, this can be a good interim step! It works right now!
>>
>> -Steve
>
> Just realized that actually it doesn't. It does not work across module
> boundaries... =/
:(
Hm... this makes sense, the template is instantiated at the context level
of the defining module, not the calling module.
It can however work for something like std.range calling std.range or
std.algorithm functions. Just not in the general sense.
-Steve
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply