digitalmars.D - Fixing D's Properties
- Chad J (274/274) Aug 17 2007 Properties are a blemish of the D language. In the first part of this
- Oskar Linde (18/29) Aug 17 2007 (Seems D is regressing. There used to be a standard library.)
- Sean Kelly (4/11) Aug 17 2007 Ideally, there should be a way to have both value and reference-based
- Sean Kelly (5/8) Aug 17 2007 All good points. I'd like properties in D to address them all somehow.
- Bill Baxter (3/14) Aug 17 2007 What issues does ref fix? You can't return a ref even in 2.0, AFAIK.
- Sean Kelly (4/15) Aug 17 2007 Oh. I assume this is a planned feature then? Otherwise I see little
- Bill Baxter (5/21) Aug 17 2007 I thought the reason was more to do with const. Given a const object
- Sean Kelly (7/13) Aug 17 2007 This is the explanation I remember as well. But I'm not sure it
- Bill Baxter (6/20) Aug 17 2007 It's all the more perplexing given that the current D 2.x compiler
- Daniel Keep (23/23) Aug 17 2007 [snip]
- Chad J (2/34) Aug 17 2007
- Chad J (27/27) Aug 17 2007 Alright I forgot to mention what something like "a.prop.x = foo;" would
- Bill Baxter (10/47) Aug 17 2007 That makes some sense. Although just avoiding the copying by allowing
- Deewiant (8/14) Aug 17 2007 Great that you took the time to go through it properly.
- Bill Baxter (6/20) Aug 17 2007 Well, 'property' may be in use by people writing property-editor widgets...
- BCS (49/118) Aug 17 2007 This is not an syntax issue with how proposes are defined, but the sema...
- Bill Baxter (34/38) Aug 17 2007 Ah yes. Although I generally agree with Chad, you did remind me of
- Chad J (41/195) Aug 18 2007 What minor changes?
- BCS (48/186) Aug 18 2007 How? Letting implicit properties be simultaneously L and R values (they ...
- Jason House (4/6) Aug 18 2007 By the very virtue that D makes the syntax identical, it _is_ trying to
- BCS (5/14) Aug 18 2007 Good point. To some extent it does. However, that just doesn't feel the ...
- Ender KaShae (19/22) Aug 18 2007 I suggest something similar to python properties, for those who are not ...
- BCS (6/14) Aug 18 2007 property int X(void, &setX);
- Jb (20/34) Aug 19 2007 Delphi does this.
- Chad J (8/54) Aug 19 2007 I like where all of this is going.
- BCS (9/18) Aug 19 2007 Let's say I wouldn't object to it. (I'm still not totally convinced some...
- Chad J (2/18) Aug 20 2007 Pretty clever! Too bad it seems very hardware dependent.
- Frits van Bommel (3/23) Aug 20 2007 It would also be hell to implement on x86 and similar architectures. Too...
- Christopher Wright (36/66) Aug 19 2007 Or:
- Ender KaShae (16/50) Aug 19 2007 The problem with that approach is it would be difficult to specify any s...
Properties are a blemish of the D language. In the first part of this post, I'll explain why and try to show how these properties are /not/ good or even better than properties in other languages. Then I'll suggest some possible solutions. I don't care if the solutions I suggest are used, but I will be very thankful if this gets fixed somehow. The first part of this is really for anyone who believes that D's properties are just dandy. So if you've already been bitten by these, feel free to skip down to the solution part of the post. I suppose I should first give some credit to a couple of the strengths of D properties: - They do not need any keywords. - They can be used outside of classes - pretty much anywhere functions can be used. When I offer a solution, I'll try to make one that preserves these advantages. The following examples are written for D v1.xxx. They are not tested on D v2.xxx, but may still work. Here is a list of some of their shortcomings: -------------------------------------(1) First, the very common ambiguity: Does foo.bar++; become foo.bar( foo.bar + 1 ); or foo.bar() + 1; ?? This is why we can't write things like this: ubyte[] array = new ubyte[50]; //... array.length++; //... array.length += 10; // Note how builtin types use properties heavily. Which means a large set of our beloved shortcut operators are completely broken on properties. -------------------------------------(2) Second, while the above is problematic, it creates yet another problem! migration from member fields to properties. Suppose a programmer have a class like so: class SomeContainer { int data; } Someone else using SomeContainer might write code like so: auto s = new SomeContainer; s.data++; Now the programmer does a slight modification to SomeContainer, one that he does not expect to break reverse compatibility: class SomeContainer { private int m_data; int data() { return m_data; } void data( int value ) { m_data = value; } } Now that someone else has a problem, because their code no longer compiles, since s.data++; is no longer a valid expression. The ability to write a lightweight interface using fields, and then at will promote it to a heavyweight interface of properties, is extremely handy. -------------------------------------(3) Third, there is a problem with value types and properties. Consider the following code: class Widget { Rectangle rect() { return m_rect; } void rect( Rectangle value ) { m_rect = value; } private Rectangle m_rect; } struct Rectangle { int x, y, w, h; // just a basic opCall "constructor", nothing important static Rectangle opCall( int x, int y, int w, int h ) { Rectangle result; result.x = x; result.y = y; result.w = w; result.h = h; return result; } } void main() { Widget w = new Widget(); // Insert some default rectangle. // The private field is assigned to so that you can remove the write property // and see that the line with w.rect.x = 10; will still compile, even // without a write property! w.m_rect = Rectangle( 0, 0, 80, 20 ); // now comes the fun part... w.rect.x = 10; assert( w.rect.x == 10 ); // fails } That code seems to be trying to write 10 to w's rect's x field. But it doesn't. It does this instead: //---- Rect temp = w.rect; temp.x = 10; //---- It never writes the temporary back into the widget's field as expected. Also, try doing as the comment in the example suggests: uncomment the write property, then compile. Observe how it still compiles, seemingly allowing you to write to a property that doesn't exist! -------------------------------------(4) Fourth, delegates get the shaft. Consider this: class Widget { private void delegate() m_onClick; // called when a click happens. void delegate() onClick() { return m_onClick; } void onClick( void delegate() value ) { m_onClick = value; } // code that polls input and calls onClick() is absent from this example. } void main() { void handler() { // handles onClick() printf( "Click happened!" ); // please forgive the use of printf, I don't know whether you are using // Tango or Phobos. } Widget w = new Widget(); // When any click happens, we want handler to do it's thing. w.onClick = &handler; // Now suppose, for whatever reason, we want to emulate a click from within // our program. w.onClick(); // This does not result in handler() being called! } The program DOES NOT print "Click happened!" as expected. Instead, w.onClick expanded to something like this: void delegate() temp = w.onClick(); That's all it does. It does not call 'temp'. It just leaves it there. That's because there is yet another ambiguity: Does foo.bar(); become T delegate() temp = foo.bar(); or T delegate() temp = foo.bar(); temp(); Try replacing w.onClick(); with w.onClick()(); and it should work. IMO, w.onClick()(); is not an obvious way at all to call that delegate. -------------------------------------(5) Fifth, delegates aren't over with yet. This isn't nearly as bad as the other 4, but it should hit eventually. Properties can be converted into delegates. This is shown in the following example code: struct Point { private int m_x = 0, m_y = 0; int x() { return m_x; } void x( int value ) { m_x = value; } int y() { return m_y; } void y( int value ) { m_y = value; } } void main() { Point p; int delegate() readX = &p.x; p.x = 2; printf( "%d", readX() ); // prints 2 } The problem is, what if, for whatever reason, the author of similar code someday wanted to demote those properties back to fields? In that case, the user code that relies on the delegate will fail. If properties were treated EXACTLY like fields, you wouldn't be able to take their address and call it like a function. You could take their address, but it would just be an address to a value. Realizing that this behavior of properties can be beneficial, it makes sense to show that similar code can be written without properties and without much added difficulty. If we wanted to make a readX function like in the above code without properties, we could write something like this: struct Point { int x,y; } void main() { Point p; int getX() { return p.x; } int delegate() readX = &getX; p.x = 2; printf( "%d", readX() ); // prints 2 } ======================================== How to fix it ======================================== The most direct solution seems to be explicit properties. This does not mean that the current implicit properties need to be removed. The two can exist together, and the disagreement that may follow is whether the implicit ones should be deprecated or not. I'm not going to worry about whether to deprecate implicit properties or not right now. Here is a table that compares some possible semantics of explicit properties against the current implicit properties: <TT> +----------------+----------+----------+ | properties | explicit | implicit | +----------------+----------+----------+ | write as field | yes | yes | +----------------+----------+----------+ | read as field | yes | yes | +----------------+----------+----------+ | lvalue* | yes | no | +----------------+----------+----------+ | overridable** | yes | yes | +----------------+----------+----------+ |callable as func| no | yes | +----------------+----------+----------+ |use as delegate | no | yes | +----------------+----------+----------+ | freestanding***| yes | yes | +----------------+----------+----------+ </TT> *lvalue: If foo is an lvalue, than foo = 5; should set foo's value to 5, instead of evaluating to 5; and doing nothing. Implicit write properties fake this to some extent, but do not hold in the general case. Example (3) above and the foo++; and foo+=10; issues are examples where lvalueness is needed. **overridable: This refers to whether or not the property can be overridden when found inside a class. ***freestanding: The property can be placed anywhere in a file, not just inside of classes. They can also be static and have attributes like a function would have. Another possible change might be to forbid return types for explicit write properties (they must return void). Then, there should be well-defined behavior in the D spec for code like this: foo = bar.x = someValue; // x is an explicit property in bar This could become bar.x = someValue; foo = someValue; or it could be bar.x = someValue; foo = bar.x; or perhaps something else. The first case is probably the easiest to understand for people reading code. In the second case foo's final value depends entirely on what bar.x does. Now the only issue is, what should the syntax be for the aforementioned explicit properties. Perhaps something like this: int foo|() { return bar; } T foo|( int baz ) { bar = baz; } Which reuses the '|' character. Thus these functions named foo are explicit properties. Other characters like '.' or '^' could be used as well. This type of syntax disambiguated templates, it should be able to do the same for properties. Another option would be to reuse a keyword, perhaps inout. inout { int foo() { return bar; } T foo( int baz ) { bar = baz; } } which would be equivalent to inout int foo() { return bar; } inout T foo( int baz ) { bar = baz; } Thus 'inout' would be an attribute that enforces explicit propertyness on a function or method. I suppose 'in' could be used for write properties and 'out' for read props, but then the curly braced attribute-has-scope syntax would not work so well. At the cost of a keyword, some intuitiveness could be gained. If said single keyword can be added, then perhaps add the keyword 'property' with syntax similar to the inout example. This post is a bit lengthy. So assuming you've read even part of it, thank you for your time. - Chad
Aug 17 2007
Chad J wrote:Properties are a blemish of the D language.[snip]printf( "Click happened!" ); // please forgive the use of printf, I don't know whether you are using // Tango or Phobos.(Seems D is regressing. There used to be a standard library.)======================================== How to fix it ======================================== The most direct solution seems to be explicit properties.[snip]This post is a bit lengthy. So assuming you've read even part of it, thank you for your time.I agree with all issues listed with properties. Properties can't be callable both with and without () in order support delegate properties transparently, which means there has to be way to mark them as non-functions. But I am not fully sure I understand your solution to these problems: a.prop++; What should this evaluate to assuming a.prop is an explicit property? What if a.prop returns a reference type (class) with an opPostInc? Likewise: a.prop.y = 7; What should this evaluate to? Any difference if a.prop evaluates to a reference type or a value type? -- Oskar
Aug 17 2007
Oskar Linde wrote:Chad J wrote: But I am not fully sure I understand your solution to these problems: a.prop++; What should this evaluate to assuming a.prop is an explicit property?Ideally, there should be a way to have both value and reference-based 'get' properties. Sean
Aug 17 2007
Chad J wrote:This post is a bit lengthy. So assuming you've read even part of it, thank you for your time.All good points. I'd like properties in D to address them all somehow. The 'ref' keyword in 2.0 solves some of the issues, but not others. If doing so requires a special syntax then I'm all for it. Sean
Aug 17 2007
Sean Kelly wrote:Chad J wrote:What issues does ref fix? You can't return a ref even in 2.0, AFAIK. --bbThis post is a bit lengthy. So assuming you've read even part of it, thank you for your time.All good points. I'd like properties in D to address them all somehow. The 'ref' keyword in 2.0 solves some of the issues, but not others. If doing so requires a special syntax then I'm all for it. Sean
Aug 17 2007
Bill Baxter wrote:Sean Kelly wrote:Oh. I assume this is a planned feature then? Otherwise I see little reason to change "inout" to "ref." SeanChad J wrote:What issues does ref fix? You can't return a ref even in 2.0, AFAIK.This post is a bit lengthy. So assuming you've read even part of it, thank you for your time.All good points. I'd like properties in D to address them all somehow. The 'ref' keyword in 2.0 solves some of the issues, but not others. If doing so requires a special syntax then I'm all for it.
Aug 17 2007
Sean Kelly wrote:Bill Baxter wrote:I hope so. That would be great.Sean Kelly wrote:Oh. I assume this is a planned feature then?Chad J wrote:What issues does ref fix? You can't return a ref even in 2.0, AFAIK.This post is a bit lengthy. So assuming you've read even part of it, thank you for your time.All good points. I'd like properties in D to address them all somehow. The 'ref' keyword in 2.0 solves some of the issues, but not others. If doing so requires a special syntax then I'm all for it.Otherwise I see little reason to change "inout" to "ref."I thought the reason was more to do with const. Given a const object passed by reference, calling it 'inout' doesn't make much sense. --bb
Aug 17 2007
Bill Baxter wrote:Sean Kelly wrote:This is the explanation I remember as well. But I'm not sure it justifies the addition of a general-purpose sounding keyword simply for parameter passing. "inout" may have been a bit confusing in light of const reference passing, but is consistency with "in" and "out" made it arguably worth retaining. SeanOtherwise I see little reason to change "inout" to "ref."I thought the reason was more to do with const. Given a const object passed by reference, calling it 'inout' doesn't make much sense.
Aug 17 2007
Sean Kelly wrote:Bill Baxter wrote:It's all the more perplexing given that the current D 2.x compiler segfaults on functions declared with const ref parameters. If const refs were the reason for changing 'inout' to 'ref' it is odd that they don't work straight out of the gate. --bbSean Kelly wrote:This is the explanation I remember as well. But I'm not sure it justifies the addition of a general-purpose sounding keyword simply for parameter passing. "inout" may have been a bit confusing in light of const reference passing, but is consistency with "in" and "out" made it arguably worth retaining.Otherwise I see little reason to change "inout" to "ref."I thought the reason was more to do with const. Given a const object passed by reference, calling it 'inout' doesn't make much sense.
Aug 17 2007
[snip] I emphatically agree that *something* needs to be done to address the shortcomings of properties in D. I mean, what's the point of having increment/decrement and the op= operators if you cannot use them in a large number of places? I do have to say that I'm not a big fan of the symbol-based syntax. Also, the inout system seems a bit... off. The problem I have with it is that *both* the getter and setter are marked inout, which doesn't make a lot of sense. Another possibility is to use contract-style notation: int foo { in(int baz) { bar = baz; } out() { return bar; } } Or even just dropping "inout" and using "in" and "out" explicitly: in void foo(int baz) { bar = baz; } out int foo() { return bar; } On another note, I think you should expand on exactly how different kinds of statements should be rewritten. For instance, you haven't clarified what happens here: w.rect.x = 10; -- Daniel
Aug 17 2007
Daniel Keep wrote:[snip] I emphatically agree that *something* needs to be done to address the shortcomings of properties in D. I mean, what's the point of having increment/decrement and the op= operators if you cannot use them in a large number of places? I do have to say that I'm not a big fan of the symbol-based syntax. Also, the inout system seems a bit... off. The problem I have with it is that *both* the getter and setter are marked inout, which doesn't make a lot of sense. Another possibility is to use contract-style notation: int foo { in(int baz) { bar = baz; } out() { return bar; } } Or even just dropping "inout" and using "in" and "out" explicitly: in void foo(int baz) { bar = baz; } out int foo() { return bar; }Looks like it will work, and is fine by me.On another note, I think you should expand on exactly how different kinds of statements should be rewritten. For instance, you haven't clarified what happens here: w.rect.x = 10; -- Daniel
Aug 17 2007
Alright I forgot to mention what something like "a.prop.x = foo;" would expand to. It would work like this: a.prop.x = foo; expands to auto temp = a.prop; temp.x = foo; a.prop = temp; Whether temp is a reference or value type need not matter, it should work exactly as it would if there was a variable there instead of a property. I suppose Sean's suggestion of allowing programmers to trap reference and value uses of properties would work, but I am skeptical that it might break the close compatibility between plain variables (fields) and properties. It should be feasible as long as there is a way to make a property equivalent to a field wrt calling syntax/semantics. Longer chains should be broken down as such: a.prop1.prop2.field.prop3.x = foo; -> auto temp1 = a.prop1; auto temp2 = temp1.prop2; auto temp3 = temp2.field.prop3; temp3.x = foo; temp2.field.prop3 = temp3; temp1.prop2 = temp2; a.prop1 = temp1; Hope that helps.
Aug 17 2007
Chad J wrote:Alright I forgot to mention what something like "a.prop.x = foo;" would expand to. It would work like this: a.prop.x = foo; expands to auto temp = a.prop; temp.x = foo; a.prop = temp; Whether temp is a reference or value type need not matter, it should work exactly as it would if there was a variable there instead of a property. I suppose Sean's suggestion of allowing programmers to trap reference and value uses of properties would work, but I am skeptical that it might break the close compatibility between plain variables (fields) and properties. It should be feasible as long as there is a way to make a property equivalent to a field wrt calling syntax/semantics. Longer chains should be broken down as such: a.prop1.prop2.field.prop3.x = foo; -> auto temp1 = a.prop1; auto temp2 = temp1.prop2; auto temp3 = temp2.field.prop3; temp3.x = foo; temp2.field.prop3 = temp3; temp1.prop2 = temp2; a.prop1 = temp1; Hope that helps.That makes some sense. Although just avoiding the copying by allowing either references or a way to create a light proxy that overloads opDot would be more useful. Then you could return a proxy from 'a' that contains just a pointer to prop2. Either way, D sorely needs a way to overload opDot (akin to C++'s operator->). Not having it eliminates a huge class of useful ways to create value wrappers. There's some seriously cool potential for a CTFE template opDot that takes a string giving the member name. --bb
Aug 17 2007
Chad J wrote:Properties are a blemish of the D language. In the first part of this post, I'll explain why and try to show how these properties are /not/ good or even better than properties in other languages. Then I'll suggest some possible solutions. I don't care if the solutions I suggest are used, but I will be very thankful if this gets fixed somehow.Great that you took the time to go through it properly. I agree that all the issues you mentioned need to be fixed somehow. big no-no I find this worth considering. Who uses "property" as an identifier name anyway? -- Remove ".doesnotlike.spam" from the mail address.
Aug 17 2007
Deewiant wrote:Chad J wrote:Ditto.Properties are a blemish of the D language. In the first part of this post, I'll explain why and try to show how these properties are /not/ good or even better than properties in other languages. Then I'll suggest some possible solutions. I don't care if the solutions I suggest are used, but I will be very thankful if this gets fixed somehow.Great that you took the time to go through it properly. I agree that all the issues you mentioned need to be fixed somehow.big no-no I find this worth considering. Who uses "property" as an identifier name anyway?Well, 'property' may be in use by people writing property-editor widgets like in IDEs. But still, I'd go for a new 'property' keyword. Will probably clash with far fewer existing uses that 'ref' did. --bb
Aug 17 2007
Reply to Chad,-------------------------------------(1) First, the very common ambiguity: Does foo.bar++; become foo.bar( foo.bar + 1 ); or foo.bar() + 1; ?? This is why we can't write things like this: ubyte[] array = new ubyte[50]; //... array.length++; //... array.length += 10; // Note how builtin types use properties heavily. Which means a large set of our beloved shortcut operators are completely broken on properties.This is not an syntax issue with how proposes are defined, but the semantics of there use. It can be fixed with minor changes.-------------------------------------(2)[...]-------------------------------------(3) Third, there is a problem with value types and properties. Consider the following code:short version: struct S {int i; void I(int j){i=j;} } struct P {S s void GetS() { return s;} } P p; p.GetS.I = 5; // assignes 5 to temp, not member of p This is a reference semantics issue. The same isses happens to general functions.-------------------------------------(4) Fourth, delegates get the shaft.Short version: void delegate() getDG(); getDG(); // calls getDG giving a delgate getDG(); // calls getDG, does not call returned delegate good point.-------------------------------------(5) Fifth, delegates aren't over with yet. This isn't nearly as bad as the other 4, but it should hit eventually. Properties can be converted into delegates.[...]The problem is, what if, for whatever reason, the author of similar code someday wanted to demote those properties back to fields? In that case, the user code that relies on the delegate will fail.[...]Realizing that this behavior of properties can be beneficial, it makes sense to show that similar code can be written without properties and without much added difficulty.[...]struct Point { int x,y; } void main() { Point p; int getX() { return p.x; } int delegate() readX = &getX; p.x = 2; printf( "%d", readX() ); // prints 2 }ouch! that sounds worse than the problem you point out.======================================== How to fix it ========================================[...]*lvalue: If foo is an lvalue, than foo = 5; should set foo's value to 5, instead of evaluating to 5; and doing nothing. Implicit write properties fake this to some extent, but do not hold in the general case. Example (3) above and the foo++; and foo+=10; issues are examples where lvalueness is needed.I would disagree. I would clam that the issue is that implicit properties can be used a lvalues or rvalues, but not both. [......] The basis of your thoughts seem to come from the idea that hiding the fact that properties are functions would be a good thing. I would assert that this is highly debatable. I will concede that the current semantics of properties being not quite interchangeable with fields is sub optimal. However fixing this by creating a new construct that, in the end, is identical to a function but with a number of things forbidden seems somehow wrong. As I see it the issues you bring up amount to: 1> no L/R-value usage (++, +=, etc, use as an inout or out arg) 2> they can lead to hard to find reference semantics issues 3> syntax problems with properties retuning delegates 4> not interchangeable with fields solution. Also, the proposed solution doesn't fix the issue because taking the address of a field is allowed, even with explicit properties, this wouldn't work IMHO there are a few minor issues with proposes that could uses a little work. However I don't see much to be gained by you proposal for explicit properties.This post is a bit lengthy. So assuming you've read even part of it, thank you for your time. - ChadIt's good to see people thinking about this stuff. I may not see your ideas to be the "right" way but, then again, I want different things out of D than many people do.
Aug 17 2007
BCS wrote:Reply to Chad,The basis of your thoughts seem to come from the idea that hiding the fact that properties are functions would be a good thing. I would assert that this is highly debatable.Ah yes. Although I generally agree with Chad, you did remind me of something. In my Luigi GUI library I use properties for lots of widget settings (things like a button's label, etc), and also rely on being able to call those as delegates for signal-slot connections to set the properties. That's handy, but it only works for members/properties that are written as functions. What I should probably do is to auto-generate a delegate wrapper for pure data members if you try to use one as a slot for a delegate(T setval) type of signal. The other issue it runs into is the overload ambiguity of void foo(int) {} int foo() {} when you take the function reference. One way to resolve the desire to use properties, not pretend they're functions, and still allow usage as a function when possible, would be to allow giving the functions names different from the property name. So then you could do something like: void set_foo(int) {...} int get_foo() {...} property foo(get_foo,set_foo); Or property foo { void set_foo(int) {...} int get_foo() {...} } Of course now you can already make aliases if you just want unambiguous function names to take the address of. (Doesn't prevent using foo() as a function though) void set_foo(int) {...} int get_foo() {...} alias set_foo foo; alias get_foo foo; // not 100% sure this works... --bb
Aug 17 2007
BCS wrote:Reply to Chad,What minor changes? If you're going to suggest giving implicit properties lvalueness, then this would break any code that relies on the quirks of current implicit properties. I was hoping to preserve reverse compatibility with my solution, especially since Walter seems conservative about doing anything that breaks existing code.-------------------------------------(1) First, the very common ambiguity: Does foo.bar++; become foo.bar( foo.bar + 1 ); or foo.bar() + 1; ?? This is why we can't write things like this: ubyte[] array = new ubyte[50]; //... array.length++; //... array.length += 10; // Note how builtin types use properties heavily. Which means a large set of our beloved shortcut operators are completely broken on properties.This is not an syntax issue with how proposes are defined, but the semantics of there use. It can be fixed with minor changes.Promotion != lvalueness extensibility issues.-------------------------------------(2)[...]Exactly. I don't want function semantics with my properties. I already have function semantics with my functions.-------------------------------------(3) Third, there is a problem with value types and properties. Consider the following code:short version: struct S {int i; void I(int j){i=j;} } struct P {S s void GetS() { return s;} } P p; p.GetS.I = 5; // assignes 5 to temp, not member of p This is a reference semantics issue. The same isses happens to general functions.I disagree, but in light of what you value, it becomes an aesthetics vs functionality issue.-------------------------------------(4) Fourth, delegates get the shaft.Short version: void delegate() getDG(); getDG(); // calls getDG giving a delgate getDG(); // calls getDG, does not call returned delegate good point.-------------------------------------(5) Fifth, delegates aren't over with yet. This isn't nearly as bad as the other 4, but it should hit eventually. Properties can be converted into delegates.[...]The problem is, what if, for whatever reason, the author of similar code someday wanted to demote those properties back to fields? In that case, the user code that relies on the delegate will fail.[...]Realizing that this behavior of properties can be beneficial, it makes sense to show that similar code can be written without properties and without much added difficulty.[...]struct Point { int x,y; } void main() { Point p; int getX() { return p.x; } int delegate() readX = &getX; p.x = 2; printf( "%d", readX() ); // prints 2 }ouch! that sounds worse than the problem you point out.Still doesn't handle promotion/demotion between fields and properties. Also, if you have a way to make the lvalue&rvalue semantics work, let me hear it.======================================== How to fix it ========================================[...]*lvalue: If foo is an lvalue, than foo = 5; should set foo's value to 5, instead of evaluating to 5; and doing nothing. Implicit write properties fake this to some extent, but do not hold in the general case. Example (3) above and the foo++; and foo+=10; issues are examples where lvalueness is needed.I would disagree. I would clam that the issue is that implicit properties can be used a lvalues or rvalues, but not both.[......] The basis of your thoughts seem to come from the idea that hiding the fact that properties are functions would be a good thing. I would assert that this is highly debatable. I will concede that the current semantics of properties being not quite interchangeable with fields is sub optimal. However fixing this by creating a new construct that, in the end, is identical to a function but with a number of things forbidden seems somehow wrong.Actually, I am suggesting that properties not be functions at all. They will of course share the notion of a body of executable code, but for all intents and purposes they are a way of accessing data while introducing side effects.As I see it the issues you bring up amount to: 1> no L/R-value usage (++, +=, etc, use as an inout or out arg) 2> they can lead to hard to find reference semantics issues 3> syntax problems with properties retuning delegates 4> not interchangeable with fieldshow?I have suffered many hours of debugging thanks to this "feature". Don't tell me it isn't a problem!I disagree, but regardless let's at least fix it while we are dealing with the related stuff.proposed solution. Also, the proposed solution doesn't fix the issue because taking the address of a field is allowed, even with explicit properties, this wouldn't workIf the proposed solution is not good enough, please give a better one. Suggestions are welcome and encouraged. As for the address of a field... perhaps if taking the address of a field returned a delegate... nah. This is somewhat of a corner case, but you've got me there, for now. I'd love to see someone come up with a slick solution to this. considered "unsafe" and done very infrequently. It makes me want to address of a property, and I probably will at some point. Even if the addressing thing can't be fixed, I'd at least like to see the rest of the stuff get fixed.IMHO there are a few minor issues with proposes that could uses a little work. However I don't see much to be gained by you proposal for explicit properties.This post is a bit lengthy. So assuming you've read even part of it, thank you for your time. - ChadIt's good to see people thinking about this stuff. I may not see your ideas to be the "right" way but, then again, I want different things out of D than many people do.
Aug 18 2007
Reply to Chad,BCS wrote:[...]Reply to Chad,-------------------------------------(1) First, the very common ambiguity: Does foo.bar++; become foo.bar( foo.bar + 1 ); or foo.bar() + 1; ??How? Letting implicit properties be simultaneously L and R values (they can now be either, just not both at the same time) won't break anything that I know of. Anything that is legal now, still would be, and any cases where the behavior would be different, were illegal to begin with.This is not a syntax issue with how properties are defined, but the semantics of there use. It can be fixed with minor changes.What minor changes? If you're going to suggest giving implicit properties lvalueness, then this would break any code that relies on the quirks of current implicit properties. I was hoping to preserve reverse compatibility with my solution, especially since Walter seems conservative about doing anything that breaks existing code.If you see them as disticntly different points, then we must be looking at things at different levels. I see both 1 and 2 as saying: "properties canPromotion != lvalueness extensibility issues.-------------------------------------(2)[...]I see your point and I strongly disagree. Under the covers, it IS a function and I /don't/ want that hidden.Exactly. I don't want function semantics with my properties. I already have function semantics with my functions.-------------------------------------(3) Third, there is a problem with value types and properties. Consider the following code:short version: struct S {int i; void I(int j){i=j;} } struct P {S s void GetS() { return s;} } P p; p.GetS.I = 5; // assignes 5 to temp, not member of p This is a reference semantics issue. The same isses happens to general functions.Unless you can trust the compiler to inline delegate literals (and the potentially virtual function call underneath), it is a performance issue.I disagree, but in light of what you value, it becomes an aesthetics vs functionality issue.-------------------------------------(5) Fifth, delegates aren't over with yet.[...]the user code that relies on the delegate will fail.[...] ouch! that sounds worse than the problem you point out.My point was not intended to. It is a minor issue claming that the mechanics of one of your (valid) issues with implicit properties is incorrectly stated.Still doesn't handle promotion/demotion between fields and properties.======================================== How to fix it ========================================[...]*lvalue: If foo is an lvalue, than foo = 5; should set foo's value to 5, instead of evaluating to 5; and doing nothing. Implicit write properties fake this to some extent, but do not hold in the general case. Example (3) above and the foo++; and foo+=10; issues are examples where lvalueness is needed.I would disagree. I would clam that the issue is that implicit properties can be used a lvalues or rvalues, but not both.Also, if you have a way to make the lvalue&rvalue semantics work, let me hear it.I think that you actually described somewhere the solution I wold use. Use the R-value property (getter) to get the value, do the operation, then use the L-value property (setter) to push it back in. struct S { ... int i(); void i(int); } S s; s.i++; // becomes { auto __tmp = s.i; __tmp++; s.i = __tmp; } If only one or the other property exits then it is assumed the author wants to disallow this type of operation.Will they be accessed by doing a call/return sequence at the machine code level? If so, I would assert they are functions for all purposes I'm interested in. If it would be implemented by some other means, then I have have missed some major point of your post. D is a low level language in many respects, as such, if it looks like a function when it comes out of the compiler, I want to be able to treat it as a function when it goes in.[......] The basis of your thoughts seem to come from the idea that hiding the fact that properties are functions would be a good thing. I would assert that this is highly debatable. I will concede that the current semantics of properties being not quite interchangeable with fields is sub optimal. However fixing this by creating a new construct that, in the end, is identical to a function but with a number of things forbidden seems somehow wrong.Actually, I am suggesting that properties not be functions at all. They will of course share the notion of a body of executable code, but for all intents and purposes they are a way of accessing data while introducing side effects.same as aboveAs I see it the issues you bring up amount to: 1> no L/R-value usage (++, +=, etc, use as an inout or out arg) 2> they can lead to hard to find reference semantics issues 3> syntax problems with properties retuning delegates 4> not interchangeable with fieldshow?It's not a problem with /properties/. dropping properties compleaty wouldn't remove this type of isses. It might make some cases easyer to spot but nothing more. As to converting from a member to a property changing things, thatI have suffered many hours of debugging thanks to this "feature". Don't tell me it isn't a problem!Nothing yet. IMHO, your proposed solution, while maybe appropriate for higher level languages, is not right for D. If I see a solution that solves these issues without cause to many of its own problems, I would be interested.proposed solution. Also, the proposed solution doesn't fix the issue because taking the address of a field is allowed, even with explicit properties, this wouldn't workIf the proposed solution is not good enough, please give a better one. Suggestions are welcome and encouraged.As for the address of a field... perhaps if taking the address of a field returned a delegate... nah. This is somewhat of a corner case, but you've got me there, for now. I'd love to see someone come up with a slick solution to this. was considered "unsafe" and done very infrequently. It makes me want to address of a property, and I probably will at some point. Even if the addressing thing can't be fixed, I'd at least like to see the rest of the stuff get fixed.I'll bet you can't. You can't use one for an out or ref argument (I just cheked) but you can use a member.
Aug 18 2007
BCS wrote:I see your point and I strongly disagree. Under the covers, it IS a function and I /don't/ want that hidden.By the very virtue that D makes the syntax identical, it _is_ trying to hide that fact. Either properties must try to act like members or the usage of properties should not mirror members.
Aug 18 2007
Reply to Jason,BCS wrote:Good point. To some extent it does. However, that just doesn't feel the same as Chad's suggestion. I'm not sure exactly why, but I thing it has something to do with the fact that currently D only hides the function'ness syntactically, but the proposal does so semantically.I see your point and I strongly disagree. Under the covers, it IS a function and I /don't/ want that hidden.By the very virtue that D makes the syntax identical, it _is_ trying to hide that fact. Either properties must try to act like members or the usage of properties should not mirror members.
Aug 18 2007
Chad J Wrote:If the proposed solution is not good enough, please give a better one. Suggestions are welcome and encouraged.I suggest something similar to python properties, for those who are not familior with it a property is created with property(getter, setter) so with my idea property (or inout or some other keyword) would be a new type, sort of like a function or delegate, but with behavior like that described by Chad. The syntax would be somthing like: int getX(){ return x;} void setX(int i){x = i;} property int X(&getX, &setX); the problem is that unlike in python we cannot use keywords to set paramaters so some syntax would have to be made for write only properties, maybe if we used inout instead of property: //read only in int x(&getX); //write only out int x(&setX); //read and write inout int x(&getX, &setX) any read would call the reading function (.reader of the property object) any write would call the writing function (.writer of the property object) so obj.x++ would be the same as: obj.x.writer(obj.x.reader() + 1) when read the property will actually return a special wrapper that sends itself to the writer whenever it is assigned to the address of the property cannot be used as a function or delegate, but can be used as the address of the type
Aug 18 2007
Reply to Ender,int getX(){ return x;} void setX(int i){x = i;} property int X(&getX, &setX); the problem is that unlike in python we cannot use keywords to set paramaters so some syntax would have to be made for write only properties, maybe if we used inout instead of property:property int X(void, &setX); property int Y(&getY, void); ?? I'm not sure I like this more than what we have, but it does leave open getting at the functions which I like. It might grow on me.
Aug 18 2007
"BCS" <ao pathlink.com> wrote in message news:ce0a3343d64a8c9afeb721a0ab0 news.digitalmars.com...Reply to Ender,Delphi does this. _foo int; int getFoo() { return _foo; }; vood setFoo(i: integer) {_foo = i; ); any of the following are valid... property int foo : read getFoo write setFoo; property int foo : read _foo write _foo; property int foo : read getFoo write _foo; property int foo : read _foo; ect.. I converted the Delphi syntax to be more D like, and its just offered for reference, not a proposal as such. So the property specifies either or both read / write, of which either or both can be direct acess to the field or a proxy function. The proxy functions can be virtual and so can be overriden in subclasses. So you get read only, write only, read/write, direct field acess, static function, or virtual function. And you can still expose the function if you want. jbint getX(){ return x;} void setX(int i){x = i;} property int X(&getX, &setX); the problem is that unlike in python we cannot use keywords to set paramaters so some syntax would have to be made for write only properties, maybe if we used inout instead of property:property int X(void, &setX); property int Y(&getY, void); ?? I'm not sure I like this more than what we have, but it does leave open getting at the functions which I like. It might grow on me.
Aug 19 2007
Jb wrote:"BCS" <ao pathlink.com> wrote in message news:ce0a3343d64a8c9afeb721a0ab0 news.digitalmars.com...I like where all of this is going. Then the solution becomes one which allows properties to be treated exactly* as members on the using side, yet allows the underlying functions to be used as well. So BCS, would you be willing to settle for such a solution? * I realize we may have to settle for properties being "close enough" to members, due to addressing.Reply to Ender,Delphi does this. _foo int; int getFoo() { return _foo; }; vood setFoo(i: integer) {_foo = i; ); any of the following are valid... property int foo : read getFoo write setFoo; property int foo : read _foo write _foo; property int foo : read getFoo write _foo; property int foo : read _foo; ect.. I converted the Delphi syntax to be more D like, and its just offered for reference, not a proposal as such. So the property specifies either or both read / write, of which either or both can be direct acess to the field or a proxy function. The proxy functions can be virtual and so can be overriden in subclasses. So you get read only, write only, read/write, direct field acess, static function, or virtual function. And you can still expose the function if you want. jbint getX(){ return x;} void setX(int i){x = i;} property int X(&getX, &setX); the problem is that unlike in python we cannot use keywords to set paramaters so some syntax would have to be made for write only properties, maybe if we used inout instead of property:property int X(void, &setX); property int Y(&getY, void); ?? I'm not sure I like this more than what we have, but it does leave open getting at the functions which I like. It might grow on me.
Aug 19 2007
Reply to Chad,I like where all of this is going. Then the solution becomes one which allows properties to be treated exactly* as members on the using side, yet allows the underlying functions to be used as well. So BCS, would you be willing to settle for such a solution?Let's say I wouldn't object to it. (I'm still not totally convinced something /needs/ to be done, but I do thing that it's worth looking at)* I realize we may have to settle for properties being "close enough" to members, due to addressing.You actually could make pointers to properties work just like members by placing the address of the member in invalid memory locations. Then on a seg-v, check if the address is attached to a property and stuff in the right value. I heard that at one point some computer systems used a something like that (but using illegal op codes) to fake having a floating point unit. (BTW, I am NOT suggesting this be used <G>)
Aug 19 2007
BCS wrote:Pretty clever! Too bad it seems very hardware dependent.* I realize we may have to settle for properties being "close enough" to members, due to addressing.You actually could make pointers to properties work just like members by placing the address of the member in invalid memory locations. Then on a seg-v, check if the address is attached to a property and stuff in the right value. I heard that at one point some computer systems used a something like that (but using illegal op codes) to fake having a floating point unit. (BTW, I am NOT suggesting this be used <G>)
Aug 20 2007
Chad J wrote:BCS wrote:It would also be hell to implement on x86 and similar architectures. Too many instructions that access memory, all needing to be emulated...Pretty clever! Too bad it seems very hardware dependent.* I realize we may have to settle for properties being "close enough" to members, due to addressing.You actually could make pointers to properties work just like members by placing the address of the member in invalid memory locations. Then on a seg-v, check if the address is attached to a property and stuff in the right value. I heard that at one point some computer systems used a something like that (but using illegal op codes) to fake having a floating point unit. (BTW, I am NOT suggesting this be used <G>)
Aug 20 2007
Ender KaShae wrote:Chad J Wrote:Or: property int X({ return x; }, (int i) { x = i; });If the proposed solution is not good enough, please give a better one. Suggestions are welcome and encouraged.I suggest something similar to python properties, for those who are not familior with it a property is created with property(getter, setter) so with my idea property (or inout or some other keyword) would be a new type, sort of like a function or delegate, but with behavior like that described by Chad. The syntax would be somthing like: int getX(){ return x;} void setX(int i){x = i;} property int X(&getX, &setX);the problem is that unlike in python we cannot use keywords to set paramaters so some syntax would have to be made for write only properties, maybe if we used inout instead of property: //read only in int x(&getX); //write only out int x(&setX); //read and write inout int x(&getX, &setX)Or: property int ReadonlyX({ return x; }, null); property int WriteonlyX(null, (int i) { x = i; });any read would call the reading function (.reader of the property object) any write would call the writing function (.writer of the property object)This is good -- getting the address of the getter and setter functions (&obj.X.reader) will be useful. And getting the address of the property will still allow you to get and set the property. Templates might have trouble with it, though.so obj.x++ would be the same as: obj.x.writer(obj.x.reader() + 1) when read the property will actually return a special wrapper that sends itself to the writer whenever it is assigned to the address of the property cannot be used as a function or delegate, but can be used as the address of the typeThe address of the property should be an address to a property struct, whose getters and setters would work as usual. Hm... --- class Property(T) { T* value; public void opIncrement() { *value++; } public void opDecrement() { *value--; } public T opAdd(U) (U addend) { return *value + addend; } // ... } class MyClass { private: int _x; public: Property!(int) X; this() { // slightly ugly... X = new Property!(int)(&_x); } } --- If you could overload the dot operator as well, then this and some template magic with __traits would give you everything you want. Without the dot operator, you get everything you need anyway for basic types. And classes are already taken care of. Structs are left out, though.
Aug 19 2007
Christopher Wright Wrote:The problem with that approach is it would be difficult to specify any special behavior for the properties (which is an important feature of properties), or am I just missing something. Actually something more like: ---- struct Property(Type){ Type delegate() getter; void delegate(Type) setter; Type opPostIncrement(){ scope temp = getter(); setter(getter() + 1); return temp; } etc... } would work pretty wellwhen read the property will actually return a special wrapper that sends itself to the writer whenever it is assigned to the address of the property cannot be used as a function or delegate, but can be used as the address of the typeThe address of the property should be an address to a property struct, whose getters and setters would work as usual. Hm... --- class Property(T) { T* value; public void opIncrement() { *value++; } public void opDecrement() { *value--; } public T opAdd(U) (U addend) { return *value + addend; } // ... } class MyClass { private: int _x; public: Property!(int) X; this() { // slightly ugly... X = new Property!(int)(&_x); } } --- If you could overload the dot operator as well, then this and some template magic with __traits would give you everything you want. Without the dot operator, you get everything you need anyway for basic types. And classes are already taken care of. Structs are left out, though.
Aug 19 2007