digitalmars.D - Properties sugestion.
- Jb (20/20) Aug 20 2007 class Foo
- Chad J (14/44) Aug 20 2007 With all of the operator overloads, this is going to get way to verbose
- Chad J (19/19) Aug 20 2007 OK well the template solution might be a nice hack to get by for now, it...
- Jb (11/17) Aug 20 2007 In Delphi theres not much to see tbh. You specify a name and tell it eit...
- Chad J (94/94) Aug 21 2007 OK how about this:
- BCS (13/22) Aug 21 2007 How about allow it like this:
- Chris Nicholson-Sauls (53/60) Aug 22 2007 I like the idea of re-using in/out/inout, but I think a new 'property' k...
- Reiner Pope (24/39) Aug 22 2007 I just want to say that I don't think this precedent is a good one. It
- Chad J (14/22) Aug 22 2007 I just realized that such a thing doesn't work at all, because nothing
- Chris Nicholson-Sauls (11/36) Aug 22 2007 I hadn't even thought about the addressing issue when I posted that. Bu...
- serg kovrov (37/37) Aug 23 2007 In a nutshell, current implementation of properties is just implicitly
class Foo { int _foo; property Bar { void opAssign(int i) { _foo = i;} int opRValue() { return _foo; } int OpPostInc() { _foo++; return _foo-1; } } property Baz { void opRValue() { return _foo*2; } } } I'll admit i didnt follow all of the points in the original thread but it got me thinking that why not re-use operator overloading syntax to define the operators that can (or cant) be used with a property. Perhaps require that the correct one be implemented when the property is used as an lvalue? Just those problematic operators that cause ambiguity could just cause a compiler error if used when not implemented. If you want to get a delegate to the function you could simply address the operator function... &Foo.Width.opAssign;
Aug 20 2007
Jb wrote:class Foo { int _foo; property Bar { void opAssign(int i) { _foo = i;} int opRValue() { return _foo; } int OpPostInc() { _foo++; return _foo-1; } } property Baz { void opRValue() { return _foo*2; } } } I'll admit i didnt follow all of the points in the original thread but it got me thinking that why not re-use operator overloading syntax to define the operators that can (or cant) be used with a property. Perhaps require that the correct one be implemented when the property is used as an lvalue? Just those problematic operators that cause ambiguity could just cause a compiler error if used when not implemented. If you want to get a delegate to the function you could simply address the operator function... &Foo.Width.opAssign;With all of the operator overloads, this is going to get way to verbose every time someone wants to write a property. Mixin templates could help, but would still be unnecessary. lvalueness is a sane default behavior. It's sort of like why D doesn't have C/C++'s -> operator, no one would want to write "auto temp = foo(); temp++;" where foo is the expanded form of a property. They almost always mean "auto temp = foo(); foo( temp + 1);". It would be nice though, to allow this kind of suggested syntax as a means of overriding default behavior. I'll be trying out some template stuff, the kind of thing that was happening at the end of the original thread. Perhaps this problem can simply be converted to a problem of lacking opDot, though that is somewhat unlikely. If that doesn't pan out just perfectly, then I'll also be looking at python and delphi properties.
Aug 20 2007
OK well the template solution might be a nice hack to get by for now, it has some unfortunate shortcomings: ----- "Future Directions The operators !, ., &&, ||, ?:, and a few others will likely never be overloadable. " Too bad for boolean properties. opDot might be on that list too. ----- The template solutions provided by Ender KaShae and Christopher Wright both need runtime intervention. They will also likely result in excess memory usage, which is a minor optimization issue. also means polluting constructors and the like with property initialization. ----- They only take delegates. This leaves out free functions and static methods. It might be fixable with more potent template magic though. ----- Onwards to Python and Delphi suggestions...
Aug 20 2007
"Chad J" <gamerChad _spamIsBad_gmail.com> wrote in message news:fad35s$1p2h$1 digitalmars.com...Jb wrote:I'll be trying out some template stuff, the kind of thing that was happening at the end of the original thread. Perhaps this problem can simply be converted to a problem of lacking opDot, though that is somewhat unlikely. If that doesn't pan out just perfectly, then I'll also be looking at python and delphi properties.In Delphi theres not much to see tbh. You specify a name and tell it either a variable to read and or write, or a getter and or setter method, thats about it. It avoids the op++ problem by simply making it illegal, the delphi equivelant of op++ is Inc(foo); But you cant use that on a property..., You can't pass a property as a var parameter, So it's probably only much different to D's current state in that its much stricter on what you can and cant do with them.
Aug 20 2007
OK how about this: Type _foo; Type getFoo() { return _foo; } void setFoo(Type val) { _foo = val; } property Type foo(getFoo,setFoo); // or &getFoo and &setFoo if you prefer // also for readonly and writeonly: property Type foo(getFoo,void); property Type foo(void,setFoo); In this incantation, properties are a compile-time construct. It just tells the compiler that we have something called "foo" which is really a pair of functions, but we want it to behave exactly like a data field. Also note the typing, this is to ensure that the property has a uniform type, since data fields don't have different types for when you read and write to them. The getter and setter functions must have types that are the same as or implicitly convertible to/from the property's type. Thus the following is correct: int _foo; short getFoo() { return cast(short)_foo; } void setFoo(long val) { _foo = cast(int)val; } property int foo(getFoo,setFoo); // calling code might work like so int bar = foo; // works, since int bar = getFoo(); works foo = bar; // works, since setFoo( bar ); works But if getFoo had been written this way: float getFoo() { return cast(float)_foo; } Then the compiler would give an error. Another possible syntax (without new keywords, almost looks better too): in Type foo(getFoo); // RO property out Type foo(setFoo); // WO property inout Type foo(getFoo,setFoo); // RW property So far this is pretty much what Ender KaShae suggested in the original thread. There is also the possibility to trap certain operations on the property: inout Type foo(getFoo,setFoo) { Type opPostInc() { scope temp = getFoo(); temp += 4; // Different behavior setFoo( temp ); return temp; } } thus opPostInc() would override the default language-defined opPostInc() for the property. I'd also suggest that taking the address of a property returns a pointer to a delegate or function pointer to the setter. Another thing that would be useful then is to allow the setter to (maybe) return a value. That way the setter can act as a getter when in a pinch. Now we can write template and generic code that take addresses of members and be mostly unaware of the difference between properties and data fields: // baz is just some function. This is on the user's side of the prop. T baz(T)(Bar arg) { auto ptr = &arg.foo; // foo is a property, a data field works too T val = *ptr; // calls foo.setFoo(typeof(foo).init); return val; } Where "T val = *ptr;" expands to the following: typeof(foo) delegate(typeof(off)) temp = *ptr; T val = temp; // same as T val = temp(); by implicit properties. - Not supplying a return type for the setter function would make taking the address of the given property into a compile-time error. - The return type for the setter function would be under the same type-strictness scrutiny as the return type for the getter function. Also note that the above use of delegates plays on the current property syntax. Without implicit properties, the example would look like this: T baz(T)(Bar arg) { auto ptr = &arg.foo; T val = *ptr(typeof(foo).init); // fails if foo is a data field. return val; } If the setter of the property does not have a return value, it would be a compile time error to take the address of said property. Should a user want to have the address operation return something else, like a pointer to the data underlying the property, then perhaps there should be an "opAddressOf" operator overload that is available only for properties. compile time error to take the address of a property. Another issue might be whether or not to let properties override each other. This is in the case of classes with inheritance. Initially, I'd say no. It is more likely that the getter and/or setter function(s) are what should be overridden. Well I'm soaking up my time and I need to pack for D con, so I'll cut it short on describing what these beasts should expand to. They should expand to whatever makes them behave as closely to data fields as possible from the property user's perspective. - Chad
Aug 21 2007
Chad J wrote:OK how about this:[...]In this incantation, properties are a compile-time construct. It just tells the compiler that we have something called "foo" which is really a pair of functions, but we want it to behave exactly like a data field.How about allow it like this: property Type Name{ ListOfFunctions }; where ListOfFunctions may contain one "Type fn(void)" and any number of "Type/void fn(Type2)" so long as the overload rules can unambiguously select one of them for all types. The value of an assignment to a property would be the return, if any, of the given function. The value of the property as a strict R-value would be the fn(void), if it exists. This wouldn't work exactly like a member but, with a little care, would be darn close. I like the override of opXxxAssign to. [...]- Chad
Aug 21 2007
Chad J wrote:Another possible syntax (without new keywords, almost looks better too): in Type foo(getFoo); // RO property out Type foo(setFoo); // WO property inout Type foo(getFoo,setFoo); // RW property So far this is pretty much what Ender KaShae suggested in the original thread.I like the idea of re-using in/out/inout, but I think a new 'property' keyword and a pair of braces are also a good idea. Properties really ought to be whole entities (encapsulation), or at least it seems so to me -- and then maybe we could re-use the precedent established with templates (that of a member with the same name as the template). Then use operator overloads to do the rest. property ulong area { ulong area () { return width * height; } ulong opAssign (ulong x) { ulong xx = x / 2_UL ; width = xx; height = x - xx; return area; } ulong opAssign (char[] x) { area = toULong(x); } } Etc. This could be treated internally by D as just a struct which calls the self-named function (which it would be an error to omit) when there is no appropriate operator overload to use. Hmm. If the self-named function is required, maybe the syntax could account for it: class Rect { ulong width, height ; property ulong area { return width * height; }{ void opAssign (ulong x) { ulong xx = x / 2_UL ; width = xx; height = x - xx; } void opAssign (char[] x) { area = toULong(x); } } } Okay, so a double pair of braces is a bit odd... so are the double parentheses used in function templates. :) Speaking of templates... template MMult2Prop (alias A, alias B) { property typeof(A) MMult2Prop { return A * B; }{ void opAssign (typeof(A) x) { typeof(A) xx = x / 2; A = xx; B = x - xx; } } } class Rect { ulong width, height ; mixin MMult2Prop!(width, height) area ; } I haven't really been following the latest property discussion too close, so forgive me if something similar has come up already. -- Chris Nicholson-Sauls
Aug 22 2007
Chris Nicholson-Sauls wrote:Chad J wrote:I just want to say that I don't think this precedent is a good one. It has the annoying problems of effectively only allowing one member (admittedly, not an inherent problem of the idea) and of requiring you to repeat the name inside the template. Consider writing a meta-programming template: (off-the-cuff, sorry if it's wrong, or a little roundabout) template IndexOf(T, U...) { static if (U.length == -1) const IndexOf = -1; else static if (is(T == U[0])) const IndexOf = 0; else static if (IndexOf!(T, U[1..$]) == -1) const IndexOf = -1; else const IndexOf = 1 + IndexOf!(T, U[1..$]); } For every "return" statement, you have to write the template's name. It makes renaming the template a pain. Contrast this with the nice thing D has done with the constructor, calling it "this()" -- there's no reason the constructor needs to know the class's name, so it shouldn't syntactically be there; and it really makes refactoring easier. -- ReinerAnother possible syntax (without new keywords, almost looks better too): in Type foo(getFoo); // RO property out Type foo(setFoo); // WO property inout Type foo(getFoo,setFoo); // RW property So far this is pretty much what Ender KaShae suggested in the original thread.I like the idea of re-using in/out/inout, but I think a new 'property' keyword and a pair of braces are also a good idea. Properties really ought to be whole entities (encapsulation), or at least it seems so to me -- and then maybe we could re-use the precedent established with templates (that of a member with the same name as the template). Then use operator overloads to do the rest.
Aug 22 2007
Chad J wrote:// baz is just some function. This is on the user's side of the prop. T baz(T)(Bar arg) { auto ptr = &arg.foo; // foo is a property, a data field works too T val = *ptr; // calls foo.setFoo(typeof(foo).init); return val; }I just realized that such a thing doesn't work at all, because nothing would expand to the "typeof(foo).init" that is passed to the setter. So the idea of returning a delegate from an address-of-property probably isn't useful at all in this context. The notion of having the thing returned from address-of-property be something that templates could work with as if it were a data field, at least syntactically, still might have some potential though. Even better if it doesn't rely on implicit properties. In an adjacent post, Chris Nicholson-Sauls suggested that properties could be "treated internally by D as just a struct which calls the self-named function". Perhaps if such a struct was revealed as some kind of special internal library built in struct then it could be returned as a property's address by default.
Aug 22 2007
Chad J wrote:Chad J wrote:I hadn't even thought about the addressing issue when I posted that. But as you seem to say, it might lead to a solution. The only issue with the design, is that properties effectively become a new UD-aggregate type alongside class/struct/union -- which might be a benefit sometimes, but might also become yet another thing to consider when writing templates. At the very least, though, we could get a compiler error about missing operator overloads if the template did something that wasn't accounted for. Assuming the user wrote the property, he/she could add that operator if its important enough. Properties whose value is itself an aggregate (including arrays), or a reference, might just have their own issues regardless of the design. -- Chris Nicholson-Sauls// baz is just some function. This is on the user's side of the prop. T baz(T)(Bar arg) { auto ptr = &arg.foo; // foo is a property, a data field works too T val = *ptr; // calls foo.setFoo(typeof(foo).init); return val; }I just realized that such a thing doesn't work at all, because nothing would expand to the "typeof(foo).init" that is passed to the setter. So the idea of returning a delegate from an address-of-property probably isn't useful at all in this context. The notion of having the thing returned from address-of-property be something that templates could work with as if it were a data field, at least syntactically, still might have some potential though. Even better if it doesn't rely on implicit properties. In an adjacent post, Chris Nicholson-Sauls suggested that properties could be "treated internally by D as just a struct which calls the self-named function". Perhaps if such a struct was revealed as some kind of special internal library built in struct then it could be returned as a property's address by default.
Aug 22 2007
In a nutshell, current implementation of properties is just implicitly calling function with same name and corresponding parameters. It's not that bad, just need to be extended. 1. give to programmer means to identify if function could be used as property. A 'property' keyword seems obvious solution. 2. introduce syntax for append/increment/decrement property. I believe that's all we really need. For aesthetic pleasure this 'property' keyword could be used as special scope to group functions belonging together. But this block should be optional. Something like: class A { int m_p1; property int p1() {return m_p;} // getter property int p1(int i) {return m_p = i;} // setter real m_p2; property // full blown property { // getter real p2(real val) {return m_p2;} // setter real p2(real val) {m_p2 = val;} // append/increment/decrement real p2(bool increment, real val=1.0) { if (increment) m_p2 += val; else m_p2 -= val; } } } Tat way `a.p2++` could be translated to `a.p2(true)`, `a.p2--` to `a.p2(false)`, `a.p2 += 2.0` to `a.p2(true, 2.0)`, etc... The trick here is to distinguish `a.p++` from `a.p = true`, but I believe it's solvable. -- serg.
Aug 23 2007