Thread overview
Question about operations on class/struct properties
Aug 18, 2014
Uranuz
Aug 18, 2014
anonymous
Aug 18, 2014
Phil Lavoie
Aug 18, 2014
Uranuz
Aug 18, 2014
Uranuz
Aug 20, 2014
Uranuz
August 18, 2014
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.
August 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.
August 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)
August 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
{

}

August 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
August 20, 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?