digitalmars.D.learn - Question about operations on class/struct properties
- Uranuz (32/32) Aug 18 2014 I think there is something that I don't understand about concept
- anonymous (7/18) Aug 18 2014 This comes up every once in a while. I don't know if there are
- Phil Lavoie (17/17) Aug 18 2014 All you said makes sense. If there is a direct connection between
I think there is something that I don't understand about concept of *properties*. I thing that property is sort of object attribute that belongs to it. Currently property considered as two functions: *get* and/or *set*. So we can do two sort of operations on concept that called *property*: *assign to it* and *read it*. If property doesn't return reference value all other manipulations are forbidden. A will illustrate it with example: import std.datetime, std.stdio; void main() { auto date = Date(1991, 5, 7); //date.day += 5; //Not working //date.day++; //Not working date.day = date.day + 1; } Because day property is of ubyte (not reference) type, we can only read it into some other variable or assign to it, but other other operations couldn't be done. It is a common case when I want to increment, decrement or using some other 'op=' - operation, but it is not working and I get compile-time error. I always was thinking that date.day++; date.day -= 5; Should be treated as: date.day = date.day + 1; date.day = date.day - 5; if the were not oveloaded. So if we have get and set property methods I see that it could be calculated and this should working. Of course I can define return value of get property as *ref* but in this case I don't understand how I should use it with *setter* method. It's interesting to see any thinkings about it.
Aug 18 2014
On Monday, 18 August 2014 at 15:35:26 UTC, Uranuz wrote:date.day++; date.day -= 5; Should be treated as: date.day = date.day + 1; date.day = date.day - 5; if the were not oveloaded. So if we have get and set property methods I see that it could be calculated and this should working.This comes up every once in a while. I don't know if there are any subtle problems, or if it's just that no one's found the time to implement it.Of course I can define return value of get property as *ref* but in this case I don't understand how I should use it with *setter* method.When you return a mutable reference, a setter doesn't make all that much sense, as it can easily be circumvented, even accidentally.
Aug 18 2014
All you said makes sense. If there is a direct connection between getter, setter and member than yes, returning it by reference is usually more convenient: private T _member; property ref inout(T) member() inout {return _member;} However, sometimes, there is no direct connection between field and mutators. Sometimes, it is calculated on the fly. Date { ... property auto hour() {return magic / someNumber;} property void hour(int newHour) {//complicated algorithm to set the hour.} } Now, assuming we have these two mutators, operators could be automagically implemented. Date myDate; myDate.hour += 4; //myDate.hour(myDate.hour() + 4)
Aug 18 2014
On Monday, 18 August 2014 at 18:07:09 UTC, Phil Lavoie wrote:All you said makes sense. If there is a direct connection between getter, setter and member than yes, returning it by reference is usually more convenient: private T _member; property ref inout(T) member() inout {return _member;} However, sometimes, there is no direct connection between field and mutators. Sometimes, it is calculated on the fly.Yes. As you said often it calculated on the fly. And sometimes when setting some value you need to trigger some *event handler*. This is one of use cases that properties were designed in different languages. Another case is to check value and throw exception or something else. So using getter that returns reference is very close to just exposing class/ struct field and allowing to modify it directly. I think that *setter* should shadow *ref getter* in opAssign and opOpAssign expressions. And opOpAssign should rewrite into *read -> execute operation -> write* sequence. Also I have another interesting question. For example I have some struct (value object) that has method, that modifies it's state. Then I declare some class/ struct that has property of value type. Problem is that logically I could expect that this method should modify property of class, but instead some value return from property method returned (by value) and then it is was modified. I'll give short illustration. struct PropType { }
Aug 18 2014
I posted it suddenly. I don't know why.I'll give short illustration.struct PropType { void append(int value) { //implementation } } class Test { PropType myProp() property { return _propValue; } private PropType _propValue; } void main() { Test test = new Test; //This line looks like I want to append value //to field of *test* object. But it cant't modify //it and just doing useless job. test.myProp.append(10); } I don't know intentions of language designers in part of properties. But in this code I see some logical contradiction. What I see and thinking of this line of code it is not what it really does. I don't know much about properties behaviour in different languages but there is something that confuses me. I believe that this problem deserves more attention. There are 5 DIP's in dlang wiki. So I think it's important
Aug 18 2014
I have another similar example illustrating this problem at semantic level. import std.stdio, std.typecons; struct Test { //ref Nullable!int prop() property { return _value; } private Nullable!int _value = 10; } void main() { auto test = Test(); //This looks like I want to modify property with *nullify*, //but instead it creates variable that will never be used and modify it test.prop.nullify(); assert( test.prop.isNull ); //This fails } So when using with value types I should always look if property is *ref* or not and it's not very good. I have another question. How could I implement in this example if I uncomment it to call some function after modifying property? Do I need some sort of class wrapper for property or is there some other ideas? What I expect: void main() { auto test = Test(); test.prop.nullify(); //Callback called after this operation test.prop = 15; //Callback called after this operation too } Callback is simple method of Test like: void callback(Nullable!int value) { if( value.isNull ) //Do smth else if( value <= 0 ) //Do smth else else //Just assign to internal field _value = value; } So I think it is possible to do this with some class wrapper around Nullable!int that should implement al of it's methods or for example dispatching calls to them via opDispacth. But this approach looks slightly complicated. What if I have complicated interface (for example std.datetime.Date as such property) so I need to reimplement or dispatch a lot of methods. Could you advise another more simple approach to this problem?
Aug 19 2014