February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sun, 03 Feb 2013 11:11:05 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > On 2/3/13 5:14 AM, Johannes Pfau wrote: >> "If a function returns a reference, then assignment through the >> paren-less call should work: " >> >> This is the only part where I would disagree. Why is this special rule >> necessary if we have full @property support? I think this would still >> allow too many false positives. > > (As a note, this is the current behavior.) > > The way I see it is this is a natural consequence of optional parens. The name of a function without a "&" prepended or a "()" after it will invoke the function, and that's that. > >> One important aspect that this proposal doesn't cover yet is whether we >> want to allow "semantic rewriting" for properties: >> ---- >> Struct a; >> a.property++; //would this be legal? >> ---- > > It's dangerous to get too clever about that. Anyhow, such rewrites are possible: > > ++a.p ----> { auto v = a.p; ++v; a.p = v; return v; }() > a.p++ ----> { auto v = a.p; ++a.p; return v; }() > > and so on. > >> for other corner cases this list is a good start: >> http://wiki.dlang.org/Property_Discussion_Wrap-up#Implementation_concerns >> >> * Can we get a reference to the property? What does >> &x.property mean? > > We need to add this to the proposal. There are two schools of thought here: > > 1. Make properties emulate regular variables as much as possible. In that case &a.p is the same as &(a.p), i.e. it applies to the returned value. (One counter-argument here is that properties should seldom return a reference because that breaks encapsulation.) > > 2. Allow people to do whatever they need to do without much aggravation. In that case &a.p obeys the normal rules of taking a method's address, and &(a.p) applies to the returned value. > > I favor (2) and put it in http://wiki.dlang.org/DIP23. Will talk to Walter. This sounds good. It might be a bit confusing, but nice in the fact that &(a.b) always means address of whatever b returns, whether it is a field or property. >> * Are UFCS properties possible? How do they work exactly? >> * How do you disambiguate property functions when they're free >> functions which conflict? > > I think it would be best to simply disallow parameterless module-level properties. > > // at top level > @property int foo(); // error > @property int goo(int); // fine, assume a getter for int I think this is a possible solution, and I can live with that, I'm fairly certain that some people use global properties currently, so they will not be too happy. But having to set global properties using a function call (global getters can simply be paren-less functions) isn't horrible. >> * Are templated properties allowed? >> ** The access syntax for properties doesn't allow providing types > > No. Templated member variables are not allowed either. Wait, what? I don't like this idea. Why should this not be allowed: @property void x(T)(T t) {_x = to!(typeof(_x))(t);} As for templates that return a templated type, it should be allowed, although the calling syntax would have to be for the explicit template call, since you can't do IFTI on return values. In other words: template x(T) { @property T x(){return to!(T)(_x);} } // could be shortened to normal @property T x(T)() auto xcopy = obj.x!(string).x; Certainly, we need templated getters and setters at module levels: @property T front(T)(T[] r) { return r[0];} -Steve |
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Sun, 03 Feb 2013 11:28:52 -0500, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> As for templates that return a templated type, it should be allowed, although the calling syntax would have to be for the explicit template call, since you can't do IFTI on return values. In other words:
>
> template x(T) { @property T x(){return to!(T)(_x);} } // could be shortened to normal @property T x(T)()
>
> auto xcopy = obj.x!(string).x;
Hm... I actually am changing my mind on this, you should be able to do:
auto xcopy = obj.x!(string);
For the simple reason that if @property is omitted, this would work (simple lack of parens). It makes no sense to make properties not have this ability.
-Steve
|
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Am Sun, 03 Feb 2013 11:11:05 -0500 schrieb Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>: > On 2/3/13 5:14 AM, Johannes Pfau wrote: > > "If a function returns a reference, then assignment through the paren-less call should work: " > > > > This is the only part where I would disagree. Why is this special rule necessary if we have full @property support? I think this would still allow too many false positives. > > (As a note, this is the current behavior.) > > The way I see it is this is a natural consequence of optional parens. The name of a function without a "&" prepended or a "()" after it will invoke the function, and that's that. I didn't think of it as a call without parentheses but it indeed makes sense. > > > * Is returning ref values from the getter OK? > > I see no reason to disallow it at the language level. You have to consider cases though where you have both a setter and a getter returning a 'ref value'. @property ref int value(); @property void value(int new); * Is it valid to define both at the same time? * Which one is used for assignment etc? > > * Is taking ref values in the setter OK? > > How do you mean that? @property int value(ref int new); IIRC that was just meant to complement the "is returning ref OK". Usually nothing bad can happen. Although the ref parameter could be converted into a pointer with some nasty tricks it's probably not the job of the language to prevent that. |
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On 02/03/2013 05:11 PM, Andrei Alexandrescu wrote: > ... > > 2. Allow people to do whatever they need to do without much aggravation. > In that case &a.p obeys the normal rules of taking a method's address, > and &(a.p) applies to the returned value. > > I favor (2) and put it in http://wiki.dlang.org/DIP23. Will talk to Walter. > That is horrible! The entire language construct is necessarily a bit of a patchwork, but there is no reason to aggravate this gratuitously. > ... >> * Are templated properties allowed? >> ** The access syntax for properties doesn't allow providing types > > No. Ditto. > Templated member variables are not allowed either. > (Actually they are silently rewritten to static member variables. I do not like that.) |
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to kenji hara | On 2/3/13 7:37 AM, kenji hara wrote: > 1. When a function is annotated with @property, it cannot be called with > parenthesis syntax. > 2. 0-arg functions which not annotated with @property can be called > without parentheses. > 3. Ref return getter can make "auxiliary setter", if formal getter is > missing. > 4. `typeof(exp)` never returns "function type". In other words, the > actual type of `exp` and `typeof(exp)` should be same. > 5. Both `&prop` and `&func` should return function pointer / delegate object > 6. UFCS CAN NOT call global setter by getter syntax. Yah. There is still some inconsistency, but I think there's no way to be 100% consistent. For properties &a.prop is not the same as &(a.prop), which is unlike other expressions. > I think that 4 to 6 are important points of this DIP. Based on the > rules, I could write an exhaustive test case. I'll insert comments inline. > alias Type = int; > > unittest > { > struct S > { > @property Type foo(); // formal getter > @property void bar(Type); // formal setter > @property ref Type baz(); // ref return getter == auxiliary > setter > } > S s; > static assert( __traits(compiles, { s.foo; })); > static assert(!__traits(compiles, { s.foo(); })); > static assert(is(typeof(s.foo) == Type)); > static assert(is(typeof(&s.foo) == Type delegate())); Yes, great. You may want to also add: static assert(!__traits(compiles, { auto p = &(s.foo); })); because that would apply & to the Type rvalue returned by s.foo. > static assert( __traits(compiles, { s.bar = 1; })); > static assert(!__traits(compiles, { s.bar(1); })); > static assert(is(typeof(s.bar)) == false); > static assert(is(typeof(&s.bar) == void delegate(Type))); Yes. Also: static assert(is(typeof(s.bar = 1) == void)); > static assert( __traits(compiles, { s.baz; })); > static assert(!__traits(compiles, { s.baz(); })); > static assert( __traits(compiles, { s.baz = 1; })); > static assert(is(typeof(s.baz) == Type)); > static assert(is(typeof(&s.foo) == ref Type delegate())); Yes, assuming you meant "baz" on the last line, too. > } > unittest > { > struct S > { > Type foo(); // 0-arg function > void bar(Type n); // 1-arg function > ref Type baz(); // 0-arg ref return function > } > S s; > static assert( __traits(compiles, { s.foo; })); > static assert( __traits(compiles, { s.foo(); })); > static assert(is(typeof(s.foo) == Type)); > static assert(is(typeof(&s.foo) == Type delegate())); Correct. Also add: static assert(!is(typeof(&(s.foo)))); > static assert(!__traits(compiles, { s.bar = 1; })); > static assert( __traits(compiles, { s.bar(1); })); > static assert(is(typeof(s.bar)) == false); > static assert(is(typeof(&s.bar) == void delegate(Type))); Correct. Also: static assert(!is(typeof(&(s.bar)))); because the expression s.bar is meaningless. (This is NEW BEHAVIOR.) The basic idea here is to disallow expressions that can't be typed. > static assert( __traits(compiles, { s.baz; })); > static assert( __traits(compiles, { s.baz = 1; })); > static assert( __traits(compiles, { s.baz(); })); > static assert(is(typeof(s.baz) == Type)); > static assert(is(typeof(&s.baz) == ref Type delegate())); > } Correct. Also: static assert(is(typeof(&(s.baz)) == Type*)); > @property Type foo(); I'm not sure we should allow this at module level. (And there is no way to write a corresponding setter.) > @property void bar(Type); > @property ref Type baz(); I think we should disallow this as well. > unittest > { > static assert( __traits(compiles, { foo; })); > static assert(!__traits(compiles, { foo(); })); > static assert(is(typeof(foo) == Type)); > static assert(is(typeof(&foo) == Type function())); If we disallow top-level global @properties neither of these will compile. If we do allow them, then the asserts would pass. > static assert( __traits(compiles, { bar = 1; })); > static assert(!__traits(compiles, { bar(1); })); > static assert(is(typeof(bar)) == false); > static assert(is(typeof(&bar) == Type function())); Nope, all of these are getters for int. The following should compile instead: static assert( __traits(compiles, { 1.bar; })); static assert(!__traits(compiles, { bar(1); })); static assert(is(typeof(bar)) == false); static assert(is(typeof(&bar) == void function(int))); > static assert( __traits(compiles, { baz; })); > static assert(!__traits(compiles, { baz(); })); > static assert( __traits(compiles, { baz = 1; })); > static assert(!__traits(compiles, { baz() = 1; })); > static assert(is(typeof(baz) == Type)); > static assert(is(typeof(&baz) == ref Type function())); If we allow top-level properties with 0 parameters, these should inded compile. > } > > @property Type foh(Type); This is always a getter for Type returning a Type. > @property void bah(Type n, Type m); This is always a setter having Type on the left-hand side and Type on the right-hand side. > @property ref Type bas(Type); This is always a getter for Type. > Type hoo(Type); > void var(Type, Type); > ref Type vaz(Type); > > unittest > { > static assert( __traits(compiles, { foh = 1; }) && > !__traits(compiles, { hoo = 1; })); No, replace with: static assert( __traits(compiles, { 1.foh; }) && !__traits(compiles, { hoo = 1; })); > static assert(!__traits(compiles, { foh(1); }) && > __traits(compiles, { hoo(1); })); Correct. > static assert(!__traits(compiles, { 1.foh; }) && > __traits(compiles, { 1.hoo; })); Incorrect, foh is a getter for Type so the first should work. static assert(__traits(compiles, { 1.foh; }) && __traits(compiles, { 1.hoo; })); > static assert(!__traits(compiles, { 1.foh(); }) && > __traits(compiles, { 1.hoo(); })); This is identical to the one above. > static assert(!__traits(compiles, { bah(1, 2); }) && > __traits(compiles, { var(1, 2); })); Correct. > static assert( __traits(compiles, { 1.bah = 2; }) && > !__traits(compiles, { 1.var = 2; })); Correct. > static assert(!__traits(compiles, { 1.bah(2); }) && > __traits(compiles, { 1.var(2); })); First expression fails. > static assert( __traits(compiles, { bas = 1; }) && > !__traits(compiles, { vaz = 1; })); Both branches fail. > static assert(!__traits(compiles, { bas(1); }) && > __traits(compiles, { vaz(1); })); Correct. > static assert(!__traits(compiles, { bas(1) = 2; }) && > __traits(compiles, { vaz(1) = 2; })); Correct. > static assert(!__traits(compiles, { 1.bas; }) && > __traits(compiles, { 1.vaz; })); The first branch fails because 1.bas is legit. > static assert(!__traits(compiles, { 1.bas = 2; }) && > __traits(compiles, { 1.vaz = 2; })); First branch fails because 1.bas is legit and yields a ref int. Second branch passes. > static assert(!__traits(compiles, { 1.bas(); }) && > __traits(compiles, { 1.vaz(); })); Correct. > static assert(!__traits(compiles, { 1.bas() = 2; }) && > __traits(compiles, { 1.vaz() = 2; })); > } Correct. > Is this correct? As noted above, I might have missed some. I will update the doc with a copy of your unittests. Thanks! Andrei |
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to kenji hara | On 2/3/13 7:40 AM, kenji hara wrote:
> 2013/2/3 Steven Schveighoffer <schveiguy@yahoo.com
> <mailto:schveiguy@yahoo.com>>
>
> I have a possible suggestion to fix this within your proposal:
>
> @property on single-arg free functions ONLY enables a setter mode,
> it does not work as a UFCS getter.
>
> [snip]
>
> I was thinking the exact same thing.
Then we can't make this work:
@property ref T front(T[] array) { return array[0]; }
unittest
{
auto a = [ 1, 2, 3];
auto b = a.front;
}
Andrei
|
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 2/3/13 7:49 AM, Steven Schveighoffer wrote:
> On Sun, 03 Feb 2013 07:16:17 -0500, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>
>> On Sun, 03 Feb 2013 03:16:08 -0500, Andrei Alexandrescu
>> <SeeWebsiteForEmail@erdani.org> wrote:
>>
>>> Walter and I have had a discussion on how to finalize properties.
>>>
>>> http://wiki.dlang.org/DIP23
>>
>> I agree with everything in this. This looks exactly like what I wanted
>> a few days ago. Thank you!
>>
>> One aspect not addressed is free-function properties:
>
> I thought of one other problem with this proposal. You can no longer get
> a delegate of a property. Like it or not, there will be existing code
> that does get a delegate to properties (it is allowed now).
>
> I think we should provide a __traits accessor for this. Otherwise, there
> is no way to port said code to the new style.
In the proposal &a.b gets the delegate of a property.
Andrei
|
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sunday, 3 February 2013 at 17:37:03 UTC, Andrei Alexandrescu wrote:
> In the proposal &a.b gets the delegate of a property.
>
:facepalm:
|
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On 2/3/13 7:59 AM, Timon Gehr wrote: > On 02/03/2013 09:16 AM, Andrei Alexandrescu wrote: >> Walter and I have had a discussion on how to finalize properties. >> >> http://wiki.dlang.org/DIP23 >> ... > > Looks good. Two things: > > What about eg: > > @property T front(T)(T[] arr){ return arr[0]; } I updated the doc to clarify that one-parameter properties are ALWAYS getters and two-parameter properties are ALWAYS setters. > And: > > "Avoid embarrassing situations such as expressions with unexpressible > types or no-op address-of operator (as is the case with C functions)." > > The DIP does not fix the first issue. > > pragma(msg, typeof(*(int x)=>x)); // pure nothrow @safe int(int x) I see. I think dereferencing a function or delegate type should never work. Andrei |
February 03, 2013 Re: DIP23 draft: Fixing properties redux | ||||
---|---|---|---|---|
| ||||
Posted in reply to FG | On 2/3/13 8:16 AM, FG wrote: > On 2013-02-03 09:16, Andrei Alexandrescu wrote: >> Walter and I have had a discussion on how to finalize properties. > > Very reasonable in the majority of the proposal. > I can't, however, get my head around 2-arg property functions: > > int a_; > @property void a(int v, bool rev=false) { a_ = rev ? -v : v; } I'd say never allow defaulted arguments with @property. > a = 10; // fine > 10.a = true; // this feels backwards, true is just an extra flag > > The boolean flag is optional and making it the rhs of the assignment > suggests it is the value which will be set. On the other hand switching > argument positions, resulting in true.a = 10 also feels very strange. > > I'd say: allow only 0 or 1 argument in properties. > If you need more arguments, write a normal function for it. Two-argument @properties are always setters. Andrei |
Copyright © 1999-2021 by the D Language Foundation