www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using ()s in property functions

reply dsimcha <dsimcha yahoo.com> writes:
Once enforcement of  property is enabled, we need to decide whether calling an
 property function using ()s should be legal.  In other words, should
 property **require** omission of ()s or just allow it?  My vote is for just
allowing omission, because I've run into the following ambiguity while
debugging std.range.  Here's a reduced test case:

struct Foo {
    uint num;

     property ref uint front() {
        return num;
    }
}

void main() {
    Foo foo;
    uint* bar = &foo.front;  // Tries to return a delegate.
}

If I can assume that  property functions can be called with explicit ()s to
forcibly disambiguate this situation, then I can fix these kinds of bugs by
simply doing a:

uint* bar = &(foo.front());

Can we finalize the idea that this will continue to be allowed now so that I
can fix the relevant bugs in Phobos and know that my fix won't be broken in a
few compiler releases?
Jun 28 2010
next sibling parent reply Rainer Deyke <rainerd eldwood.com> writes:
On 6/28/2010 20:40, dsimcha wrote:
 Once enforcement of  property is enabled, we need to decide whether calling an
  property function using ()s should be legal.

No, "we" don't. Walter does.
  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:
 
 struct Foo {
     uint num;
 
      property ref uint front() {
         return num;
     }
 }
 
 void main() {
     Foo foo;
     uint* bar = &foo.front;  // Tries to return a delegate.
 }

Allowing parentheses also leads to ambiguity: struct Foo { uint num; property int delegate() front() { return delegate (){ return 5; }; } } void main() { Foo foo; auto bar = foo.front(); // What is the type of 'bar'? } The problem is that 'front' refers to two things: the property and its accessor function. So long as this is the case, ambiguity is unavoidable. -- Rainer Deyke - rainerd eldwood.com
Jun 28 2010
next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 00:02:32 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:

 On 6/28/2010 20:40, dsimcha wrote:
 Once enforcement of  property is enabled, we need to decide whether  
 calling an
  property function using ()s should be legal.

No, "we" don't. Walter does.

No, Walter doesn't. Walter and others wanted zero and one argument functions to be allowed to behave like properties which is entirely different from properties behaving like functions.
Jun 28 2010
prev sibling next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 00:02:32 -0400, Rainer Deyke <rainerd eldwood.com>  
wrote:
 On 6/28/2010 20:40, dsimcha wrote:
  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for  
 just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:

 struct Foo {
     uint num;

      property ref uint front() {
         return num;
     }
 }

 void main() {
     Foo foo;
     uint* bar = &foo.front;  // Tries to return a delegate.
 }

Allowing parentheses also leads to ambiguity: struct Foo { uint num; property int delegate() front() { return delegate (){ return 5; }; } } void main() { Foo foo; auto bar = foo.front(); // What is the type of 'bar'? } The problem is that 'front' refers to two things: the property and its accessor function. So long as this is the case, ambiguity is unavoidable.

So... If we allow properties to be called using () syntax, we have corner case ambiguities. And if we don't allow properties to be called using () syntax, we have corner case ambiguities and a minor loss of function (because one ambiguity can't be expressed). Add in Andrei's posts about the difficulty in deciding what methods should and shouldn't be properties, and the more it seems that the real world experience with properties is indicating that property is a mis-feature that should be dropped from the language.
Jun 28 2010
parent "Nick Sabalausky" <a a.a> writes:
"Robert Jacques" <sandford jhu.edu> wrote in message 
news:op.ve1p6xrt26stm6 sandford.myhome.westell.com...
 On Tue, 29 Jun 2010 00:02:32 -0400, Rainer Deyke <rainerd eldwood.com> 
 wrote:
 On 6/28/2010 20:40, dsimcha wrote:
  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for 
 just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:

 struct Foo {
     uint num;

      property ref uint front() {
         return num;
     }
 }

 void main() {
     Foo foo;
     uint* bar = &foo.front;  // Tries to return a delegate.
 }

Allowing parentheses also leads to ambiguity: struct Foo { uint num; property int delegate() front() { return delegate (){ return 5; }; } } void main() { Foo foo; auto bar = foo.front(); // What is the type of 'bar'? } The problem is that 'front' refers to two things: the property and its accessor function. So long as this is the case, ambiguity is unavoidable.

So... If we allow properties to be called using () syntax, we have corner case ambiguities. And if we don't allow properties to be called using () syntax, we have corner case ambiguities and a minor loss of function (because one ambiguity can't be expressed). Add in Andrei's posts about the difficulty in deciding what methods should and shouldn't be properties, and the more it seems that the real world experience with properties is indicating that property is a mis-feature that should be dropped from the language.

Hell no. It's far better than what we had before, and far better than just simply not having properties. I wouldn't mind improvements, but as for ditching it: there's a baby in that bathwater.
Jun 28 2010
prev sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 01:38:45 -0400, Nick Sabalausky <a a.a> wrote:
 "Robert Jacques" <sandford jhu.edu> wrote in message

 So... If we allow properties to be called using () syntax, we have  
 corner
 case ambiguities. And if we don't allow properties to be called using ()
 syntax, we have corner case ambiguities and a minor loss of function
 (because one ambiguity can't be expressed). Add in Andrei's posts about
 the difficulty in deciding what methods should and shouldn't be
 properties, and the more it seems that the real world experience with
 properties is indicating that  property is a mis-feature that should be
 dropped from the language.

Hell no. It's far better than what we had before, and far better than just simply not having properties. I wouldn't mind improvements, but as for ditching it: there's a baby in that bathwater.

What baby? property was introduced solely to clarify the ambiguity of the corner case of returning a zero-argument delegate from a method. That's it. Methods-as-properties existed long before the property was proposed. (I'm not suggesting in any way that methods-as-properties should be thrown out, because that really would be throwing the baby out with the bath water.) P.S. The other reason for introducing the property was to allow a library writer to force certain coding standards on the library users. Some like this, some hate this. My count had a con from every pro, but not a pro for every con. However, the pro-less cons were mostly mitigated by leaving in methods-as-properties, making the final verdict one of those agree to disagree compromises.
Jun 28 2010
prev sibling next sibling parent reply "Nick Sabalausky" <a a.a> writes:
"dsimcha" <dsimcha yahoo.com> wrote in message 
news:i0bme6$2phb$1 digitalmars.com...
 Once enforcement of  property is enabled, we need to decide whether 
 calling an
  property function using ()s should be legal.  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for 
 just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:

 struct Foo {
    uint num;

     property ref uint front() {
        return num;
    }
 }

 void main() {
    Foo foo;
    uint* bar = &foo.front;  // Tries to return a delegate.
 }

 If I can assume that  property functions can be called with explicit ()s 
 to
 forcibly disambiguate this situation, then I can fix these kinds of bugs 
 by
 simply doing a:

 uint* bar = &(foo.front());

 Can we finalize the idea that this will continue to be allowed now so that 
 I
 can fix the relevant bugs in Phobos and know that my fix won't be broken 
 in a
 few compiler releases?

Crazy idea: The whole point of properties is to simulate a member that's *not* a function. With that in mind, does it even make sense to allow the use of unary "&" to get a delegate to a property at all? On the off-chance that you really do need a delegate to a setter/getter you can just make a lambda - and that works exactly the same even if it's a real member variable instead of a property. And inlining could take care of any performance issues. Somewhat related question: What normally happens when you try to get a delegate to an overloaded function?
Jun 28 2010
next sibling parent reply "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 01:53:05 -0400, Nick Sabalausky <a a.a> wrote:

 "dsimcha" <dsimcha yahoo.com> wrote in message
 news:i0bme6$2phb$1 digitalmars.com...
 Once enforcement of  property is enabled, we need to decide whether
 calling an
  property function using ()s should be legal.  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for
 just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:

 struct Foo {
    uint num;

     property ref uint front() {
        return num;
    }
 }

 void main() {
    Foo foo;
    uint* bar = &foo.front;  // Tries to return a delegate.
 }

 If I can assume that  property functions can be called with explicit ()s
 to
 forcibly disambiguate this situation, then I can fix these kinds of bugs
 by
 simply doing a:

 uint* bar = &(foo.front());

 Can we finalize the idea that this will continue to be allowed now so  
 that
 I
 can fix the relevant bugs in Phobos and know that my fix won't be broken
 in a
 few compiler releases?

Crazy idea: The whole point of properties is to simulate a member that's *not* a function. With that in mind, does it even make sense to allow the use of unary "&" to get a delegate to a property at all? On the off-chance that you really do need a delegate to a setter/getter you can just make a lambda - and that works exactly the same even if it's a real member variable instead of a property. And inlining could take care of any performance issues.

1. You'd get a closure, not a lambda, which generally would require a heap allocation. 2. The idea's not too crazy, I've been having similar thoughts. However, 3. D's a system's programming language and not being able to get a function pointer to a function seems wrong. 4. There's actually valid use cases where getting the address of a property makes sense. One of the intents of properties is to allow a library to upgrade a variable to a set of methods without breaking third party user code. And in the new version of your library, taking the address might make sense or be needed due to new functionality.
 Somewhat related question: What normally happens when you try to get a
 delegate to an overloaded function?

It grabs the first overload, IIRC.
Jun 28 2010
parent "Nick Sabalausky" <a a.a> writes:
"Robert Jacques" <sandford jhu.edu> wrote in message 
news:op.ve1us1xj26stm6 sandford.myhome.westell.com...
 On Tue, 29 Jun 2010 01:53:05 -0400, Nick Sabalausky <a a.a> wrote:

 Crazy idea:

 The whole point of properties is to simulate a member that's *not* a
 function. With that in mind, does it even make sense to allow the use of
 unary "&" to get a delegate to a property at all? On the off-chance that 
 you
 really do need a delegate to a setter/getter you can just make a lambda -
 and that works exactly the same even if it's a real member variable 
 instead
 of a property. And inlining could take care of any performance issues.

1. You'd get a closure, not a lambda, which generally would require a heap allocation. 2. The idea's not too crazy, I've been having similar thoughts. However, 3. D's a system's programming language and not being able to get a function pointer to a function seems wrong. 4. There's actually valid use cases where getting the address of a property makes sense. One of the intents of properties is to allow a library to upgrade a variable to a set of methods without breaking third party user code. And in the new version of your library, taking the address might make sense or be needed due to new functionality.
 Somewhat related question: What normally happens when you try to get a
 delegate to an overloaded function?

It grabs the first overload, IIRC.

Then maybe what we need is a way to specify what overload of a function you want to get a reference to. Something along these lines (disregard the exact syntax, I'm looking more at the semantics): ref int foo() {/*...*/} // #1 ref int foo(int x, string y) {/*...*/} // #2 main() { auto a = &foo; // Either same behavior as now, or disallowed for overloaded funcs, or a collection of delegates auto b = &foo(); // Address of return value of #1 auto c = &foo(1, "foo"); // Address of return value of #2 auto d = &foo(void); // Delegate to #1 auto e = &foo(int, string); // Delegate to #2 } Then, that would open the door for this: class Foo { property ref uint foo() {/*...*/} } main() { auto f = new Foo(); auto a = &f.foo; // Address of returned value auto b = &f.foo(void); // Delegate to getter //auto c = &f.foo(uint); // If exists, delegate to setter }
Jun 29 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 29 Jun 2010 01:53:05 -0400, Nick Sabalausky <a a.a> wrote:

 "dsimcha" <dsimcha yahoo.com> wrote in message
 news:i0bme6$2phb$1 digitalmars.com...
 Once enforcement of  property is enabled, we need to decide whether
 calling an
  property function using ()s should be legal.  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for
 just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:

 struct Foo {
    uint num;

     property ref uint front() {
        return num;
    }
 }

 void main() {
    Foo foo;
    uint* bar = &foo.front;  // Tries to return a delegate.
 }

 If I can assume that  property functions can be called with explicit ()s
 to
 forcibly disambiguate this situation, then I can fix these kinds of bugs
 by
 simply doing a:

 uint* bar = &(foo.front());

 Can we finalize the idea that this will continue to be allowed now so  
 that
 I
 can fix the relevant bugs in Phobos and know that my fix won't be broken
 in a
 few compiler releases?

Crazy idea: The whole point of properties is to simulate a member that's *not* a function. With that in mind, does it even make sense to allow the use of unary "&" to get a delegate to a property at all? On the off-chance that you really do need a delegate to a setter/getter you can just make a lambda - and that works exactly the same even if it's a real member variable instead of a property. And inlining could take care of any performance issues.

I don't think a delegate to a property is a common need. Besides, you would almost expect something that accesses both read/write properties. I'm sure someone can come up with a way to do this in a type-agnostic way (a "property delegate" if you will...) If one truly needs a delegate to a property, why not use __traits/meta to do it? meta.delegateOf(foo.front); This could actually solve the issue of how to get a specific delegate: struct S { void foo(int x); void foo(string s); } S s; auto d1 = meta.delegateOf(s.foo, int); auto d2 = meta.delegateOf(s.foo, string); -Steve
Jun 29 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 28 Jun 2010 22:40:06 -0400, dsimcha <dsimcha yahoo.com> wrote:

 Once enforcement of  property is enabled, we need to decide whether  
 calling an
  property function using ()s should be legal.  In other words, should
  property **require** omission of ()s or just allow it?

Require it. Otherwise, we are left with the same situation as before. We already had optional parentheses for properties, and it was/is an ambiguity disaster. It's the whole reason for introducing property in the first place.
  My vote is for just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:

 struct Foo {
     uint num;

      property ref uint front() {
         return num;
     }
 }

 void main() {
     Foo foo;
     uint* bar = &foo.front;  // Tries to return a delegate.
 }

 If I can assume that  property functions can be called with explicit ()s  
 to
 forcibly disambiguate this situation, then I can fix these kinds of bugs  
 by
 simply doing a:

 uint* bar = &(foo.front());

 Can we finalize the idea that this will continue to be allowed now so  
 that I
 can fix the relevant bugs in Phobos and know that my fix won't be broken  
 in a
 few compiler releases?

If I'm reading this correctly, you are saying that you want &foo.front to return a pointer to uint, not a delegate? This is what I'd expect if property forced no parentheses. That is, foo.front can be replaced with (foo.front()) always, so &foo.front always translates to &(foo.front()). What this means is that you can't get a delegate to the property function. This makes sense -- if you replaced the property with an actual field, you wouldn't be able to get a delegate anyways. A property should operate just like a field. -Steve
Jun 29 2010
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 06/29/2010 07:16 AM, Steven Schveighoffer wrote:
 If I'm reading this correctly, you are saying that you want &foo.front
 to return a pointer to uint, not a delegate?
 
 This is what I'd expect if  property forced no parentheses.  That is,
 foo.front can be replaced with (foo.front()) always, so &foo.front
 always translates to &(foo.front()).
 
 What this means is that you can't get a delegate to the property
 function.  This makes sense -- if you replaced the property with an
 actual field, you wouldn't be able to get a delegate anyways.  A
 property should operate just like a field.
 
 -Steve

That's the spirit of it. Properties are fields, except much more powerful but addressing is tricky. There is one thing that bugs me about this solution though. What if the user does this: (1) Grab the pointer. *ptr = prop; (2) assigns to it. *ptr = val; (3) expects the result to be updated in prop. assert(val == prop);
Jun 29 2010
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Whoops, made a mistake.

On 06/29/2010 08:03 AM, Chad J wrote:
 On 06/29/2010 07:16 AM, Steven Schveighoffer wrote:
 If I'm reading this correctly, you are saying that you want &foo.front
 to return a pointer to uint, not a delegate?

 This is what I'd expect if  property forced no parentheses.  That is,
 foo.front can be replaced with (foo.front()) always, so &foo.front
 always translates to &(foo.front()).

 What this means is that you can't get a delegate to the property
 function.  This makes sense -- if you replaced the property with an
 actual field, you wouldn't be able to get a delegate anyways.  A
 property should operate just like a field.

 -Steve

That's the spirit of it. Properties are fields, except much more powerful but addressing is tricky. There is one thing that bugs me about this solution though. What if the user does this: (1) Grab the pointer. *ptr = prop;

 (2) assigns to it.  *ptr = val;
 (3) expects the result to be updated in prop.  assert(val == prop);

Jun 29 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella 
 <luca llucax.com.ar> wrote:
 
 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What 


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

It returns a ref. That can't be a calculated value. If it's a calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei
Jun 29 2010
next sibling parent BLS <windevguy hotmail.de> writes:
On 29/06/2010 17:44, Andrei Alexandrescu wrote:
 struct A { int x;  property ref y() { return x; } }

should be : struct A { int x; property ref int y() { return x; } } or am I wrong ? bjoern
Jun 29 2010
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Robert Jacques wrote:
 On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella 
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What 


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

Okay, but what about non-ref properties? i.e. struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?

I don't think you should be able to even take the address of a non-ref property. Andrei
Jun 29 2010
next sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 06/30/2010 12:33 AM, Robert Jacques wrote:
 On Tue, 29 Jun 2010 23:41:48 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 .
 Robert Jacques wrote:
 On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though. 


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val ==



Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?

I don't think you should be able to even take the address of a non-ref property. Andrei

I agree with you from a under-the-hood perspective, but I wasn't asking about that. I was asking about the leak in the property abstraction. Not being able to pass non-ref properties to functions by ref is a fairly serious (i.e. common) break/leak in the property abstraction: that properties should be "virtually indistinguishable from a field".

ref parameters are easily handled by property expression rewriting: void foo( ref bar ) { ... } foo(prop) becomes auto t = prop() foo(t) prop(t) I would handle "out" parameters similarly, but without calling the getter. They are essentially a different way of writing an assignment. Taking the /address/ is a bit different, since there may be no well-defined line of code at which the pointer holding the address dies. The ref parameter, on the other hand, WILL expire when the function returns.
Jun 29 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Chad J wrote:
 On 06/30/2010 12:33 AM, Robert Jacques wrote:
 On Tue, 29 Jun 2010 23:41:48 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 .
 Robert Jacques wrote:
 On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
 There is one thing that bugs me about this solution though. 


 user does this:
 (1) Grab the pointer.  *ptr = prop;

 (2) assigns to it.  *ptr = val;
 (3) expects the result to be updated in prop.  assert(val ==


Why would this assert fail? If a property returns a ref


calculated value then T* ptr = &prop will fail to compile.

the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?

property. Andrei

about that. I was asking about the leak in the property abstraction. Not being able to pass non-ref properties to functions by ref is a fairly serious (i.e. common) break/leak in the property abstraction: that properties should be "virtually indistinguishable from a field".

ref parameters are easily handled by property expression rewriting: void foo( ref bar ) { ... } foo(prop) becomes auto t = prop() foo(t) prop(t)

I think that would be very error-prone. Andrei
Jun 29 2010
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Robert Jacques wrote:
 On Tue, 29 Jun 2010 23:41:48 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 ..
 Robert Jacques wrote:
 On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella 
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == 



Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?

I don't think you should be able to even take the address of a non-ref property. Andrei

I agree with you from a under-the-hood perspective, but I wasn't asking about that. I was asking about the leak in the property abstraction. Not being able to pass non-ref properties to functions by ref is a fairly serious (i.e. common) break/leak in the property abstraction: that properties should be "virtually indistinguishable from a field".

Indeed properties with separate get and set that don't expose references are quite different from fields. Those that return ref are more similar to fields. The basic goal is to bring both as close as possible to field syntax and semantics. Andrei
Jun 29 2010
prev sibling parent reply sclytrack <sclytrack fake.com> writes:
C# doesn't allow ref on properties.

void doStuff( ref int a) {a++;}
doStuff(obj.NumberProperty);

//Compiler magic. Stores in local variables behind the scenes. Calls getter and
setter.
Jul 07 2010
parent sclytrack <sclytrack sorry.com> writes:
== Quote from sclytrack (sclytrack fake.com)'s article
 C# doesn't allow ref on properties.
 void doStuff( ref int a) {a++;}
 doStuff(obj.NumberProperty);
 //Compiler magic. Stores in local variables behind the scenes. Calls getter and
 setter.

Sorry about that, it does not appear to work, I should test the code before I post.
Jul 07 2010
prev sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 06/29/2010 11:38 AM, Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella
 <luca llucax.com.ar> wrote:
 
 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

It returns a ref. That can't be a calculated value. If it's a calculated value then T* ptr = &prop will fail to compile.
 then you
 set through that ref.  What you did is no different, you just
 delayed the assignment for a bit.

 You can reduce your test case to:

 (1) assign to the property.   prop = val;

Is not the same at all! prop = val will call the setter function, *ptr = val will not!

Maybe you are thinking of something different, here is the code we're discussing: struct Foo { uint num; property ref uint front() { return num; } } -Steve

Ah, so we're speaking only of ref-returning properties. It took me this post and some of its replies until I realized this restriction. Sorry, I was speaking about properties in general. Actually this is really cool. I hadn't thought of ref-returns as a way to escape the lack-of-addressability problem. I'm too sleepy to think of the consequences of this, but so far I like it!
Jun 29 2010
prev sibling next sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 06/28/2010 10:40 PM, dsimcha wrote:
 Once enforcement of  property is enabled, we need to decide whether calling an
  property function using ()s should be legal.  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for just
 allowing omission, because I've run into the following ambiguity while
 debugging std.range.  Here's a reduced test case:
 
 struct Foo {
     uint num;
 
      property ref uint front() {
         return num;
     }
 }
 
 void main() {
     Foo foo;
     uint* bar = &foo.front;  // Tries to return a delegate.
 }
 
 If I can assume that  property functions can be called with explicit ()s to
 forcibly disambiguate this situation, then I can fix these kinds of bugs by
 simply doing a:
 
 uint* bar = &(foo.front());
 
 Can we finalize the idea that this will continue to be allowed now so that I
 can fix the relevant bugs in Phobos and know that my fix won't be broken in a
 few compiler releases?

I don't think it should be possible to take the address of a property. I also think applying property to normal fields would have special behavior. The effect would be a field that can't be addressed. Ideally this would be the default, to make it more intuitive to build flexible APIs, but I think we are too late for that bit of niceness. Just having that ability to build flexible APIs at all (in this case) by using property fields would be a good start. I wouldn't mind having some way to grab the delegate, but I think it should be very explicit. "This may break your code if the library writer changes their implementation" kinda explicit.
Jun 29 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 29 Jun 2010 08:05:04 -0400, Chad J  
<chadjoan __spam.is.bad__gmail.com> wrote:

 Whoops, made a mistake.

 On 06/29/2010 08:03 AM, Chad J wrote:
 On 06/29/2010 07:16 AM, Steven Schveighoffer wrote:
 If I'm reading this correctly, you are saying that you want &foo.front
 to return a pointer to uint, not a delegate?

 This is what I'd expect if  property forced no parentheses.  That is,
 foo.front can be replaced with (foo.front()) always, so &foo.front
 always translates to &(foo.front()).

 What this means is that you can't get a delegate to the property
 function.  This makes sense -- if you replaced the property with an
 actual field, you wouldn't be able to get a delegate anyways.  A
 property should operate just like a field.

 -Steve

That's the spirit of it. Properties are fields, except much more powerful but addressing is tricky. There is one thing that bugs me about this solution though. What if the user does this: (1) Grab the pointer. *ptr = prop;

 (2) assigns to it.  *ptr = val;
 (3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref, then you set through that ref. What you did is no different, you just delayed the assignment for a bit. You can reduce your test case to: (1) assign to the property. prop = val; (2) expects the result to be updated in prop. assert(val == prop); -Steve
Jun 29 2010
prev sibling next sibling parent Leandro Lucarella <luca llucax.com.ar> writes:
Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What if the
user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?
 then you
 set through that ref.  What you did is no different, you just
 delayed the assignment for a bit.
 
 You can reduce your test case to:
 
 (1) assign to the property.   prop = val;

Is not the same at all! prop = val will call the setter function, *ptr = val will not! -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- ¿Qué será lo que hace que una brújula siempre marque el norte? - Ser aguja, nada más, y cumplir su misión. -- Ricardo Vaporeso
Jun 29 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella <luca llucax.com.ar>  
wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What if  


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

It returns a ref. That can't be a calculated value. If it's a calculated value then T* ptr = &prop will fail to compile.
 then you
 set through that ref.  What you did is no different, you just
 delayed the assignment for a bit.

 You can reduce your test case to:

 (1) assign to the property.   prop = val;

Is not the same at all! prop = val will call the setter function, *ptr = val will not!

Maybe you are thinking of something different, here is the code we're discussing: struct Foo { uint num; property ref uint front() { return num; } } -Steve
Jun 29 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella  
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What  


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control.

Yes, my point was simply, for anything that returns ref: auto ptr = &foo(); *ptr = x; is equivalent to foo() = x;
 I agree with the view that a  property returning ref should be virtually  
 indistinguishable from a field. Currently that's not the case, e.g. if  
 you want to assign to such a property you must add parens:

 struct A { int x;  property ref y() { return x; } }

 unittest
 {
      A a;
      a.y = 5; // fails
      a.y() = 5; // works
 }

http://d.puremagic.com/issues/show_bug.cgi?id=3511 -Steve
Jun 29 2010
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 12:03:38 -0400, BLS <windevguy hotmail.de> wrote:

 On 29/06/2010 17:44, Andrei Alexandrescu wrote:
 struct A { int x;  property ref y() { return x; } }

should be : struct A { int x; property ref int y() { return x; } } or am I wrong ? bjoern

You're forgetting type inference.
Jun 29 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Monday, June 28, 2010 19:40:06 dsimcha wrote:
 Once enforcement of  property is enabled, we need to decide whether calling
 an  property function using ()s should be legal.  In other words, should
  property **require** omission of ()s or just allow it?  My vote is for
 just allowing omission, because I've run into the following ambiguity
 while debugging std.range.  Here's a reduced test case:
 
 struct Foo {
     uint num;
 
      property ref uint front() {
         return num;
     }
 }
 
 void main() {
     Foo foo;
     uint* bar = &foo.front;  // Tries to return a delegate.
 }
 
 If I can assume that  property functions can be called with explicit ()s to
 forcibly disambiguate this situation, then I can fix these kinds of bugs by
 simply doing a:
 
 uint* bar = &(foo.front());
 
 Can we finalize the idea that this will continue to be allowed now so that
 I can fix the relevant bugs in Phobos and know that my fix won't be broken
 in a few compiler releases?

FWIW, TDPL says that you can't use the parens when using a property function. - Jonathan M Davis
Jun 29 2010
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella  
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What  


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val == prop);


Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

Okay, but what about non-ref properties? i.e. struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?
Jun 29 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Tuesday 29 June 2010 20:41:48 Andrei Alexandrescu wrote:
 
 I don't think you should be able to even take the address of a non-ref
 property.
 
 Andrei

In order to have a properly reference a non-ref property, you would have to have an abstraction which wrapped the object along with its getter and/or setter and treated it exactly like a reference to a public member variable (including dealing with the fact that it could be read-only or write-only). On top of that, that same abstraction would have to be used for references to actual member variables. That way, they could be treated identically be user code, and that code would not have to care whether it was dealing with property functions or a naked member variable. And while I'm sure that such an abstraction is technically feasible, it would likely be an expensive one (particularly for a systems language), and by the time you're talking addresses of things, you're way too low-level to even consider it. Properties are definitely a useful abstraction, but they are by their nature a bit broken - particularly when you get to the low-level stuff - because while they may act like member variables, they _aren't_ member variables. A ref property is giving you essentially what a naked member variable would give you, so it makes some sense to allow you to take its address. However, if it's a non-ref property, I don't think that it really makes sense anymore. What would you take the address of? You'd need one (possibly two) functions and the object that has the property. With a ref, you have the variable itself. It's nowhere near that simple with a non-ref property functions - there may not even _be_ a variable to reference. So, I'd have to agree that it doesn't make sense to take the address of a non-ref property. I don't remember the details, but IIRC, C# has similar restrictions on its properties where you can't really treat them quite the same as public member variables, so it's not like it's without precedent. If we really wanted to fix it, we'd need a higher-level reference abstraction than we currently have, it there's a good chance that the abstraction would cost far too much to make sense - particularly for a systems lanuage. - Jonathan M Davis
Jun 29 2010
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Tue, 29 Jun 2010 23:41:48 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:
.
 Robert Jacques wrote:
 On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella  
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.  What  


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val ==  



Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?

I don't think you should be able to even take the address of a non-ref property. Andrei

I agree with you from a under-the-hood perspective, but I wasn't asking about that. I was asking about the leak in the property abstraction. Not being able to pass non-ref properties to functions by ref is a fairly serious (i.e. common) break/leak in the property abstraction: that properties should be "virtually indistinguishable from a field".
Jun 29 2010
prev sibling next sibling parent "Robert Jacques" <sandford jhu.edu> writes:
On Wed, 30 Jun 2010 01:00:53 -0400, Chad J  
<chadjoan __spam.is.bad__gmail.com> wrote:

 On 06/30/2010 12:33 AM, Robert Jacques wrote:
 On Tue, 29 Jun 2010 23:41:48 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 .
 Robert Jacques wrote:
 On Tue, 29 Jun 2010 11:44:07 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Tue, 29 Jun 2010 10:15:10 -0400, Leandro Lucarella
 <luca llucax.com.ar> wrote:

 Steven Schveighoffer, el 29 de junio a las 08:13 me escribiste:
There is one thing that bugs me about this solution though.


user does this:
(1) Grab the pointer.  *ptr = prop;

(2) assigns to it.  *ptr = val;
(3) expects the result to be updated in prop.  assert(val ==



Why would this assert fail? If a property returns a ref

What if it doesn't? If returns a temporary calculated value?

calculated value then T* ptr = &prop will fail to compile.

It's a "calculated reference", e.g. several instances could share the same value etc. Once the reference is out, clearly there's no more control. I agree with the view that a property returning ref should be virtually indistinguishable from a field. Currently that's not the case, e.g. if you want to assign to such a property you must add parens: struct A { int x; property ref y() { return x; } } unittest { A a; a.y = 5; // fails a.y() = 5; // works } Andrei

struct A { int x; property int y() { return x; } property int y(int v) { return x = v; } } unittest { A a; int* ptr = &a.x; // works int* ptr = &a.y; // fails } Is there a good way of patching this leak in the property abstraction?

I don't think you should be able to even take the address of a non-ref property. Andrei

I agree with you from a under-the-hood perspective, but I wasn't asking about that. I was asking about the leak in the property abstraction. Not being able to pass non-ref properties to functions by ref is a fairly serious (i.e. common) break/leak in the property abstraction: that properties should be "virtually indistinguishable from a field".

ref parameters are easily handled by property expression rewriting: void foo( ref bar ) { ... } foo(prop) becomes auto t = prop() foo(t) prop(t) I would handle "out" parameters similarly, but without calling the getter. They are essentially a different way of writing an assignment. Taking the /address/ is a bit different, since there may be no well-defined line of code at which the pointer holding the address dies. The ref parameter, on the other hand, WILL expire when the function returns.

Nope. That's a data race. Consider the case where foo has access to prop's object. Essentially, t is stale, so this breaks the concept of reference semantics.
Jun 29 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 30 Jun 2010 00:33:54 -0400, Robert Jacques <sandford jhu.edu>  
wrote:

 I agree with you from a under-the-hood perspective, but I wasn't asking  
 about that. I was asking about the leak in the  property abstraction.  
 Not being able to pass non-ref  properties to functions by ref is a  
 fairly serious (i.e. common) break/leak in the  property abstraction:  
 that  properties should be "virtually indistinguishable from a field".

properties are not fields, they are restricted fields. That is, you can completely control access to a property, but you cannot control access to a field. With a field, you cannot control: - Who has an address to it - What the user sets it to - The action of setting or getting it. Properties allow control over those things. The drawback is you cannot use it exactly like a field. I think we would all agree that the main mode of access to a field is: container.field = x; x = container.field; Properties mimic that functionality perfectly, and that is pretty much it. The one thing I'd like to see for properties is some sort of "property delegate." This could be generated in a struct. What I don't know is how to make it as "virtual" as a real delegate. Another thing I'd like to see (brought up elsewhere in this thread) is some sort of __traits/meta means to get a delegate to a property setter or getter, since with required omission of parens, you can no longer get a delegate to the property. This solution could also be amended to solve another pet peeve of mine: struct S { property void foo(int x); property void foo(string s); } S s; auto d1 = meta.delegateOf(s.foo, int); auto d2 = meta.delegateOf(s.foo, string); -Steve
Jul 01 2010