www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Question about operations on class/struct properties

reply "Uranuz" <neuranuz gmail.com> writes:
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
next sibling parent "anonymous" <anonymous example.com> writes:
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
prev sibling parent reply "Phil Lavoie" <maidenphil hotmail.com> writes:
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
parent reply "Uranuz" <neuranuz gmail.com> writes:
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
parent reply "Uranuz" <neuranuz gmail.com> writes:
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
parent "Uranuz" <neuranuz gmail.com> writes:
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