www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Properties: a.b.c = 3

reply Walter Bright <newshound1 digitalmars.com> writes:
The issue is what if b is a property, returns a temporary object, and 
that temp's .c field is uselessly set to 3?

It's a classic problem with properties that are implemented as functions.

I don't see how C#'s special property syntax adds any value for dealing 
with this.

One thought I had was to simply disallow the '.' to appear after a 
function style property.
Jul 28 2009
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Walter Bright escribi:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.
 
 I don't see how C#'s special property syntax adds any value for dealing 
 with this.
 
 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

Maybe only if the return value of the property is a struct? For class references it'll work well (except your property returns a new object each time, which is kind of a weird way to implement a property).
Jul 28 2009
parent reply "Lionello Lunesu" <lionello lunesu.remove.com> writes:
"Ary Borenszweig" <ary esperanto.org.ar> wrote in message 
news:h4ocm1$28k$1 digitalmars.com...
 Walter Bright escribi:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

 I don't see how C#'s special property syntax adds any value for dealing 
 with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

Maybe only if the return value of the property is a struct? For class references it'll work well (except your property returns a new object each time, which is kind of a weird way to implement a property).

What if structure B has get/set c and B.c(int) does something non-local? Then the function call ("property assignment") should be allowed. L.
Jul 29 2009
parent KennyTM~ <kennytm gmail.com> writes:
Lionello Lunesu wrote:
 
 "Ary Borenszweig" <ary esperanto.org.ar> wrote in message 
 news:h4ocm1$28k$1 digitalmars.com...
 Walter Bright escribió:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as 
 functions.

 I don't see how C#'s special property syntax adds any value for 
 dealing with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

Maybe only if the return value of the property is a struct? For class references it'll work well (except your property returns a new object each time, which is kind of a weird way to implement a property).

What if structure B has get/set c and B.c(int) does something non-local? Then the function call ("property assignment") should be allowed. L.

No, you should tell the author of struct B to revise the design :p
Jul 29 2009
prev sibling next sibling parent reply BLS <windevguy hotmail.de> writes:
Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.
 
 I don't see how C#'s special property syntax adds any value for dealing 
 with this.
 
 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

What I don't see is why a property isn't just a property. What you announce is more a kind of "universal maybe these value holder" - a temporary object is not a property period If something is exceeding the meaning of property then fire up your keyboard.
Jul 28 2009
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
BLS wrote:
 What I don't see is why a property isn't just a property. What you 
 announce is more a kind of "universal maybe these value holder"
 - a temporary object is not a property period

Even returning an 'int' is a temporary object.
Jul 28 2009
parent reply BLS <windevguy hotmail.de> writes:
Walter Bright wrote:
 BLS wrote:
 What I don't see is why a property isn't just a property. What you 
 announce is more a kind of "universal maybe these value holder"
 - a temporary object is not a property period

Even returning an 'int' is a temporary object.

?
Jul 28 2009
parent "Nick Sabalausky" <a a.a> writes:
"BLS" <windevguy hotmail.de> wrote in message 
news:h4oeb0$5sr$2 digitalmars.com...
 Walter Bright wrote:
 BLS wrote:
 What I don't see is why a property isn't just a property. What you 
 announce is more a kind of "universal maybe these value holder"
 - a temporary object is not a property period

Even returning an 'int' is a temporary object.

?

He means it's a temporary value, not actually an object.
Jul 28 2009
prev sibling parent reply BLS <windevguy hotmail.de> writes:
BLS wrote:
 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

 I don't see how C#'s special property syntax adds any value for 
 dealing with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

What I don't see is why a property isn't just a property. What you announce is more a kind of "universal maybe these value holder" - a temporary object is not a property period If something is exceeding the meaning of property then fire up your keyboard.

So int'max is a property int'ILikeIt() definitely not. let's keep it simple
Jul 28 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
BLS wrote:
 BLS wrote:
 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as 
 functions.

 I don't see how C#'s special property syntax adds any value for 
 dealing with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

What I don't see is why a property isn't just a property. What you announce is more a kind of "universal maybe these value holder" - a temporary object is not a property period If something is exceeding the meaning of property then fire up your keyboard.

So int'max is a property int'ILikeIt() definitely not. let's keep it simple

I don't understand, could you please elaborate? Andrei
Jul 28 2009
parent reply BLS <windevguy hotmail.de> writes:
Andrei Alexandrescu wrote:
 BLS wrote:
 BLS wrote:
 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, 
 and that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as 
 functions.

 I don't see how C#'s special property syntax adds any value for 
 dealing with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

What I don't see is why a property isn't just a property. What you announce is more a kind of "universal maybe these value holder" - a temporary object is not a property period If something is exceeding the meaning of property then fire up your keyboard.

So int'max is a property int'ILikeIt() definitely not. let's keep it simple

I don't understand, could you please elaborate? Andrei

Sure, int'max will give you (let's assume for a while that the compiler writer was not completely drunken) a useful answer int`ILikeIt() is probabely "green" I guess what I want to say is that a property will give you certainly useful answers depending on it's very own nature... a temporary object not. or : a horse is a horse is a horse of course
Jul 28 2009
parent Franklin Minty <minty monty.com> writes:
BLS Wrote:

 Andrei Alexandrescu wrote:
 BLS wrote:
 BLS wrote:
 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, 
 and that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as 
 functions.

 I don't see how C#'s special property syntax adds any value for 
 dealing with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

What I don't see is why a property isn't just a property. What you announce is more a kind of "universal maybe these value holder" - a temporary object is not a property period If something is exceeding the meaning of property then fire up your keyboard.

So int'max is a property int'ILikeIt() definitely not. let's keep it simple

I don't understand, could you please elaborate? Andrei

Sure, int'max will give you (let's assume for a while that the compiler writer was not completely drunken) a useful answer int`ILikeIt() is probabely "green" I guess what I want to say is that a property will give you certainly useful answers depending on it's very own nature... a temporary object not. or : a horse is a horse is a horse of course

Jul 29 2009
prev sibling next sibling parent reply davidl <davidl nospam.org> writes:
 Wed, 29 Jul 2009 10:33:53 +0800Walter Bright  
<newshound1 digitalmars.com> д:

 The issue is what if b is a property, returns a temporary object, and  
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

 I don't see how C#'s special property syntax adds any value for dealing  
 with this.

 One thought I had was to simply disallow the '.' to appear after a  
 function style property.

A property should never return temp obj in the first place. -- ʹ Opera Եĵʼͻ: http://www.opera.com/mail/
Jul 28 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
davidl wrote:
 A property should never return temp obj in the first place.

That's unworkable - it means you cannot have user defined types.
Jul 28 2009
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.
 
 I don't see how C#'s special property syntax adds any value for dealing
 with this.
 
 One thought I had was to simply disallow the '.' to appear after a
 function style property.

Maybe the compiler could rewrite the above as: auto t = a.b; t.c = 3; a.b = t; Unless it can prove it doesn't need to. Same solution as to the op= conundrum.
Jul 28 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Daniel Keep" <daniel.keep.lists gmail.com> wrote in message 
news:h4og1p$b3f$1 digitalmars.com...
 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

 I don't see how C#'s special property syntax adds any value for dealing
 with this.

 One thought I had was to simply disallow the '.' to appear after a
 function style property.

Maybe the compiler could rewrite the above as: auto t = a.b; t.c = 3; a.b = t; Unless it can prove it doesn't need to. Same solution as to the op= conundrum.

That's how I've always felt about it. I don't think any other approach really makes any sense.
Jul 28 2009
prev sibling next sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Daniel Keep wrote:
 
 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

 I don't see how C#'s special property syntax adds any value for dealing
 with this.

 One thought I had was to simply disallow the '.' to appear after a
 function style property.

Maybe the compiler could rewrite the above as: auto t = a.b; t.c = 3; a.b = t; Unless it can prove it doesn't need to. Same solution as to the op= conundrum.

YES. I'd always suspected C# properties did something like this, though it's been so long since I've used C# now that I'm wondering if it's bad memory. Anyhow, this is why properties must have their own semantics. Anything else will likely have some nasty corner case that you haven't thought of yet. Just bite the bullet and do this. Put the beast to rest. For another example array.length++; becomes auto t = array.length; t++; array.length = t; Even that case works just like the programmer expects. You'd never have to hear another n00b bitch about "array.length++; doesn't work!!11" ever again.
Jul 28 2009
parent reply "Nick Sabalausky" <a a.a> writes:
"Chad J" <chadjoan __spam.is.bad__gmail.com> wrote in message 
news:h4oku0$uel$1 digitalmars.com...
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:

 auto t = a.b;
 t.c = 3;
 a.b = t;

I'd always suspected C# properties did something like this, though it's been so long since I've used C# now that I'm wondering if it's bad memory.

Same here, so I just did a little test in Visual C# 2008 Express. Turns out that rewriting *is* exactly what C# does. Code: -------------------------------------- using System; namespace ConsoleApplication1 { class Program { class Rect { public int width; public int height; public Rect(int width, int height) { this.width = width; this.height = height; } } class Widget { public Rect size { get; set; } public Widget() { size = new Rect(50,50); } } static void Main(string[] args) { Widget wid = new Widget(); Console.Out.WriteLine("width: "+wid.size.width); Console.Out.WriteLine("height: "+wid.size.height); wid.size.width = 100; Console.Out.WriteLine("width: "+wid.size.width); Console.Out.WriteLine("height: "+wid.size.height); Console.ReadLine(); // So I can *read* the dang output } } } -------------------------------------- Compiles successfully. Output: -------------------------------------- width: 50 height: 50 width: 100 height: 50 --------------------------------------
Jul 30 2009
next sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Steven Schveighoffer wrote:
 On Thu, 30 Jul 2009 14:40:21 -0400, Nick Sabalausky <a a.a> wrote:
 
 "Chad J" <chadjoan __spam.is.bad__gmail.com> wrote in message
 news:h4oku0$uel$1 digitalmars.com...
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:

 auto t = a.b;
 t.c = 3;
 a.b = t;

I'd always suspected C# properties did something like this, though it's been so long since I've used C# now that I'm wondering if it's bad memory.

Same here, so I just did a little test in Visual C# 2008 Express. Turns out that rewriting *is* exactly what C# does. Code: -------------------------------------- using System; namespace ConsoleApplication1 { class Program { class Rect {

Change Rect to a struct, try again. Classes are reference types (note, I don't have a C# compiler handy, so I'm not sure what the result is).

It gives a compiler error, I already posted that somewhere else: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=94292
Jul 30 2009
prev sibling next sibling parent "Nick Sabalausky" <a a.a> writes:
"Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
news:op.uxv89qjieav7ka localhost.localdomain...
 On Thu, 30 Jul 2009 14:40:21 -0400, Nick Sabalausky <a a.a> wrote:

 "Chad J" <chadjoan __spam.is.bad__gmail.com> wrote in message
 news:h4oku0$uel$1 digitalmars.com...
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:

 auto t = a.b;
 t.c = 3;
 a.b = t;

I'd always suspected C# properties did something like this, though it's been so long since I've used C# now that I'm wondering if it's bad memory.

Same here, so I just did a little test in Visual C# 2008 Express. Turns out that rewriting *is* exactly what C# does. Code: -------------------------------------- using System; namespace ConsoleApplication1 { class Program { class Rect {

Change Rect to a struct, try again. Classes are reference types (note, I don't have a C# compiler handy, so I'm not sure what the result is).

Oops, yea, you're right. With Rect as a struct it fails to compile complaining (in it's own words) that size is not an lvalue.
Jul 30 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Nick Sabalausky wrote:
 "Chad J" <chadjoan __spam.is.bad__gmail.com> wrote in message 
 news:h4oku0$uel$1 digitalmars.com...
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:

 auto t = a.b;
 t.c = 3;
 a.b = t;

been so long since I've used C# now that I'm wondering if it's bad memory.

Same here, so I just did a little test in Visual C# 2008 Express. Turns out that rewriting *is* exactly what C# does. Code: -------------------------------------- using System; namespace ConsoleApplication1 { class Program { class Rect { public int width; public int height; public Rect(int width, int height) { this.width = width; this.height = height; } } class Widget { public Rect size { get; set; } public Widget() { size = new Rect(50,50); } } static void Main(string[] args) { Widget wid = new Widget(); Console.Out.WriteLine("width: "+wid.size.width); Console.Out.WriteLine("height: "+wid.size.height); wid.size.width = 100; Console.Out.WriteLine("width: "+wid.size.width); Console.Out.WriteLine("height: "+wid.size.height); Console.ReadLine(); // So I can *read* the dang output } } } -------------------------------------- Compiles successfully. Output: -------------------------------------- width: 50 height: 50 width: 100 height: 50 --------------------------------------

As far as I understand, this example is not relevant because there are no structs involved. Andrei
Jul 30 2009
prev sibling next sibling parent Zhenyu Zhou <rinick gmail.com> writes:
Daniel Keep Wrote:
 Maybe the compiler could rewrite the above as:
 
 auto t = a.b;
 t.c = 3;
 a.b = t;
 
 Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

I think b should be const if it is a temporary object. And the compiler will tell you that a.b.c is read only. Otherwise, don't make it look like a property: a.createNewB().c = 3;
Jul 29 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
 
 auto t = a.b;
 t.c = 3;
 a.b = t;
 
 Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???
Jul 29 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:
 
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want. Properties don't have to be exactly like fields. I think we need to get away from that idea.

Agreed. But it would be nice if they didn't allowed code with identical syntax and useless semantics.
 It would be nice if the compiler could help by simply rejecting what it 
 can reject (assignment to rvalues), but other than that, there's not 
 much that can be done.
 
 This can be detected in simple cases, but in the case where the end 
 point is a function, it will be difficult or impossible.

I think it is eminently possible, but we must figure a solution that doesn't complicate the language all too much.
 I don't believe the problem needs to be solved.

To me it looks like an essential problem. Andrei
Jul 29 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 11:08 AM, Andrei
 Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 I don't believe the problem needs to be solved.


It's a problem, but not a problem of properties. Just a problem of temporary return values in general.

Yes. It is particularly exacerbated by properties because of the syntactical deception. Andrei
Jul 29 2009
parent KennyTM~ <kennytm gmail.com> writes:
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 11:08 AM, Andrei
 Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 I don't believe the problem needs to be solved.


It's a problem, but not a problem of properties. Just a problem of temporary return values in general.

Yes. It is particularly exacerbated by properties

and opDot(). struct S { int s; } class X { S opDot() { S temp; temp.s = 6; return temp; } } X z = new X; assert(z.s == 6); z.s = 3; assert(z.s == 6); So, can we stop considering this general problem as a barrier for properties? Or just remove opDot() altogether.
 because of the 
 syntactical deception.
 
 Andrei

Jul 29 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:08:18 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 It would be nice if the compiler could help by simply rejecting what 
 it can reject (assignment to rvalues), but other than that, there's 
 not much that can be done.
  This can be detected in simple cases, but in the case where the end 
 point is a function, it will be difficult or impossible.

I think it is eminently possible, but we must figure a solution that doesn't complicate the language all too much.

Here is a struct, defined in a .di file: struct S { private int _c; int c(); void c(int n); } struct S2 { S b(); } Now, in your main file you have: void main() { S2 a; a.b.c = 3; } How in the world is the compiler supposed to know whether to allow this or not?

By making a conservative decision. Andrei
Jul 29 2009
next sibling parent Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:08:18 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 It would be nice if the compiler could help by simply rejecting what 
 it can reject (assignment to rvalues), but other than that, there's 
 not much that can be done.
  This can be detected in simple cases, but in the case where the end 
 point is a function, it will be difficult or impossible.

I think it is eminently possible, but we must figure a solution that doesn't complicate the language all too much.

Here is a struct, defined in a .di file: struct S { private int _c; int c(); void c(int n); } struct S2 { S b(); } Now, in your main file you have: void main() { S2 a; a.b.c = 3; } How in the world is the compiler supposed to know whether to allow this or not?

By making a conservative decision. Andrei

Yay! :-)
Jul 29 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 15:14:04 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:08:18 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 It would be nice if the compiler could help by simply rejecting 
 what it can reject (assignment to rvalues), but other than that, 
 there's not much that can be done.
  This can be detected in simple cases, but in the case where the 
 end point is a function, it will be difficult or impossible.

I think it is eminently possible, but we must figure a solution that doesn't complicate the language all too much.

struct S { private int _c; int c(); void c(int n); } struct S2 { S b(); } Now, in your main file you have: void main() { S2 a; a.b.c = 3; } How in the world is the compiler supposed to know whether to allow this or not?

By making a conservative decision.

If we have restrictive shit like this where the compiler thinks it knows better than me, can we at least only have it in SafeD?

It's a matter of degree. There's already plenty of correct code that D or other languages deem as invalid. So the issue is how conservative the conservative decision would be. Andrei
Jul 30 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:
 
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want.

I don't want to type out that mess as a user either... Design changes to avoid that mentioned mess would interfere with the goal of abstraction (e.g. assume you have widget.position, now how do you set only the x coordinate? yeah, split the property into position_x and position_y. Result is you have more noise, and you can't use a Point struct.) As for the rest, I'm sure all of you will have figured it out after some more ~500 postings.
Jul 29 2009
next sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
grauzone wrote:
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want.

I don't want to type out that mess as a user either... Design changes to avoid that mentioned mess would interfere with the goal of abstraction (e.g. assume you have widget.position, now how do you set only the x coordinate? yeah, split the property into position_x and position_y. Result is you have more noise, and you can't use a Point struct.) As for the rest, I'm sure all of you will have figured it out after some more ~500 postings.

But you don't /have/ to type out that mess! Why would anyone do that?! It's madness. The compiler does it FOR you. That's why it's there. It is supposed to save you from doing tedious work. Currently there are some cases where the current paradigm forces you to do that kind of work by hand: struct Rectangle { float x,y,w,h; } class Widget { Rectangle _rect; Rectangle rect() { return _rect; } Rectangle rect(Rectangle r) { return _rect = r; } // etc } void main() { auto widget = new Widget(); // DOES WORK: auto tmp = widget.rect; tmp.w = 200; tmp.h = 100; widget.rect = tmp; // DOES NOT WORK: // widget.rect.w = 200; // widget.rect.h = 100; }
Jul 29 2009
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:46:11 -0400, Chad J
 <chadjoan __spam.is.bad__gmail.com> wrote:
 
 grauzone wrote:
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want.

I don't want to type out that mess as a user either... Design changes to avoid that mentioned mess would interfere with the goal of abstraction (e.g. assume you have widget.position, now how do you set only the x coordinate? yeah, split the property into position_x and position_y. Result is you have more noise, and you can't use a Point struct.) As for the rest, I'm sure all of you will have figured it out after some more ~500 postings.

But you don't /have/ to type out that mess! Why would anyone do that?! It's madness. The compiler does it FOR you. That's why it's there. It is supposed to save you from doing tedious work. Currently there are some cases where the current paradigm forces you to do that kind of work by hand: struct Rectangle { float x,y,w,h; } class Widget { Rectangle _rect; Rectangle rect() { return _rect; } Rectangle rect(Rectangle r) { return _rect = r; } // etc } void main() { auto widget = new Widget(); // DOES WORK: auto tmp = widget.rect; tmp.w = 200; tmp.h = 100; widget.rect = tmp; // DOES NOT WORK: // widget.rect.w = 200; // widget.rect.h = 100; }

Wouldn't the compiler write: //widget.rect.w = 200 translates to auto tmp1 = widget.rect; tmp1.w = 200; widget.rect = tmp1; //widget.rect.h = 100 translates to auto tmp2 = widget.rect; tmp2.h = 100; widget.rect = tmp2; ??? Unless you want some serious optimization requirements... -Steve

It would. The optimization you speak of is reference caching. I often do it by hand in deeply nested loops where it actually means a damn. It's also an optimization I think compilers should do, because it is useful in many more cases than just this. Using some manner of property syntax would not preclude the programmer from writing the optimized version of the code by hand.
Jul 29 2009
next sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Chad J wrote:
 Steven Schveighoffer wrote:
 Wouldn't the compiler write:

 //widget.rect.w = 200 translates to
 auto tmp1 = widget.rect;
 tmp1.w = 200;
 widget.rect = tmp1;

 //widget.rect.h = 100 translates to
 auto tmp2 = widget.rect;
 tmp2.h = 100;
 widget.rect = tmp2;

 ???

 Unless you want some serious optimization requirements...

 -Steve

It would. The optimization you speak of is reference caching. I often do it by hand in deeply nested loops where it actually means a damn. It's also an optimization I think compilers should do, because it is useful in many more cases than just this.

I should probably explain this more. Suppose you have some data structure in memory. struct Foo { Bar* a; } struct Bar { float[] thingies; } Then you have a Foo and you need to stroke it's Bar's thingies. Foo f; // ... for ( int i = 0; i < f.a.thingies.length; i++ ) stroke(f.a.thingies[i]); Bad bad bad! For every thingie stroked there is going to be two whole dereferences. That's if the compiler was smart enough to move "f.a.thingies.length" outside of the loop. Otherwise it's more like 3 derefences. Unlike mere assignments, those can be expensive, especially if you have a cache miss. 3 of those. Ouch. So rewrite the code like this: Foo f; // ... var things = f.a.thingies; // The reference is now cached. var len = things.length; for ( int i = 0; i < len; i++ ) stroke(things[i]); Ah, much better. Only does 1 dereference per thing ever.
Jul 29 2009
prev sibling parent Benji Smith <dlanguage benjismith.net> writes:
Chad J wrote:
 Steven Schveighoffer wrote:
 struct Rectangle
 {
     float x,y,w,h;
 }

 class Widget
 {
     Rectangle _rect;
     Rectangle rect() { return _rect; }
     Rectangle rect(Rectangle r) { return _rect = r; }
     // etc
 }

 void main()
 {
     auto widget = new Widget();

     // DOES WORK:
     auto tmp = widget.rect;
     tmp.w = 200;
     tmp.h = 100;
     widget.rect = tmp;

     // DOES NOT WORK:
     // widget.rect.w = 200;
     // widget.rect.h = 100;
 }

//widget.rect.w = 200 translates to auto tmp1 = widget.rect; tmp1.w = 200; widget.rect = tmp1; //widget.rect.h = 100 translates to auto tmp2 = widget.rect; tmp2.h = 100; widget.rect = tmp2; ??? Unless you want some serious optimization requirements... -Steve

It would. The optimization you speak of is reference caching. I often do it by hand in deeply nested loops where it actually means a damn. It's also an optimization I think compilers should do, because it is useful in many more cases than just this. Using some manner of property syntax would not preclude the programmer from writing the optimized version of the code by hand.

And, in fact, this exact kind of optimization is made very simple if the compiler uses a static single assignment (SSA) form for its internal code representation. The LLVM suite already does it. --benji
Jul 30 2009
prev sibling next sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:22:26 -0400, grauzone <none example.net> wrote:
 
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

gigantic mess, which the user might not want.

I don't want to type out that mess as a user either...

What I meant was, I wouldn't want something like a.b.c.d.e.f = 3 to generate the equivalent of 25 lines of code.
 Design changes to avoid that mentioned mess would interfere with the
 goal of abstraction (e.g. assume you have widget.position, now how do
 you set only the x coordinate? yeah, split the property into
 position_x and position_y. Result is you have more noise, and you
 can't use a Point struct.)

option 1, return a ref Point struct option 2, return a special struct which uses properties to set the values in the original widget. I don't think it's an impossible problem to solve, I just don't think the compiler should be involved, because it makes it too easy to gerenate horrible code.

So we could have semantics that actually work, but you don't want them because, oh man, my code might have to do a few more assignments. A few assignments. Really?! !!!! Assignments aren't even that expensive! They are one of the easiest operations your CPU can perform! It's like you'll have a few more MOV operations laying around in the worst case. If there are dereferences involved it has to break up the expression ANYWAYS. ARGH! You'll have to forgive me. What I'm reading here is quite frustrating.
 Now, having the compiler reject invalid assignments is definitely
 something I can live with.
 
 -Steve

Jul 29 2009
next sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Chad J wrote:
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:22:26 -0400, grauzone <none example.net> wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

gigantic mess, which the user might not want.


generate the equivalent of 25 lines of code.
 Design changes to avoid that mentioned mess would interfere with the
 goal of abstraction (e.g. assume you have widget.position, now how do
 you set only the x coordinate? yeah, split the property into
 position_x and position_y. Result is you have more noise, and you
 can't use a Point struct.)

option 2, return a special struct which uses properties to set the values in the original widget. I don't think it's an impossible problem to solve, I just don't think the compiler should be involved, because it makes it too easy to gerenate horrible code.

So we could have semantics that actually work, but you don't want them because, oh man, my code might have to do a few more assignments. A few assignments. Really?! !!!! Assignments aren't even that expensive! They are one of the easiest operations your CPU can perform! It's like you'll have a few more MOV operations laying around in the worst case. If there are dereferences involved it has to break up the expression ANYWAYS. ARGH! You'll have to forgive me. What I'm reading here is quite frustrating.

Thinking about it a little more, the extra temporaries could run you out of registers. That still sounds like a negligable cost in most code. As I've mentioned elsewhere, it's also really easy to optimize this by hand if it ever becomes a performance problem.
 Now, having the compiler reject invalid assignments is definitely
 something I can live with.

 -Steve


Jul 29 2009
parent reply grauzone <none example.net> writes:
Chad J wrote:
 Thinking about it a little more, the extra temporaries could run you out
 of registers.  That still sounds like a negligable cost in most code.

Temporaries can be on the stack. That's not a problem.
Jul 29 2009
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 1:14 PM, grauzone<none example.net> wrote:
 Chad J wrote:
 Thinking about it a little more, the extra temporaries could run you out
 of registers.  That still sounds like a negligable cost in most code.


How is that not a performance issue? The stack is in main memory. --bb

This is where my knowledge starts to run a bit thin. So correct me if I'm wrong, but isn't something like the stack (or at least the top/bottom/end in use) extremely likely to be in the nearest cache (L1)? If that's the case, then this kind of dereference is going to be of the cheaper variety.
Jul 29 2009
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Chad J wrote:
 Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 1:14 PM, grauzone<none example.net> wrote:
 Chad J wrote:
 Thinking about it a little more, the extra temporaries could run you out
 of registers.  That still sounds like a negligable cost in most code.


--bb

This is where my knowledge starts to run a bit thin. So correct me if I'm wrong, but isn't something like the stack (or at least the top/bottom/end in use) extremely likely to be in the nearest cache (L1)? If that's the case, then this kind of dereference is going to be of the cheaper variety.

Also, really deep dot chains are unlikely to happen. I just feel like this won't create many more memory accesses than there were already. Especially for people with 64 bit OSes on x86_64 that are not register starved like the 32 bit x86. On x86 you are hitting the stack all the time anyways, and the extra access or two will go unnoticed.
Jul 29 2009
parent Benji Smith <dlanguage benjismith.net> writes:
Chad J wrote:
 Chad J wrote:
 Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 1:14 PM, grauzone<none example.net> wrote:
 Chad J wrote:
 Thinking about it a little more, the extra temporaries could run you out
 of registers.  That still sounds like a negligable cost in most code.


--bb

So correct me if I'm wrong, but isn't something like the stack (or at least the top/bottom/end in use) extremely likely to be in the nearest cache (L1)? If that's the case, then this kind of dereference is going to be of the cheaper variety.

Also, really deep dot chains are unlikely to happen. I just feel like this won't create many more memory accesses than there were already. Especially for people with 64 bit OSes on x86_64 that are not register starved like the 32 bit x86. On x86 you are hitting the stack all the time anyways, and the extra access or two will go unnoticed.

Especially especially because, if you prevent the a.b.c = x syntax, the only thing that'll happen is you'll cause people to write all that code themselves. The same number of assignments will happen anyhow, but the user will have to write them all manually. I'll all for having the compiler automate the boilerplate stuff. Also, note that the double-assignment case only happens when assigning to value types. Assigning to reference type properties will be unaffected. --benji
Jul 30 2009
prev sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 15:01:47 -0400, Chad J
 <chadjoan __spam.is.bad__gmail.com> wrote:
 So we could have semantics that actually work, but you don't want them
 because, oh man, my code might have to do a few more assignments.  A few
 assignments.  Really?!

Hold on a second, don't we already have semantics that work? I mean we can already write auto tmp = a.b; tmp.c = 5; a.b = tmp; So what is the big deal? If your widget.position is a struct with fields, then the compiler should be able to detect the error (not yet?) and tell you "no, it's an rvalue." Why should the compiler make assumptions about the logic of code when they may be incorrect, and might be better optimized by a person? I would think that macros would be a better fit for this.
 Assignments aren't even that expensive!  They are one of the easiest
 operations your CPU can perform!  It's like you'll have a few more MOV
 operations laying around in the worst case.

It's not the assignments, its the idea that the compiler should workaround the poor design of the code by writing possibly incorrect code. I'd rather be in charge of the design of my code, thanks. If the compiler help prevent me from making obvious *provable* mistakes, then fine. If it makes decisions that might be based on incomplete information, I don't want that. -Steve

Ah, well that argument has some weight. I'd like to know though, what assumptions does this make about the code? Why might they be incorrect? Those are not rhetorical questions. Just to be clear, I acknowledge that me saying "it is unlikely to produce incorrect code" is a poor argument for a feature. My intent here is to find out what exactly is wrong with the proposed transformation. If the compiler /knows/ that something being used is a property, I just don't understand how that information is incomplete. Also macros aren't a good fit for this because they aren't going to exist in D2, or necessarily ever. Well, properties may not pop up in D2 either, but if they're going to happen it'd be nicer if they did.
Jul 29 2009
next sibling parent reply Zhenyu Zhou <rinick gmail.com> writes:
Chad J Wrote:
 Currently there are some cases where the current paradigm forces you to
 do that kind of work by hand:
 
 struct Rectangle
 {
 	float x,y,w,h;
 }
 class Widget
 {
 	Rectangle _rect;
 	Rectangle rect() { return _rect; }
 	Rectangle rect(Rectangle r) { return _rect = r; }
 }
 void main()
 {
 	auto widget = new Widget();
 
 	// DOES WORK:
 	auto tmp = widget.rect;
 	tmp.w = 200;
 	tmp.h = 100;
 	widget.rect = tmp;
 
 	// DOES NOT WORK:
 	// widget.rect.w = 200;
 	// widget.rect.h = 100;
 }

What about: class Widget { Rectangle _rect; immutable(Rectangle) rect() const { return _rect; } Rectangle rect(Rectangle r) { return _rect = r; } }
Jul 29 2009
parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Zhenyu Zhou wrote:
 
 What about:
 
 class Widget
 {
  	Rectangle _rect;
  	immutable(Rectangle) rect() const { return _rect; }
  	Rectangle rect(Rectangle r) { return _rect = r; }
 }

You'll fall into the trap unless you know about immutable. Said another way: the default is unsafe. Also this still isn't very useful, as it still requires the calling programmer to manually break apart the accesses and will not work for things like "array.length++;" which were errors already. So even if newbies were omniscient and always made their getters immutable, all you'd have accomplished is turning a debugging session into an annoyance. Noble, but all too bitter when a complete solution is within grasp. This is also conflating the problem with rvalues-in-lhs with the issue of D's lack of properties. Forbidding assignment and mutation of rvalues eliminates the debugging session. The annoyance of "why won't this work?!", well, that is for properties to solve. See: http://d.puremagic.com/issues/show_bug.cgi?id=3008
Jul 29 2009
parent reply Zhenyu Zhou <rinick gmail.com> writes:
Chad J Wrote:
 Zhenyu Zhou wrote:
 
 What about:
 
 class Widget
 {
  	Rectangle _rect;
  	immutable(Rectangle) rect() const { return _rect; }
  	Rectangle rect(Rectangle r) { return _rect = r; }
 }

You'll fall into the trap unless you know about immutable. Said another way: the default is unsafe. Also this still isn't very useful, as it still requires the calling programmer to manually break apart the accesses and will not work for things like "array.length++;" which were errors already. So even if newbies were omniscient and always made their getters immutable, all you'd have accomplished is turning a debugging session into an annoyance. Noble, but all too bitter when a complete solution is within grasp.

Maybe this is what you want ref rect() { return _rect; } But I would still return const type because it's unsafe to change the rect without calling its setter e.g. Rectangle rect(Rectangle r) { _rect = r; redraw(); return _rect; } If you allow widget.rect.w = 200; widget.rect.h = 100; you will have to write much more code to handle the painting correctly. and we don't want to call redraw twice here
Jul 29 2009
next sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Zhenyu Zhou wrote:
 Chad J Wrote:
 Zhenyu Zhou wrote:
 What about:

 class Widget
 {
  	Rectangle _rect;
  	immutable(Rectangle) rect() const { return _rect; }
  	Rectangle rect(Rectangle r) { return _rect = r; }
 }

way: the default is unsafe. Also this still isn't very useful, as it still requires the calling programmer to manually break apart the accesses and will not work for things like "array.length++;" which were errors already. So even if newbies were omniscient and always made their getters immutable, all you'd have accomplished is turning a debugging session into an annoyance. Noble, but all too bitter when a complete solution is within grasp.

Maybe this is what you want ref rect() { return _rect; } But I would still return const type because it's unsafe to change the rect without calling its setter

The point is to make sure that when a property's member appears on the left-hand-side of an assign expression, the setter will be called. All this is doing is outright forbidding the property's member to appear on the left-hand-side of an assign expression at all. It's a step in the right direction, but we can do better. (Some of what I said there is probably wrong at a technical level, but I'm trying to be clear and not pedantic.)
 e.g.
 Rectangle rect(Rectangle r) {
   _rect = r;
   redraw(); 
   return _rect; 
 }
 
 If you allow
 widget.rect.w = 200;
 widget.rect.h = 100;
 you will have to write much more code to handle the painting correctly. 
 and we don't want to call redraw twice here
 
 

meh, it's not going to cost anything to resize until the next frame is rendered anyways ;) Widgets and Rectangles are just an arbitrary example used to show where people can get trapped by the language's misfeature. Solving this only for Widgets and Rectangles will, well, only do that much, and thus be a waste of time since we've already acknowledged the problem and know how to hack around it in existing code.
Jul 30 2009
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Zhenyu Zhou" <rinick gmail.com> wrote in message 
news:h4rfif$2os2$1 digitalmars.com...
 e.g.
 Rectangle rect(Rectangle r) {
  _rect = r;
  redraw();
  return _rect;
 }

 If you allow
 widget.rect.w = 200;
 widget.rect.h = 100;
 you will have to write much more code to handle the painting correctly.
 and we don't want to call redraw twice here

I've dealt with that sort of thing in C# and it's a trivial issue. When you write code such as the above, it's very clear that you're changing the rect twice. If that's a problem, you just do this: widget.rect = Rect(200, 100); Easy.
Jul 30 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Steven Schveighoffer" <schveiguy yahoo.com> wrote in message 
news:op.uxv7r6ndeav7ka localhost.localdomain...
 On Thu, 30 Jul 2009 14:09:06 -0400, Nick Sabalausky <a a.a> wrote:

 "Zhenyu Zhou" <rinick gmail.com> wrote in message
 news:h4rfif$2os2$1 digitalmars.com...
 e.g.
 Rectangle rect(Rectangle r) {
  _rect = r;
  redraw();
  return _rect;
 }

 If you allow
 widget.rect.w = 200;
 widget.rect.h = 100;
 you will have to write much more code to handle the painting correctly.
 and we don't want to call redraw twice here

I've dealt with that sort of thing in C# and it's a trivial issue. When you write code such as the above, it's very clear that you're changing the rect twice. If that's a problem, you just do this: widget.rect = Rect(200, 100);

Rect has 4 values (x, y, width, height), otherwise, the example doesn't make much sense.

I've dealt with that as well, and done both of these (though not at the same time obviously ;) ): - widget.rect = Rect(widget.rect.x, widget.rect.y, 200, 100); - Cache the reference manually. So still easy.
Jul 30 2009
prev sibling parent Benji Smith <dlanguage benjismith.net> writes:
Nick Sabalausky wrote:
 "Zhenyu Zhou" <rinick gmail.com> wrote in message 
 news:h4rfif$2os2$1 digitalmars.com...
 e.g.
 Rectangle rect(Rectangle r) {
  _rect = r;
  redraw();
  return _rect;
 }

 If you allow
 widget.rect.w = 200;
 widget.rect.h = 100;
 you will have to write much more code to handle the painting correctly.
 and we don't want to call redraw twice here

I've dealt with that sort of thing in C# and it's a trivial issue. When you write code such as the above, it's very clear that you're changing the rect twice. If that's a problem, you just do this: widget.rect = Rect(200, 100); Easy.

It's kind of a moot point anyhow, because most respectable graphics frameworks will defer any rendering until all properties have been set. Something like this: class Rect { private int _w; private int _h; private boolean _dirty; property set w(int value) { _w = value; _dirty = true; } property set h(int value) { _h = value; _dirty = true; } void draw() { if (_dirty) { // rendering code _dirty = false; } } } Rendering code is *never* invoked from within a property-setter, and property values are never changed during rendering code. (Also, there's usually a separate "measurement" phase, following the manual property-setting phase, within which properties can be changed to suit the positional constraints but where no rendering occurs.) Anyhow, I think those kinds of considerations are mostly orthogonal to a discussion of properties, in the general sense, except insofar as the existence of a property syntax makes it more convenient to implement things like dirty-flag marking, property-change listeners, and the like. --benji
Jul 30 2009
prev sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 18:27:09 -0400, Chad J
 <chadjoan __spam.is.bad__gmail.com> wrote:
 
 I'd like to know though, what assumptions does this make about the code?
  Why might they be incorrect?

Simply that the compiler assumes it's simply setting a value. However, setters are functions and can execute any code they wish. You may get into weird states that you don't want to or perform unneeded actions that you aren't expecting. The example given later in this thread is perfect -- setting the width and height individually may re-render the widget when you only should re-render once.

That's a problem of library design and code architecture, nothing inherent in properties. You can make such mistakes using functions as well. The mistake here is not so much the multiple calls into the widget, but the fact that multiple calls would redraw the widget multiple times - bad! That thing should wait until the entire screen is redrawn. There are data types where changes to state are expensive. They tend to be designed with the ability to queue up changes and execute them all at once. Whether the changes are done with functions or properties would not matter.
 Another example is something like this:
 
 class C
 {
   int x;
 }
 struct S
 {
   C c;
 }
 
 struct S2
 {
   C c;
   S s() { return S(c); }
   void s(S s) {c = s.c;}
 }
 
 void main()
 {
   S2 s2;
   s2.c = new C;
   s2.s.c.x = 5;
 }
 
 If the compiler rewrites that last line as:
 
 auto tmp = s2.s;
 s2.c.x = 5;
 s2.s = tmp;
 

I think there's a typo. auto tmp = s2.s; tmp.c.x = 5; s2.s = tmp; I'm going to assume that's what you meant.
 Then it is completely unnecessary, and could cause problems if the
 setter does something else besides setting.
 

I'm not quite sure I follow. I'm reading the first bit as, "if I write code just like so, then 's2.s.c.x = 5;' will generate extra code because 's2.s.c.x = 5;' works fine without the rewrite." A true statement. I don't see how it relates to setters doing other things. Setters don't have to be written like that, and they can do other perfectly useful things that won't cause any problems. The setter can execute arbitrary code. Of course it can cause problems! Programmers aren't going to make it cause problems though.
 If the compiler /knows/ that something being used is a property, I just
 don't understand how that information is incomplete.

First, because the problem of determining what code does is an NP-complete problem -- the compiler can't know what code is going to do except by running it. It's just like trying to write a program that detects infinite loops. So it must make some assumptions. Second, because D allows opaque function declarations, so the compiler might not even be able to *look* at what a property does. It can only assume, and the assumption you would have it make is that a property is a simple setter that has no other side effects.

I never suggested analyzing the code. The implication is that the definition contains information telling the compiler that it is a property getter/setter. Assuming the properties don't have side effects is also a bad idea. Side effects are one of the big uses of properties. They allow you to set dirty-flags, increment/decrement counters, lazily allocate memory, call events, and do any number of nifty things. Functions let you do those nifty things too, but if you have an existing API that has fields, you can't use functions without breaking API changes. However, you can migrate the fields to properties without breaking user code and then reap the benefits of functions without using functions. Not to mention the bonus in being able to make the API's intent more clear (that whole verb vs noun thing). Like many features, properties can be used to do bad things. The important thing is that the programmer must be very intentional to accomplish said bad things. Using properties correctly is very easy and natural. (Note that I don't consider D's current properties to actually be properties. Omissible parentheses is a better name.) In the Widget/Rectangle example, properties can be used to set dirty flags whenever the rectangle is modified. Now the GUI library knows exactly which Widgets have changed and which haven't, and all without having to diff against previous state. This information can be used to only modify the pixels over places where widgets changed. This example is somewhat fictitious though, since that optimization doesn't bring you many gains with today's GPUs. Still, it does hint at the usefulness of monitoring changes in state. Currently the only way to do this in a complete and *future-proof* manner is to use interfaces that expose only functions. Some people seem perfectly happy with that, but I dislike how it tends to cause code bloat (SLoC mostly) and terrible violations of DRY. The lack of nouns is a little annoying too.
 Also macros aren't a good fit for this because they aren't going to
 exist in D2, or necessarily ever.  Well, properties may not pop up in D2
 either, but if they're going to happen it'd be nicer if they did.

I think you can solve this problem (and solve it correctly) with some sort of fancy template magic, but not without some extra baggage (such as an extra type that might not be optimized). I envision something like this: with(Setter!(widget.location)) // don't know if this works { width = 50; height = 100; // at scope end, Setter sets widget.location to it's internal representation via a finalizer }

That seems to solve a different problem. That might actually be an interesting trick. I'll have to try and use it some time. Properties are often used for doing bookkeeping on the callee's side, so requiring the caller to do a bunch of bookkeeping defeats the purpose.
 The reason I said macros is because they have the power to rewrite the
 code exactly like you said the compiler should.  At least with a macro
 you would have some indication that it is exactly what the user wants,
 and you can have the power to do your optimizations.
 
 -Steve

My issue with macros isn't a lack of capability, but rather a lack of existence ;)
Jul 31 2009
prev sibling parent reply grauzone <none example.net> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:22:26 -0400, grauzone <none example.net> wrote:
 
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

gigantic mess, which the user might not want.

I don't want to type out that mess as a user either...

What I meant was, I wouldn't want something like a.b.c.d.e.f = 3 to generate the equivalent of 25 lines of code.

The thing above was just the consequence of Daniel Keep's idea of writing back those modified temporary values. In the common case, you wouldn't use such deep nesting anyway.
 Design changes to avoid that mentioned mess would interfere with the 
 goal of abstraction (e.g. assume you have widget.position, now how do 
 you set only the x coordinate? yeah, split the property into 
 position_x and position_y. Result is you have more noise, and you 
 can't use a Point struct.)

option 1, return a ref Point struct

Then I can it make just a field. The reason that one makes this a property are: 1. Want to catch writes with a getter. 2. Wants to make it read-only (you had to return a const reference, which probably would make the implementation of Point a bit annoying: you had to mark methods as const etc.)
 option 2, return a special struct which uses properties to set the 
 values in the original widget.

Now this would generate a mess (lots of bloat). Now I'm not sure; maybe macros could provide a way out (auto generate the code bloat), or possibly you could use alias this to properly "wrap" a value? Any ideas on this?
 
 I don't think it's an impossible problem to solve, I just don't think 
 the compiler should be involved, because it makes it too easy to 
 gerenate horrible code.
 
 Now, having the compiler reject invalid assignments is definitely 
 something I can live with.

This would be better than the status quo, yes.
 -Steve

Jul 29 2009
parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
grauzone wrote:
 Steven Schveighoffer wrote:
 option 2, return a special struct which uses properties to set the
 values in the original widget.

Now this would generate a mess (lots of bloat). Now I'm not sure; maybe macros could provide a way out (auto generate the code bloat), or possibly you could use alias this to properly "wrap" a value? Any ideas on this?

Just say no. It is far easier to optimize the nesting assignments. It is also easy to optimize the nesting assignments by hand if it's really necessary. Not to mention if you don't optimize the nesting assignments at all, they are still orders of magnitude faster than this sort of thing, and only slightly slower than optimal code. What worries me more about this is things like type casting and the unary ++ the special struct may be expected to overload. This is the case in something like "array.length++;". length would return a proxy struct that would have to: 1) be implicity castable to size_t 2) overload opPostInc, opPostDec, opXxxAssign, and opAssign. That's a lot of DRY violation and tedium. You have to do it for every property that sets a calculated value.
Jul 29 2009
prev sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:
 
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want.

!!! Wait, just what is wrong here? I mean, so what. That expression would generate a lot of code if the compiler were completely naive about this. It might run slightly slower. But, it's probably pretty easy for the compiler to prove that this doesn't need to happen in common cases (e.g. all members are fields). Also, having 5 dotOps in a row like that is very uncommon. If you are worried about speed than cache your dereferences. It's a common trick. I still don't see how we can't do this in a very mechanical manner.
 Properties don't have to be exactly like fields.  I think we need to get
 away from that idea.
 
 It would be nice if the compiler could help by simply rejecting what it
 can reject (assignment to rvalues), but other than that, there's not
 much that can be done.
 
 This can be detected in simple cases, but in the case where the end
 point is a function, it will be difficult or impossible.
 
 I don't believe the problem needs to be solved.
 
 -Steve

There are a lot of problems that don't /need/ to be solved but /should/ be solved.
Jul 29 2009
prev sibling next sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
Wed, 29 Jul 2009 13:39:50 +1000, Daniel Keep wrote:

 Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.
 
 I don't see how C#'s special property syntax adds any value for dealing
 with this.
 
 One thought I had was to simply disallow the '.' to appear after a
 function style property.

Maybe the compiler could rewrite the above as: auto t = a.b; t.c = 3; a.b = t; Unless it can prove it doesn't need to. Same solution as to the op= conundrum.

vote++ Though I'm not sure what to do with this: void foo(ref int x) { x++; } foo(a.b.c); Probably it's not that hard anyway. Perhaps it's enough to rewrite property access every time a value-returning property is used as lvalue: auto t = a.b; foo(t.c); a.b = t;
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want. Properties don't have to be exactly like fields. I think we need to get away from that idea. It would be nice if the compiler could help by simply rejecting what it can reject (assignment to rvalues), but other than that, there's not much that can be done. This can be detected in simple cases, but in the case where the end point is a function, it will be difficult or impossible. I don't believe the problem needs to be solved. -Steve
Jul 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 11:08 AM, Andrei
Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 I don't believe the problem needs to be solved.

To me it looks like an essential problem.

It's a problem, but not a problem of properties. Just a problem of temporary return values in general. --bb
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 14:08:18 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 It would be nice if the compiler could help by simply rejecting what it  
 can reject (assignment to rvalues), but other than that, there's not  
 much that can be done.
  This can be detected in simple cases, but in the case where the end  
 point is a function, it will be difficult or impossible.

I think it is eminently possible, but we must figure a solution that doesn't complicate the language all too much.

Here is a struct, defined in a .di file: struct S { private int _c; int c(); void c(int n); } struct S2 { S b(); } Now, in your main file you have: void main() { S2 a; a.b.c = 3; } How in the world is the compiler supposed to know whether to allow this or not? What if the actual code for S looks like this: struct S { private int _c; int c() {return *(cast(int*)_c);} void c(int n) {*(cast(int*)_c) = n;} } Shouldn't this be allowed? -Steve
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 14:22:26 -0400, grauzone <none example.net> wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

gigantic mess, which the user might not want.

I don't want to type out that mess as a user either...

What I meant was, I wouldn't want something like a.b.c.d.e.f = 3 to generate the equivalent of 25 lines of code.
 Design changes to avoid that mentioned mess would interfere with the  
 goal of abstraction (e.g. assume you have widget.position, now how do  
 you set only the x coordinate? yeah, split the property into position_x  
 and position_y. Result is you have more noise, and you can't use a Point  
 struct.)

option 1, return a ref Point struct option 2, return a special struct which uses properties to set the values in the original widget. I don't think it's an impossible problem to solve, I just don't think the compiler should be involved, because it makes it too easy to gerenate horrible code. Now, having the compiler reject invalid assignments is definitely something I can live with. -Steve
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 14:46:11 -0400, Chad J  
<chadjoan __spam.is.bad__gmail.com> wrote:

 grauzone wrote:
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

Yeah, I think this idea is no good. a.b.c.d.e.f = 3, results in one gigantic mess, which the user might not want.

I don't want to type out that mess as a user either... Design changes to avoid that mentioned mess would interfere with the goal of abstraction (e.g. assume you have widget.position, now how do you set only the x coordinate? yeah, split the property into position_x and position_y. Result is you have more noise, and you can't use a Point struct.) As for the rest, I'm sure all of you will have figured it out after some more ~500 postings.

But you don't /have/ to type out that mess! Why would anyone do that?! It's madness. The compiler does it FOR you. That's why it's there. It is supposed to save you from doing tedious work. Currently there are some cases where the current paradigm forces you to do that kind of work by hand: struct Rectangle { float x,y,w,h; } class Widget { Rectangle _rect; Rectangle rect() { return _rect; } Rectangle rect(Rectangle r) { return _rect = r; } // etc } void main() { auto widget = new Widget(); // DOES WORK: auto tmp = widget.rect; tmp.w = 200; tmp.h = 100; widget.rect = tmp; // DOES NOT WORK: // widget.rect.w = 200; // widget.rect.h = 100; }

Wouldn't the compiler write: //widget.rect.w = 200 translates to auto tmp1 = widget.rect; tmp1.w = 200; widget.rect = tmp1; //widget.rect.h = 100 translates to auto tmp2 = widget.rect; tmp2.h = 100; widget.rect = tmp2; ??? Unless you want some serious optimization requirements... -Steve
Jul 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 1:14 PM, grauzone<none example.net> wrote:
 Chad J wrote:
 Thinking about it a little more, the extra temporaries could run you out
 of registers. =A0That still sounds like a negligable cost in most code.

Temporaries can be on the stack. That's not a problem.

How is that not a performance issue? The stack is in main memory. --bb
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 15:01:47 -0400, Chad J  
<chadjoan __spam.is.bad__gmail.com> wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:22:26 -0400, grauzone <none example.net> wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 10:16:33 -0400, grauzone <none example.net> wrote:

 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:
  auto t = a.b;
 t.c = 3;
 a.b = t;
  Unless it can prove it doesn't need to.  Same solution as to the  
 op=
 conundrum.

Yes! At least that's what the user wants. The compiler has to detect, that the object was modified at all. (To know whether it should generate code to write back the property.) Would this make the compiler much complexer? You also have to deal with nested properties: a.b.c.d = 3; turns to auto t = a.b; auto t2 = t.c; c.d = 3; t.c = t2; a.b = t; ???

gigantic mess, which the user might not want.

I don't want to type out that mess as a user either...

What I meant was, I wouldn't want something like a.b.c.d.e.f = 3 to generate the equivalent of 25 lines of code.
 Design changes to avoid that mentioned mess would interfere with the
 goal of abstraction (e.g. assume you have widget.position, now how do
 you set only the x coordinate? yeah, split the property into
 position_x and position_y. Result is you have more noise, and you
 can't use a Point struct.)

option 1, return a ref Point struct option 2, return a special struct which uses properties to set the values in the original widget. I don't think it's an impossible problem to solve, I just don't think the compiler should be involved, because it makes it too easy to gerenate horrible code.

So we could have semantics that actually work, but you don't want them because, oh man, my code might have to do a few more assignments. A few assignments. Really?!

Hold on a second, don't we already have semantics that work? I mean we can already write auto tmp = a.b; tmp.c = 5; a.b = tmp; So what is the big deal? If your widget.position is a struct with fields, then the compiler should be able to detect the error (not yet?) and tell you "no, it's an rvalue." Why should the compiler make assumptions about the logic of code when they may be incorrect, and might be better optimized by a person? I would think that macros would be a better fit for this.
 Assignments aren't even that expensive!  They are one of the easiest
 operations your CPU can perform!  It's like you'll have a few more MOV
 operations laying around in the worst case.

It's not the assignments, its the idea that the compiler should workaround the poor design of the code by writing possibly incorrect code. I'd rather be in charge of the design of my code, thanks. If the compiler help prevent me from making obvious *provable* mistakes, then fine. If it makes decisions that might be based on incomplete information, I don't want that. -Steve
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 18:27:09 -0400, Chad J  
<chadjoan __spam.is.bad__gmail.com> wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 15:01:47 -0400, Chad J
 <chadjoan __spam.is.bad__gmail.com> wrote:
 So we could have semantics that actually work, but you don't want them
 because, oh man, my code might have to do a few more assignments.  A  
 few
 assignments.  Really?!

Hold on a second, don't we already have semantics that work? I mean we can already write auto tmp = a.b; tmp.c = 5; a.b = tmp; So what is the big deal? If your widget.position is a struct with fields, then the compiler should be able to detect the error (not yet?) and tell you "no, it's an rvalue." Why should the compiler make assumptions about the logic of code when they may be incorrect, and might be better optimized by a person? I would think that macros would be a better fit for this.
 Assignments aren't even that expensive!  They are one of the easiest
 operations your CPU can perform!  It's like you'll have a few more MOV
 operations laying around in the worst case.

It's not the assignments, its the idea that the compiler should workaround the poor design of the code by writing possibly incorrect code. I'd rather be in charge of the design of my code, thanks. If the compiler help prevent me from making obvious *provable* mistakes, then fine. If it makes decisions that might be based on incomplete information, I don't want that. -Steve

Ah, well that argument has some weight. I'd like to know though, what assumptions does this make about the code? Why might they be incorrect?

Simply that the compiler assumes it's simply setting a value. However, setters are functions and can execute any code they wish. You may get into weird states that you don't want to or perform unneeded actions that you aren't expecting. The example given later in this thread is perfect -- setting the width and height individually may re-render the widget when you only should re-render once. Another example is something like this: class C { int x; } struct S { C c; } struct S2 { C c; S s() { return S(c); } void s(S s) {c = s.c;} } void main() { S2 s2; s2.c = new C; s2.s.c.x = 5; } If the compiler rewrites that last line as: auto tmp = s2.s; s2.c.x = 5; s2.s = tmp; Then it is completely unnecessary, and could cause problems if the setter does something else besides setting.
 If the compiler /knows/ that something being used is a property, I just
 don't understand how that information is incomplete.

First, because the problem of determining what code does is an NP-complete problem -- the compiler can't know what code is going to do except by running it. It's just like trying to write a program that detects infinite loops. So it must make some assumptions. Second, because D allows opaque function declarations, so the compiler might not even be able to *look* at what a property does. It can only assume, and the assumption you would have it make is that a property is a simple setter that has no other side effects.
 Also macros aren't a good fit for this because they aren't going to
 exist in D2, or necessarily ever.  Well, properties may not pop up in D2
 either, but if they're going to happen it'd be nicer if they did.

I think you can solve this problem (and solve it correctly) with some sort of fancy template magic, but not without some extra baggage (such as an extra type that might not be optimized). I envision something like this: with(Setter!(widget.location)) // don't know if this works { width = 50; height = 100; // at scope end, Setter sets widget.location to it's internal representation via a finalizer } The reason I said macros is because they have the power to rewrite the code exactly like you said the compiler should. At least with a macro you would have some indication that it is exactly what the user wants, and you can have the power to do your optimizations. -Steve
Jul 30 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 15:14:04 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:08:18 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 It would be nice if the compiler could help by simply rejecting what  
 it can reject (assignment to rvalues), but other than that, there's  
 not much that can be done.
  This can be detected in simple cases, but in the case where the end  
 point is a function, it will be difficult or impossible.

I think it is eminently possible, but we must figure a solution that doesn't complicate the language all too much.

struct S { private int _c; int c(); void c(int n); } struct S2 { S b(); } Now, in your main file you have: void main() { S2 a; a.b.c = 3; } How in the world is the compiler supposed to know whether to allow this or not?

By making a conservative decision.

If we have restrictive shit like this where the compiler thinks it knows better than me, can we at least only have it in SafeD? -Steve
Jul 30 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jul 2009 14:09:06 -0400, Nick Sabalausky <a a.a> wrote:

 "Zhenyu Zhou" <rinick gmail.com> wrote in message
 news:h4rfif$2os2$1 digitalmars.com...
 e.g.
 Rectangle rect(Rectangle r) {
  _rect = r;
  redraw();
  return _rect;
 }

 If you allow
 widget.rect.w = 200;
 widget.rect.h = 100;
 you will have to write much more code to handle the painting correctly.
 and we don't want to call redraw twice here

I've dealt with that sort of thing in C# and it's a trivial issue. When you write code such as the above, it's very clear that you're changing the rect twice. If that's a problem, you just do this: widget.rect = Rect(200, 100);

Rect has 4 values (x, y, width, height), otherwise, the example doesn't make much sense. -Steve
Jul 30 2009
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Jul 2009 14:40:21 -0400, Nick Sabalausky <a a.a> wrote:

 "Chad J" <chadjoan __spam.is.bad__gmail.com> wrote in message
 news:h4oku0$uel$1 digitalmars.com...
 Daniel Keep wrote:
 Maybe the compiler could rewrite the above as:

 auto t = a.b;
 t.c = 3;
 a.b = t;

I'd always suspected C# properties did something like this, though it's been so long since I've used C# now that I'm wondering if it's bad memory.

Same here, so I just did a little test in Visual C# 2008 Express. Turns out that rewriting *is* exactly what C# does. Code: -------------------------------------- using System; namespace ConsoleApplication1 { class Program { class Rect {

Change Rect to a struct, try again. Classes are reference types (note, I don't have a C# compiler handy, so I'm not sure what the result is). -Steve
Jul 30 2009
prev sibling next sibling parent reply Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Tue, Jul 28, 2009 at 10:33 PM, Walter
Bright<newshound1 digitalmars.com> wrote:
 The issue is what if b is a property, returns a temporary object, and that
 temp's .c field is uselessly set to 3?

So, the issue is that 'a.b()' returns a struct by value. Such return values should always be considered rvalues. Furthermore, 'rvalue.field' should also only yield an rvalue. Therefore, 'a.b.c = 3' should be flagged by the compiler as being as nonsensical as "5 = x". The compiler would flag this as an error, and the solution would of course be to make a.b's return value by reference instead.
Jul 28 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Jarrett Billingsley wrote:
 On Tue, Jul 28, 2009 at 10:33 PM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 The issue is what if b is a property, returns a temporary object, and that
 temp's .c field is uselessly set to 3?

So, the issue is that 'a.b()' returns a struct by value. Such return values should always be considered rvalues. Furthermore, 'rvalue.field' should also only yield an rvalue. Therefore, 'a.b.c = 3' should be flagged by the compiler as being as nonsensical as "5 = x". The compiler would flag this as an error, and the solution would of course be to make a.b's return value by reference instead.

Problem: what if .b is computed? Take .length as an example; the setter MUST be invoked when the length changes, or the stored value won't have any bearing on the actual number of elements being stored. Returning refs only works if the property is just pretending to be a field; in which case the correct solution is to use a field. Unless it's a property in a class as required by an interface, but you can't make that ref either because then your interface is dictating implementation. :P
Jul 28 2009
next sibling parent Benji Smith <dlanguage benjismith.net> writes:
Jarrett Billingsley wrote:
 The issue is that the compiler accepts
 no-effect modifications of temporary values as valid statements.
 There is no setter being invoked here, nor should there be.
 
 Or should there?  In the face of a value type, should the compiler
 rewrite this code as
 
 auto t = a.b();
 t.c = 3;
 a.b = t;
 
 ?  The last line of the rewrite is unnecessary if a.b() returns a
 reference type or a byref struct.  But is this what people would
 expect to happen?

I think the compiler should only rewrite the code (as above) if a.b() returns a struct, by value. The compiler can figure that out easily enough. Depending on the return types of all the different properties in a.b.c.d.e.f = 3, there might be a few ref types and a few value types returned. Each of those subexpressions would be rewritten with the appropriate semantics. --benji
Jul 30 2009
prev sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Jarrett Billingsley wrote:
 On Wed, Jul 29, 2009 at 1:08 AM, Daniel Keep<daniel.keep.lists gmail.com>
wrote:
 Problem: what if .b is computed?  Take .length as an example; the setter
 MUST be invoked when the length changes, or the stored value won't have
 any bearing on the actual number of elements being stored.

That's not the issue at hand. The issue is that the compiler accepts no-effect modifications of temporary values as valid statements. There is no setter being invoked here, nor should there be. Or should there? In the face of a value type, should the compiler rewrite this code as auto t = a.b(); t.c = 3; a.b = t; ? The last line of the rewrite is unnecessary if a.b() returns a reference type or a byref struct. But is this what people would expect to happen?

I believe it is. Note what happens if the user doesn't expect this: Rather than the reference being set to the correct value, the reference is set to the correct value. Seems fine to me. The only issue I see as being potentially bad is that if the coder doesn't know about the rewrite rule and writes a non-trivial setter that assumes that the incoming reference never was the outgoing reference. It's kind of an odd assumption, and one that's bound to fail in other cases too (where other cases is any time the caller explicitly reads from the property and then ... and then writes the original back into it).
 Returning refs only works if the property is just pretending to be a
 field; in which case the correct solution is to use a field.

Not entirely; you can (for some value of 'can') return a struct that has overloaded opAssign to do nontrivial setters. But that's kludgey and C++-y.

Yeah. Does not want.
Jul 31 2009
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
Walter Bright Wrote:

 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.

Does this problem pertain to properties? Look at this: void main() { int a=3; } Is "a" assigned usefully or uselessly? And what compiler says about it? And how did you determine that your example is useless? Did you check for destructors, invariants, copy constructors? And do your question imply that functions are allowed to return temporaries while properties aren't? And what's the difference if c is field or property?
Jul 29 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Kagamin wrote:
 Walter Bright Wrote:
 
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

Does this problem pertain to properties? Look at this: void main() { int a=3; } Is "a" assigned usefully or uselessly? And what compiler says about it? And how did you determine that your example is useless? Did you check for destructors, invariants, copy constructors?

The code above is only loosely related. The code above contains a definition initiated by the user. The code a.b.c = 4 expresses the intent of a user to effect a change a field that can be accessed later through a.b.c, whereas with properties the code will in all likelihood not do that.
 And do your question imply that functions are allowed to return temporaries
while properties aren't?

The question is very simple: given that we're used with a specific semantics for a.b.c = 3, how can we address the fact that the semantics of this familiar operation is so different (and likely so useless) when properties replace fields?
 And what's the difference if c is field or property?

It's b that's the problem. Andrei
Jul 29 2009
next sibling parent reply Kagamin <spam here.lot> writes:
Andrei Alexandrescu Wrote:

 The code above is only loosely related. The code above contains a 
 definition initiated by the user.

True, but is there any technical difference for compiler? How the usefulness of the code change depending on whether it's written by the user or... huh?.. don't know what is the alternative...
 The code a.b.c = 4 expresses the 
 intent of a user to effect a change a field that can be accessed later 
 through a.b.c, whereas with properties the code will in all likelihood 
 not do that.

That's idealistic view of things. A programmer's imagination used to be more inventive :)
 The question is very simple: given that we're used with a specific 
 semantics for a.b.c = 3, how can we address the fact that the semantics 
 of this familiar operation is so different (and likely so useless) when 
 properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.
 And what's the difference if c is field or property?

It's b that's the problem.

So do you attempt to dictate how library designers should implement their types, whether they should use value types or reference types? More important, are you trying to say that property is not allowed to return std.typecons.Unique since it's a value type?
Jul 29 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a specific 
 semantics for a.b.c = 3, how can we address the fact that the semantics 
 of this familiar operation is so different (and likely so useless) when 
 properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.

IMHO it's quite the contrary, a.b.c = 3 is a very simple and concrete problem that emphatically shows we haven't gotten properties up to snuff. Andrei
Jul 29 2009
next sibling parent reply Kagamin <spam here.lot> writes:
Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a specific 
 semantics for a.b.c = 3, how can we address the fact that the semantics 
 of this familiar operation is so different (and likely so useless) when 
 properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.

IMHO it's quite the contrary, a.b.c = 3 is a very simple and concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#. They have problems with understanding and using interfaces, inheritance, value types, but never - this thing.
Jul 29 2009
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Kagamin wrote:
 Andrei Alexandrescu Wrote:
 
 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c = 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

syntetic examples.

concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei
Jul 29 2009
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c = 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

syntetic examples.

concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei

Yes they can. And also C# shows us the solution to the problem (similar to what Walter proposed). --- public class Bar { public Foo Foo { get; set; } } public struct Foo { public int Property { get; set; } } Bar bar = new Bar(); Foo foo = new Foo(); foo.Property = 10; bar.Foo = foo; bar.Foo.Property = 20; // line 16 --- Error on line 16: Cannot modify the return value of 'Bar.Foo' because it is not a variable
Jul 29 2009
next sibling parent KennyTM~ <kennytm gmail.com> writes:
Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 9:23 AM, Jarrett
 Billingsley<jarrett.billingsley gmail.com> wrote:
 On Wed, Jul 29, 2009 at 10:44 AM, Ary Borenszweig<ary esperanto.org.ar> wrote:
 Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c = 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

syntetic examples.

concrete problem that emphatically shows we haven't gotten properties up to snuff.


can't define properties. Andrei

what Walter proposed). --- public class Bar { public Foo Foo { get; set; } } public struct Foo { public int Property { get; set; } } Bar bar = new Bar(); Foo foo = new Foo(); foo.Property = 10; bar.Foo = foo; bar.Foo.Property = 20; // line 16 --- Error on line 16: Cannot modify the return value of 'Bar.Foo' because it is not a variable


Yeh, I don't understand how any of this has anything to do with properties. It's the same question if you ask what should a.b().c = 5 do. It's the same issue whether you have properties or not, and needs a solution whether you have properties or not. --bb

Probably Walter means a.b.c = 3; // b is a field works, while a.b.c = 3; // b is a getter is a useless call. Nevertheless, if b() uses ref-return then it is meaningful.
Jul 29 2009
prev sibling next sibling parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c = 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

syntetic examples.

concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei

Yes they can. And also C# shows us the solution to the problem (similar to what Walter proposed). --- public class Bar { public Foo Foo { get; set; } } public struct Foo { public int Property { get; set; } } Bar bar = new Bar(); Foo foo = new Foo(); foo.Property = 10; bar.Foo = foo; bar.Foo.Property = 20; // line 16 --- Error on line 16: Cannot modify the return value of 'Bar.Foo' because it is not a variable

What about something like "array.length++;"? It's a very similar problem, but not quite the same. Generalizes to all unary operators I think.
Jul 29 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should
 
  a.b().c = 5
 
  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected. One great thing about properties is that they are mostly interchangeable with fields. The a.b.c = 3 problem works against that. Andrei
Jul 29 2009
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected.

No. Whenever you do Expression1 . Expression2 . Expression3 = Expression4. and Expression 2 is a struct type, and Expression 3 is not a static field of the struct, that should be an error.
Jul 29 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected.

No. Whenever you do Expression1 . Expression2 . Expression3 = Expression4. and Expression 2 is a struct type, and Expression 3 is not a static field of the struct, that should be an error.

I don't think D allows expressions to the right of ".". Andrei
Jul 29 2009
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected.

No. Whenever you do Expression1 . Expression2 . Expression3 = Expression4. and Expression 2 is a struct type, and Expression 3 is not a static field of the struct, that should be an error.

I don't think D allows expressions to the right of ".".

Ok, then change the rule to a . b . c = d; where b is an identifier or a function call, c is an identifier.
Jul 29 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected.

No. Whenever you do Expression1 . Expression2 . Expression3 = Expression4. and Expression 2 is a struct type, and Expression 3 is not a static field of the struct, that should be an error.

I don't think D allows expressions to the right of ".".

Ok, then change the rule to a . b . c = d; where b is an identifier or a function call, c is an identifier.

How about this fella then. a.b[5].c = d; Should work or not? Andrei
Jul 29 2009
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected.

No. Whenever you do Expression1 . Expression2 . Expression3 = Expression4. and Expression 2 is a struct type, and Expression 3 is not a static field of the struct, that should be an error.

I don't think D allows expressions to the right of ".".

Ok, then change the rule to a . b . c = d; where b is an identifier or a function call, c is an identifier.

How about this fella then. a.b[5].c = d; Should work or not? Andrei

Not if b[5] is a struct. Do I need to list evey possible syntax there it to get my point clear? :-( I'll change it to: a.b.c = d; If b is anything that has a struct type, and c is anything else, the statement must report an error.
Jul 29 2009
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:39:07 -0400, Ary Borenszweig 
 <ary esperanto.org.ar> wrote:
 
 a.b.c = d;

 If b is anything that has a struct type, and c is anything else, the 
 statement must report an error.

struct S { int *x; void c(int n) {*x = n;} } struct S2 { int n; S b() { return S(&n);} } void main() { S2 a; a.b.c = 5; } Why should this not be allowed?

Because in the general case it might not work. It's simple: if you disallow it, no bugs caused because of this can exist. If you don't disallow it, sometimes it might work, sometimes it won't work. Which option do you prefer as a programmer?
Jul 29 2009
parent Ary Borenszweig <ary esperanto.org.ar> writes:
Steven Schveighoffer Wrote:

 On Wed, 29 Jul 2009 14:50:42 -0400, Ary Borenszweig <ary esperanto.org.ar>  
 wrote:
 
 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:39:07 -0400, Ary Borenszweig  
 <ary esperanto.org.ar> wrote:

 a.b.c = d;

 If b is anything that has a struct type, and c is anything else, the  
 statement must report an error.

{ int *x; void c(int n) {*x = n;} } struct S2 { int n; S b() { return S(&n);} } void main() { S2 a; a.b.c = 5; } Why should this not be allowed?

Because in the general case it might not work. It's simple: if you disallow it, no bugs caused because of this can exist. If you don't disallow it, sometimes it might work, sometimes it won't work. Which option do you prefer as a programmer?

I prefer to have the power to create whatever I want without the compiler telling me incorrectly that it won't work. why allow any programming at all? The programmer might write incorrect code! -Steve

I reply using the web page because Thunderbird gives me a Bad Request error (very strange). The cases where you do: a.b.c = 3; where b is a struct are probably few, and most of the time they lead to incorrect result, such as the Widget and Rectangle examples, and many others. So I prefer the compiler to tell me "Look, b is a struct, please make sure you know what you are doing". And I can tell you, most of the time you'll say "Thanks, compiler, I didn't notice that!". If the compiler is wrong, you just do: auto x = a.b; b.c = 3; and that's it. No big deal.
Jul 29 2009
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c = 5 makes it clear that there's a function call in the mix, so the field-like behavior is not necessarily to be expected.

No. Whenever you do Expression1 . Expression2 . Expression3 = Expression4. and Expression 2 is a struct type, and Expression 3 is not a static field of the struct, that should be an error.

I don't think D allows expressions to the right of ".".

Ok, then change the rule to a . b . c = d; where b is an identifier or a function call, c is an identifier.

How about this fella then. a.b[5].c = d; Should work or not? Andrei

Not if b[5] is a struct. Do I need to list evey possible syntax there it to get my point clear? :-(

Yes, you actually need to. It was the point of my post. Andrei
Jul 29 2009
parent Rainer Deyke <rainerd eldwood.com> writes:
Andrei Alexandrescu wrote:
 Ary Borenszweig wrote:
 Not if b[5] is a struct. Do I need to list evey possible syntax there
 it to get my point clear? :-(

Yes, you actually need to. It was the point of my post.

Simple rule: rvalues cannot be modified (i.e. rvalues are implicitly const). No need to list all situations that generate rvalues. Or does that miss any cases? -- Rainer Deyke - rainerd eldwood.com
Jul 29 2009
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:39:07 -0400, Ary Borenszweig 
 <ary esperanto.org.ar> wrote:
 
 a.b.c = d;

 If b is anything that has a struct type, and c is anything else, the 
 statement must report an error.

struct S { int *x; void c(int n) {*x = n;} } struct S2 { int n; S b() { return S(&n);} } void main() { S2 a; a.b.c = 5; } Why should this not be allowed?

Simpler yet: class C { int x; } struct B { C c; } struct A { C x; B b() { return B(x); } void main() { A a; a.x = new C; a.b.c = 42; // fine, modifies the C object } Andrei
Jul 29 2009
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 10:50 AM, Andrei
 Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

call in the mix, so the field-like behavior is not necessarily to be expected. One great thing about properties is that they are mostly interchangeable with fields. The a.b.c = 3 problem works against that.

Maybe you were expecting it to be possible to make properties completely indistinguishable from fields?

Not quite. If they were completelty indistinguishable from fields, they'd be fields.
 But that can't really be
 when you can take the address of a field and not a property.

Correct. Probably there are other issues as well.
 The goal
 has to be removing as many differences in behavior as possible.

Yah, but that's a bit vague. The a.b.c=3 problem is that a code that looks the same with fields or properties does very different things, and useless under certain conditions.
 On the point above,  a.b.c = 5 is dicey whether b is obviously a
 function call or not, if what b returns is not an lvalue.  If I think
 a.b() is returning a ref and it doesn't, then I want the compiler to
 tell me I'm doing something silly -- if it can determine so.
 Properties or no properties.

Yah. That is by the way a wart inherited from C++. It's one of the few places where you can reach the address of an rvalue: you call a function returning an rvalue and then you call a member function of the rvalue. Inside that member function, there's no knowledge that the object is an rvalue. I strongly disliked that behavior of C++, and I dislike it as strongly for D. However, there are cases in which it *does* make sense to call a member function against an rvalue. But those are few and far apart, so I'd like us to look into disallowing member function calls against structs in all situation. That would rid us of this problem and many others. Andrei
Jul 29 2009
parent Ary Borenszweig <ary esperanto.org.ar> writes:
Andrei Alexandrescu wrote:
 Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 10:50 AM, Andrei
 Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties.  It's the same question if you ask what should

  a.b().c = 5

  do.  It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

function call in the mix, so the field-like behavior is not necessarily to be expected. One great thing about properties is that they are mostly interchangeable with fields. The a.b.c = 3 problem works against that.

Maybe you were expecting it to be possible to make properties completely indistinguishable from fields?

Not quite. If they were completelty indistinguishable from fields, they'd be fields.
 But that can't really be
 when you can take the address of a field and not a property.

Correct. Probably there are other issues as well.
 The goal
 has to be removing as many differences in behavior as possible.

Yah, but that's a bit vague. The a.b.c=3 problem is that a code that looks the same with fields or properties does very different things, and useless under certain conditions.
 On the point above,  a.b.c = 5 is dicey whether b is obviously a
 function call or not, if what b returns is not an lvalue.  If I think
 a.b() is returning a ref and it doesn't, then I want the compiler to
 tell me I'm doing something silly -- if it can determine so.
 Properties or no properties.

Yah. That is by the way a wart inherited from C++. It's one of the few places where you can reach the address of an rvalue: you call a function returning an rvalue and then you call a member function of the rvalue. Inside that member function, there's no knowledge that the object is an rvalue. I strongly disliked that behavior of C++, and I dislike it as strongly for D. However, there are cases in which it *does* make sense to call a member function against an rvalue. But those are few and far apart, so I'd like us to look into disallowing member function calls against structs in all situation. That would rid us of this problem and many others.

I don't know why you keep discussing this if C# already implements it correctly and nobody has problems with it. Can't D just copy the idea and that's it? *It works.* C# allows you to invoke methods on returned structs. If that method modifies the struct itself, then doing something like: bar.Foo.Method(); // Method does this.Property = 30; then when you do: int x = bar.Foo.Property; x won't have the value 30. So I think D should also forbid invoking methods on returned structs. You can always assign Foo to a temporary and work from it.
Jul 29 2009
prev sibling parent reply "Nick Sabalausky" <a a.a> writes:
"Ary Borenszweig" <ary esperanto.org.ar> wrote in message 
news:h4pn90$3070$1 digitalmars.com...
 Yes they can. And also C# shows us the solution to the problem (similar to 
 what Walter proposed).

 ---
 public class Bar
 {
     public Foo Foo { get; set; }
 }

 public struct Foo
 {
     public int Property { get; set; }
 }

 Bar bar = new Bar();
 Foo foo = new Foo();
 foo.Property = 10;
 bar.Foo = foo;

 bar.Foo.Property = 20; // line 16
 ---

 Error on line 16: Cannot modify the return value of 'Bar.Foo' because it 
 is not a variable

C# shows us *A* solution. Doesn't mean we can't do one better. The whole point of properties is that, to the outside observer, they behave, as much as possible, like plain fields. Obviously this can't be done in all cases (like getting an int* from &myIntProp), but in this case: widget.sizeAsAStructField.width = 10; // Works widget.sizeAsAStructProp.width = 10; // Doesn't work ...we absolutely can fix that even if C# doesn't.
Jul 30 2009
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Nick Sabalausky wrote:
 "Ary Borenszweig" <ary esperanto.org.ar> wrote in message 
 news:h4pn90$3070$1 digitalmars.com...
 Yes they can. And also C# shows us the solution to the problem (similar to 
 what Walter proposed).

 ---
 public class Bar
 {
     public Foo Foo { get; set; }
 }

 public struct Foo
 {
     public int Property { get; set; }
 }

 Bar bar = new Bar();
 Foo foo = new Foo();
 foo.Property = 10;
 bar.Foo = foo;

 bar.Foo.Property = 20; // line 16
 ---

 Error on line 16: Cannot modify the return value of 'Bar.Foo' because it 
 is not a variable

C# shows us *A* solution. Doesn't mean we can't do one better. The whole point of properties is that, to the outside observer, they behave, as much as possible, like plain fields. Obviously this can't be done in all cases (like getting an int* from &myIntProp), but in this case: widget.sizeAsAStructField.width = 10; // Works widget.sizeAsAStructProp.width = 10; // Doesn't work ...we absolutely can fix that even if C# doesn't.

I don't understand your example. What's wrong with it? What doesn't work as you expect and what is your expected result? In the first case, if you modify sizeAsAStructField.width, it's ok and should compile, because you are modifying sizeAsAStructField itself, not a copy of it. In C#: public struct Foo { public int Property { get; set; } } public class Bar { public Foo Foo; } static Main() { Bar bar = new Bar(); Foo foo = new Foo(); foo.Property = 10; bar.Foo = foo; bar.Foo.Property = 20; // prints 20, as expected Console.WriteLine(bar.Foo.Property); }
Jul 30 2009
parent "Nick Sabalausky" <a a.a> writes:
"Ary Borenszweig" <ary esperanto.org.ar> wrote in message 
news:h4su5p$2ctj$1 digitalmars.com...
 Nick Sabalausky wrote:
 C# shows us *A* solution. Doesn't mean we can't do one better.

 The whole point of properties is that, to the outside observer, they 
 behave, as much as possible, like plain fields. Obviously this can't be 
 done in all cases (like getting an int* from &myIntProp), but in this 
 case:

 widget.sizeAsAStructField.width = 10; // Works
 widget.sizeAsAStructProp.width = 10; // Doesn't work

 ...we absolutely can fix that even if C# doesn't.

I don't understand your example. What's wrong with it? What doesn't work as you expect and what is your expected result? In the first case, if you modify sizeAsAStructField.width, it's ok and should compile, because you are modifying sizeAsAStructField itself, not a copy of it. In C#:

Here's the problem (still in C#): --------------------- public struct MyStruct { public int x; } public class ClassWithProp { // The whole point of properties is that the interface // for these two should work as similarly as possible. public MyStruct field; public MyStruct prop {get;set;} } static Main() { ClassWithProp classWithProp = new ClassWithProp(); // Works classWithProp.field.x = 10; // Doesn't currently work in either C# or D. // *But*, unlike "&field" vs "&prop", this prop-vs-field // inconsistency *is* easily fixable, // and therefore should be fixed. classWithProp.prop.x = 10; } ---------------------
Jul 30 2009
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-07-30 15:32:58 -0400, "Nick Sabalausky" <a a.a> said:

 C# shows us *A* solution. Doesn't mean we can't do one better.
 
 The whole point of properties is that, to the outside observer, they behave,
 as much as possible, like plain fields. Obviously this can't be done in all
 cases (like getting an int* from &myIntProp), but in this case:
 
 widget.sizeAsAStructField.width = 10; // Works
 widget.sizeAsAStructProp.width = 10; // Doesn't work
 
 ...we absolutely can fix that even if C# doesn't.

I'm undecided about what to do here. But I'd like to note that while common in our current examples, assignment to a member of the returned struct is *one* thing that can change the struct. Any function taking a non-const reference to the struct can also change it. For instance, let's add a call to a mutator for the size struct of your example: widget.sizeAsStructProp.increaseBy(10, 10); In this case, you'll also want the compiler automatically call the setter once it's done. And that doesn't apply only to member functions, you could define it a standalone function taking a ref to the struct and call it that way: increaseSizeBy(widget.size.sizeAsStructProp, 10, 10); In all those cases, you'll want to call the setter back once the struct has been modified. Don't take this as an argument for or against automatically setting the modified struct property value; I'm just pointing out that operators (and specially the assignment operator) isn't the only way to change a value. Assuming the non-mutator functions take a const ref to the struct, the compiler should be able to avoid setting back the property value when it's not necessary. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jul 30 2009
prev sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 10:36 AM, Bill Baxter<wbaxter gmail.com> wrote:
 On Wed, Jul 29, 2009 at 10:26 AM, Steven
 Schveighoffer<schveiguy yahoo.com> wrote:
 On Wed, 29 Jul 2009 13:22:14 -0400, Bill Baxter <wbaxter gmail.com> wrot=


 Similarly, taking the address should lead to an error.

 auto ptr =3D &a.b; =A0// ok if b is a field
 auto ptr =3D &a.b; // bogus if b is a property returning a non-lvalue

Impossible with the current implementation -- &a.b is a delegate :)


[edit]
 Good point. =A0I suppose we can chalk that up as another problem with
 not having real properties. =A0Though if you were expecting it to be an
 int* then you'll probably encounter a compiler

*error*
 sooner or later. =A0Just
 might be far from where the actual problem is.

--bb
Jul 29 2009
prev sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 10:26 AM, Steven
Schveighoffer<schveiguy yahoo.com> wrote:
 On Wed, 29 Jul 2009 13:22:14 -0400, Bill Baxter <wbaxter gmail.com> wrote=

 Similarly, taking the address should lead to an error.

 auto ptr =3D &a.b; =A0// ok if b is a field
 auto ptr =3D &a.b; // bogus if b is a property returning a non-lvalue

Impossible with the current implementation -- &a.b is a delegate :)

Good point. I suppose we can chalk that up as another problem with not having real properties. Though if you were expecting it to be an int* then you'll probably encounter a compiler sooner or later. Just might be far from where the actual problem is. --bb
Jul 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 10:11 AM, KennyTM~<kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 9:23 AM, Jarrett
 Billingsley<jarrett.billingsley gmail.com> wrote:
 On Wed, Jul 29, 2009 at 10:44 AM, Ary Borenszweig<ary esperanto.org.ar>
 wrote:
 Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c =3D 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.

IMHO it's quite the contrary, a.b.c =3D 3 is a very simple and concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei

Yes they can. And also C# shows us the solution to the problem (simila=




 to
 what Walter proposed).

 ---
 public class Bar
 {
 =A0 public Foo Foo { get; set; }
 }

 public struct Foo
 {
 =A0 public int Property { get; set; }
 }

 Bar bar =3D new Bar();
 Foo foo =3D new Foo();
 foo.Property =3D 10;
 bar.Foo =3D foo;

 bar.Foo.Property =3D 20; // line 16
 ---

 Error on line 16: Cannot modify the return value of 'Bar.Foo' because =




 is
 not a variable

Booom, exactly what I said about rvalues.

Yeh, I don't understand how any of this has anything to do with properties. =A0It's the same question if you ask what should =A0a.b().c =3D 5 =A0do. =A0It's the same issue whether you have properties or not, and needs a solution whether you have properties or not. --bb

Probably Walter means a.b.c =3D 3; =A0 =A0 =A0// b is a field works, while a.b.c =3D 3; =A0 =A0 =A0// b is a getter is a useless call. Nevertheless, if b() uses ref-return then it is meaningful.

Similarly, taking the address should lead to an error. auto ptr =3D &a.b; // ok if b is a field auto ptr =3D &a.b; // bogus if b is a property returning a non-lvalue --bb
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 13:22:14 -0400, Bill Baxter <wbaxter gmail.com> wrote:

 Similarly, taking the address should lead to an error.

 auto ptr = &a.b;  // ok if b is a field
 auto ptr = &a.b; // bogus if b is a property returning a non-lvalue

Impossible with the current implementation -- &a.b is a delegate :) -Steve
Jul 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 10:11 AM, KennyTM~<kennytm gmail.com> wrote:
 Bill Baxter wrote:
 On Wed, Jul 29, 2009 at 9:23 AM, Jarrett
 Billingsley<jarrett.billingsley gmail.com> wrote:
 On Wed, Jul 29, 2009 at 10:44 AM, Ary Borenszweig<ary esperanto.org.ar>
 wrote:
 Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c =3D 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.

IMHO it's quite the contrary, a.b.c =3D 3 is a very simple and concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei

Yes they can. And also C# shows us the solution to the problem (simila=




 to
 what Walter proposed).

 ---
 public class Bar
 {
 =A0 public Foo Foo { get; set; }
 }

 public struct Foo
 {
 =A0 public int Property { get; set; }
 }

 Bar bar =3D new Bar();
 Foo foo =3D new Foo();
 foo.Property =3D 10;
 bar.Foo =3D foo;

 bar.Foo.Property =3D 20; // line 16
 ---

 Error on line 16: Cannot modify the return value of 'Bar.Foo' because =




 is
 not a variable

Booom, exactly what I said about rvalues.

Yeh, I don't understand how any of this has anything to do with properties. =A0It's the same question if you ask what should =A0a.b().c =3D 5 =A0do. =A0It's the same issue whether you have properties or not, and needs a solution whether you have properties or not. --bb

Probably Walter means a.b.c =3D 3; =A0 =A0 =A0// b is a field works, while a.b.c =3D 3; =A0 =A0 =A0// b is a getter is a useless call. Nevertheless, if b() uses ref-return then it is meaningful.

a.b as a field effectively "returns" a ref, so a.b.c=3D3 is OK. a.b as a property does not, so may be an error. Similarly if a.b() returns a ref or pointer, a.b().c =3D 3 is OK. but if a.b() returns a tmp, it a.b().c =3D 3 may be an error. Not really any different. Seems like BCS, Steve, and Jarrett are all over this red herring, though. --bb
Jul 29 2009
prev sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 10:50 AM, Andrei
Alexandrescu<SeeWebsiteForEmail erdani.org> wrote:
 Bill Baxter wrote:
 Yeh, I don't understand how any of this has anything to do with
 properties. =A0It's the same question if you ask what should

 =A0a.b().c =3D 5

 =A0do. =A0It's the same issue whether you have properties or not, and
 needs a solution whether you have properties or not.

Well the problem is that a.b().c =3D 5 makes it clear that there's a func=

 call in the mix, so the field-like behavior is not necessarily to be
 expected.

 One great thing about properties is that they are mostly interchangeable
 with fields. The a.b.c =3D 3 problem works against that.

Maybe you were expecting it to be possible to make properties completely indistinguishable from fields? But that can't really be when you can take the address of a field and not a property. The goal has to be removing as many differences in behavior as possible. On the point above, a.b.c =3D 5 is dicey whether b is obviously a function call or not, if what b returns is not an lvalue. If I think a.b() is returning a ref and it doesn't, then I want the compiler to tell me I'm doing something silly -- if it can determine so. Properties or no properties. --bb
Jul 29 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 29 Jul 2009 18:36:07 +0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c = 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

syntetic examples.

concrete problem that emphatically shows we haven't gotten properties up to snuff.


Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei

I believe you can still return a struct from an object via property.
Jul 29 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Jul 29, 2009 at 10:44 AM, Ary Borenszweig<ary esperanto.org.ar> wro=
te:
 Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c =3D 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.

IMHO it's quite the contrary, a.b.c =3D 3 is a very simple and concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their structs can't define properties. Andrei

Yes they can. And also C# shows us the solution to the problem (similar t=

 what Walter proposed).

 ---
 public class Bar
 {
 =A0 =A0public Foo Foo { get; set; }
 }

 public struct Foo
 {
 =A0 =A0public int Property { get; set; }
 }

 Bar bar =3D new Bar();
 Foo foo =3D new Foo();
 foo.Property =3D 10;
 bar.Foo =3D foo;

 bar.Foo.Property =3D 20; // line 16
 ---

 Error on line 16: Cannot modify the return value of 'Bar.Foo' because it =

 not a variable

Booom, exactly what I said about rvalues.
Jul 29 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Wed, Jul 29, 2009 at 9:23 AM, Jarrett
Billingsley<jarrett.billingsley gmail.com> wrote:
 On Wed, Jul 29, 2009 at 10:44 AM, Ary Borenszweig<ary esperanto.org.ar> w=

 Andrei Alexandrescu wrote:
 Kagamin wrote:
 Andrei Alexandrescu Wrote:

 Kagamin wrote:
 Andrei Alexandrescu Wrote:
 The question is very simple: given that we're used with a
 specific semantics for a.b.c =3D 3, how can we address the fact
 that the semantics of this familiar operation is so different
 (and likely so useless) when properties replace fields?

You're solving problems that never came to life. Well... only as syntetic examples.

IMHO it's quite the contrary, a.b.c =3D 3 is a very simple and concrete problem that emphatically shows we haven't gotten properties up to snuff.

Never saw this problem in C#.

Of course you didn't. This is because C# doesn't have it - their struct=



 can't define properties.

 Andrei

Yes they can. And also C# shows us the solution to the problem (similar =


 what Walter proposed).

 ---
 public class Bar
 {
 =A0 =A0public Foo Foo { get; set; }
 }

 public struct Foo
 {
 =A0 =A0public int Property { get; set; }
 }

 Bar bar =3D new Bar();
 Foo foo =3D new Foo();
 foo.Property =3D 10;
 bar.Foo =3D foo;

 bar.Foo.Property =3D 20; // line 16
 ---

 Error on line 16: Cannot modify the return value of 'Bar.Foo' because it=


 not a variable

Booom, exactly what I said about rvalues.

Yeh, I don't understand how any of this has anything to do with properties. It's the same question if you ask what should a.b().c =3D 5 do. It's the same issue whether you have properties or not, and needs a solution whether you have properties or not. --bb
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 14:39:07 -0400, Ary Borenszweig <ary esperanto.org.ar>  
wrote:

 a.b.c = d;

 If b is anything that has a struct type, and c is anything else, the  
 statement must report an error.

struct S { int *x; void c(int n) {*x = n;} } struct S2 { int n; S b() { return S(&n);} } void main() { S2 a; a.b.c = 5; } Why should this not be allowed?
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 14:50:42 -0400, Ary Borenszweig <ary esperanto.org.ar>  
wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:39:07 -0400, Ary Borenszweig  
 <ary esperanto.org.ar> wrote:

 a.b.c = d;

 If b is anything that has a struct type, and c is anything else, the  
 statement must report an error.

{ int *x; void c(int n) {*x = n;} } struct S2 { int n; S b() { return S(&n);} } void main() { S2 a; a.b.c = 5; } Why should this not be allowed?

Because in the general case it might not work. It's simple: if you disallow it, no bugs caused because of this can exist. If you don't disallow it, sometimes it might work, sometimes it won't work. Which option do you prefer as a programmer?

I prefer to have the power to create whatever I want without the compiler telling me incorrectly that it won't work. why allow any programming at all? The programmer might write incorrect code! -Steve
Jul 29 2009
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 29 Jul 2009 15:00:42 -0400, Ary Borenszweig <ary esperanto.org.ar>  
wrote:

 Steven Schveighoffer Wrote:

 On Wed, 29 Jul 2009 14:50:42 -0400, Ary Borenszweig  
 <ary esperanto.org.ar>
 wrote:

 Steven Schveighoffer wrote:
 On Wed, 29 Jul 2009 14:39:07 -0400, Ary Borenszweig
 <ary esperanto.org.ar> wrote:

 a.b.c = d;

 If b is anything that has a struct type, and c is anything else, the
 statement must report an error.

{ int *x; void c(int n) {*x = n;} } struct S2 { int n; S b() { return S(&n);} } void main() { S2 a; a.b.c = 5; } Why should this not be allowed?

Because in the general case it might not work. It's simple: if you disallow it, no bugs caused because of this can exist. If you don't disallow it, sometimes it might work, sometimes it won't work. Which option do you prefer as a programmer?

I prefer to have the power to create whatever I want without the compiler telling me incorrectly that it won't work. why allow any programming at all? The programmer might write incorrect code! -Steve

I reply using the web page because Thunderbird gives me a Bad Request error (very strange).

I retracted my original message (just found out how to do that), because I wrote "...without the telling me..." instead of "...without the *compiler* telling me..." You probably tried to reply to my original message ;)
 The cases where you do:

 a.b.c = 3;

 where b is a struct are probably few

Let's not forget that structs are the vehicle to extend the type system. I want to make my own pointer type, it has to be a struct. You're going to predjudice the compiler against my wrapper types because I *might* do something that confuses people.
 and most of the time they lead to incorrect result, such as the Widget  
 and Rectangle examples, and many others.

In fact, this is the *only* example I've seen. It leads me to believe we need a better approach to widgets and rectangles more than a better approach to properties. Like maybe making Rectangles and Points immutable? I think that's how C# does it.
 So I prefer the compiler to tell me "Look, b is a struct, please make  
 sure you know what you are doing". And I can tell you, most of the time  
 you'll say "Thanks, compiler, I didn't notice that!". If the compiler is  
 wrong, you just do:

 auto x = a.b;
 b.c = 3;

 and that's it. No big deal.

It is a big deal. Ever used Vista? It has a similar conservative approach to security. It makes Vista unusable in my opinion. -Steve
Jul 29 2009
prev sibling next sibling parent KennyTM~ <kennytm gmail.com> writes:
Walter Bright wrote:
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.
 
 I don't see how C#'s special property syntax adds any value for dealing 
 with this.
 
 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

.... unless it returns a ref.
Jul 29 2009
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2009-07-28 22:33:53 -0400, Walter Bright <newshound1 digitalmars.com> said:

 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as functions.

With the local namespace approach I propsed a little while ago, you could allow "subproperties": namespace a { namespace b { namespace c { int opGet() {...} void opAssign(int) {...} } } } or (alternate syntax for the above): int a.b.c.opGet() {...} void a.b.c.opAssign(int) {...} Although it'd be somewhat clumbersome to redefine every member of a struct like this each time you make a property just so assigning to it works fine (I guess a mixin or some compiler magic could help).
 I don't see how C#'s special property syntax adds any value for dealing 
 with this.

Me neither.
 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

I think that's a little harsh. If you return something by reference (like an object), the waterver you do to that object won't be lost. It's for value types (like structs) that this is a problem. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Jul 29 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 On 2009-07-28 22:33:53 -0400, Walter Bright <newshound1 digitalmars.com> 
 said:
 
 The issue is what if b is a property, returns a temporary object, and 
 that temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

With the local namespace approach I propsed a little while ago, you could allow "subproperties": namespace a { namespace b { namespace c { int opGet() {...} void opAssign(int) {...} } } }

We're looking for a modular solution - one that allows one person to define the type of b and another person to define the type of a, which has a member b. Andrei
Jul 29 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Wed, Jul 29, 2009 at 1:08 AM, Daniel Keep<daniel.keep.lists gmail.com> w=
rote:
 Jarrett Billingsley wrote:
 On Tue, Jul 28, 2009 at 10:33 PM, Walter
 Bright<newshound1 digitalmars.com> wrote:
 The issue is what if b is a property, returns a temporary object, and t=



 temp's .c field is uselessly set to 3?

So, the issue is that 'a.b()' returns a struct by value. =A0Such return values should always be considered rvalues. =A0Furthermore, 'rvalue.field' should also only yield an rvalue. =A0Therefore, 'a.b.c =


 3' should be flagged by the compiler as being as nonsensical as "5 =3D
 x".

 The compiler would flag this as an error, and the solution would of
 course be to make a.b's return value by reference instead.

Problem: what if .b is computed? =A0Take .length as an example; the sette=

 MUST be invoked when the length changes, or the stored value won't have
 any bearing on the actual number of elements being stored.

That's not the issue at hand. The issue is that the compiler accepts no-effect modifications of temporary values as valid statements. There is no setter being invoked here, nor should there be. Or should there? In the face of a value type, should the compiler rewrite this code as auto t =3D a.b(); t.c =3D 3; a.b =3D t; ? The last line of the rewrite is unnecessary if a.b() returns a reference type or a byref struct. But is this what people would expect to happen?
 Returning refs only works if the property is just pretending to be a
 field; in which case the correct solution is to use a field.

Not entirely; you can (for some value of 'can') return a struct that has overloaded opAssign to do nontrivial setters. But that's kludgey and C++-y.
Jul 29 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 28 Jul 2009 22:33:53 -0400, Walter Bright  
<newshound1 digitalmars.com> wrote:

 The issue is what if b is a property, returns a temporary object, and  
 that temp's .c field is uselessly set to 3?

There's one way you could do it -- since the return value is a temporary, it should not be an lvalue. But it's members could be lvalues, but only if they 1) are reference types or 2) implement opAssign. So for instance: struct S1 { int c; } If a.b returns an S, then it's not an lvalue, and since c is part of the struct, it's also not an lvalue, so the result should be an error. If a.b returns a ref S, then it's an lvalue, and c is also an lvalue. I think the compiler already restricts you from assigning to rvalues, so it could be extended. An opAssign example: struct S2 { private int *refdint; void opAssign(int x) {*refdint = x;} } if a.b.c returned an S2, even as a temporary, then it should be allowed. Unfortunately, even if the opAssign simply stores the result inside the S2 struct, then you still need to allow it, because the compiler cannot tell what the opAssign is going to do.
 It's a classic problem with properties that are implemented as functions.

Is there another implementation that allows this to work magically? I've only ever seen properties as functions.
 I don't see how C#'s special property syntax adds any value for dealing  
 with this.

And I don't see why it should, or where anyone said it would. The syntax only helps to allow the author to explain what a property is. Note that most types in C# are reference types, so for the most part, there is no issue.
 One thought I had was to simply disallow the '.' to appear after a  
 function style property.

Then you forbid struct types that wrap a reference from being returned, or from affecting ref types that were returned. -Steve
Jul 29 2009
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Walter,

 The issue is what if b is a property, returns a temporary object, and
 that temp's .c field is uselessly set to 3?
 
 It's a classic problem with properties that are implemented as
 functions.
 
 I don't see how C#'s special property syntax adds any value for
 dealing with this.
 
 One thought I had was to simply disallow the '.' to appear after a
 function style property.
 

seems related to this: http://d.puremagic.com/issues/show_bug.cgi?id=3008
Jul 29 2009
prev sibling parent "Jimbob" <jim bob.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:h4ocet$27k$1 digitalmars.com...
 The issue is what if b is a property, returns a temporary object, and that 
 temp's .c field is uselessly set to 3?

 It's a classic problem with properties that are implemented as functions.

 I don't see how C#'s special property syntax adds any value for dealing 
 with this.

 One thought I had was to simply disallow the '.' to appear after a 
 function style property.

Disallow it by default. IE. a.b.c.d = 3; If any of those return an rvalue / temporary then it should be an error to assign to it. However we should not disallow... int i = a.b.c.d.e.f; But we should also find a way to let the programmer tell the compiler if it is ok for it to rewrite... a.b.c = 3; as B tmp = a.b; tmp.c = 3; a.b = tmp; perhaps an extra operator opSubAssign() Which tells the compiler it's ok to rewrite the the assignment using temporrarys and reassignment. class A { B _b; void b.opAssign(ref B bbb) { _b = bbb; } void b.opSubAssign(ref B bbb) { opAssign(bbb); } } Then the programmer could also write an specialized subassignment.
Jul 30 2009