www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Properties sugestion.

reply "Jb" <jb nowhere.com> writes:
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
next sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
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
next sibling parent Chad J <gamerChad _spamIsBad_gmail.com> writes:
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.

Without some kind of runtime initializer for variables like C# has, this 
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
prev sibling parent "Jb" <jb nowhere.com> writes:
"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
prev sibling next sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
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.

Alternatively, we could just do what I found C# to do and make it a 
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
next sibling parent BCS <BCS pathlink.com> writes:
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
prev sibling next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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
parent Reiner Pope <some address.com> writes:
Chris Nicholson-Sauls wrote:
 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.

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. -- Reiner
Aug 22 2007
prev sibling parent reply Chad J <gamerChad _spamIsBad_gmail.com> writes:
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
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Chad J wrote:
 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.

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
Aug 22 2007
prev sibling parent serg kovrov <sergk mailinator.com> writes:
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