www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Property rewriting; I feel it's important. Is there still time?

reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
I speak of the property rewriting where an expression like

    foo.prop++;

is rewritten as

    auto t = foo.prop();
    t++;
    foo.prop(t);


So, Walter or Andrei or someone on the planning behind the scenes,
please lend me your thoughts:
How much time is left to make this sort of thing happen?
If a working patch for this showed up, would it have a reasonable chance
of acceptance at this point?

I really want to make this happen, even if I have to pay someone to do
it or finish it off.  It's very close but I have almost nil free time
for this stuff.

Note that I have made it work and seen it in action.  There'd be a patch
two months ago if I hadn't decided to rebel against the way DMD did things*.

Now I'll try and remind you why this patch is important:

- I see no other way to make properties behave like lvalues in an
elegant manner.  Explicit property syntax does not help this.  This is
quite a semantics problem, and the syntax is completely orthogonal.

- Having property rewrites allows the special case for "array.length +=
foo;" to be removed.  Property rewriting is the more general solution
that will work for all properties and in arbitrary expressions.

- By treating opIndex and opIndexAssign as properties then that pair
alone will make cases like "a[i]++;" work correctly without the need for
opIndexUnary overloads.  Also "a[i] += foo" will work too, as well as
anything else you haven't thought of yet.

- It has implications for purity.  We may want to figure this out before
its too late.  Consider "foo.prop + bar;", where prop returns a struct
with opBinary!("+") defined.  It ends up being "foo.prop.opAdd(bar)".
In general this is "foo.property.someFunction(args...)".  In the general
case you want it to call the setter function for the property so that
any changes in state get propagated back to foo, as we would expect from
an lvalue.  If there is no setter, it's an error.  However, in the
(usual) case of opAdd, this is an unnecessary restriction since opAdd is
likely pure and just returns a value -- there is no need to require foo
to have a setter.
	It makes sense to me to allow the purity of member functions to
determine whether or not any parent property needs its setter called.
Alternatively we could just make member function calls not count as side
effects for properties.  That would be more dangerous though, because
sometimes they ARE side effects.  Things like += and -= are not as
troubling because they are ALWAYS side effects, so we know what to do
with them.  I want +=, -=, ++, etc. to be covered even if this purity
issue is completely ignored.



* So I made it work.  Just not well enough for production quality,
unfortunately.  The problem had nothing to do with the rewrite being
difficult or impossible to do.  Instead, the problem was me rebelling
against the way DMD does things, followed by failing on rare instances
of valid code.  Calling Expression->semantic() twice on the same
expression will do that.**  This was all a little over two months ago,
back when I was still unemployed.  I've hardly touched it since then.

** If you're in the know, you might wonder why I would do a fool thing
like call Expression->semantic() twice on the same expression.  When I
originally wrote the function for doing this rewrite, I wrote it to
climb the expression tree with recursive calls.  So I tried to keep that
quality.  I'd let semantic run, then I'd recursive over the tree, then
run semantic again.  I had an idea in mind to optimize away the second
pass for expressions devoid of properties, or to even minimize the
scanning to only the branches that had both side effects and properties.
 I was hoping this would at least make it work.  But it doesn't.
	What I need to do then is rewrite my recursive function for doing the
rewrite into a flat and non-recursive function that tracks its state
using some combination of variables in Scope and variables in
Expression.  I'd be done already if I had just written it that way in
the first place.
Mar 09 2010
next sibling parent reply retard <re tard.com.invalid> writes:
Tue, 09 Mar 2010 22:48:08 -0500, Chad J wrote:

 I speak of the property rewriting where an expression like
 
     foo.prop++;
 
 is rewritten as
 
     auto t = foo.prop();
     t++;
     foo.prop(t);
 
 
 So, Walter or Andrei or someone on the planning behind the scenes,
 please lend me your thoughts:
 How much time is left to make this sort of thing happen? If a working
 patch for this showed up, would it have a reasonable chance of
 acceptance at this point?
 
 I really want to make this happen, even if I have to pay someone to do
 it or finish it off.  It's very close but I have almost nil free time
 for this stuff.
 
 Note that I have made it work and seen it in action.  There'd be a patch
 two months ago if I hadn't decided to rebel against the way DMD did
 things*.
 
 Now I'll try and remind you why this patch is important:
 
 - I see no other way to make properties behave like lvalues in an
 elegant manner.  Explicit property syntax does not help this.  This is
 quite a semantics problem, and the syntax is completely orthogonal.
 
 - Having property rewrites allows the special case for "array.length +=
 foo;" to be removed.  Property rewriting is the more general solution
 that will work for all properties and in arbitrary expressions.
 
 - By treating opIndex and opIndexAssign as properties then that pair
 alone will make cases like "a[i]++;" work correctly without the need for
 opIndexUnary overloads.  Also "a[i] += foo" will work too, as well as
 anything else you haven't thought of yet.

This is so unbelievable. I knew the property stuff was being redesigned since there was so much talk in the ng some time ago. But even now, why on earth doesn't it work like it should. Is it so hard to copy/steal the good ideas from the better languages. Guess how C#/Scala solved this - I bet having a PhD helps getting things right the first time.. Scala: ------ object foo { def a = { println("getter returns 42"); 42 } def a_=(b:Int) = println("setter sets " + b) } scala> foo.a += 3 getter returns 42 setter sets 45 C#: --- using System; class Hello { public int id { get { Console.WriteLine ("getter returns 42"); return 42; } set { Console.WriteLine ("setter sets " + value); } } static void Main() { Hello h = new Hello(); h.id += 2; } } D: -- struct Foo { property int data() { return m_data; } // read property property int data(int value) { return m_data = value; } // write property private: int m_data; } void main() { Foo f; f.data += 3; } test.d(13): Error: 'f.data' is not a scalar, it is a property int() test.d(13): Error: incompatible types for ((f.data) += (3)): ' property int()' and 'int'
Mar 10 2010
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 04:38 AM, retard wrote:
 Wed, 10 Mar 2010 10:05:03 +0000, retard wrote:

 This is so unbelievable. I knew the property stuff was being redesigned
 since there was so much talk in the ng some time ago. But even now, why
 on earth doesn't it work like it should. Is it so hard to copy/steal the
 good ideas from the better languages. Guess how C#/Scala solved this - I
 bet having a PhD helps getting things right the first time..

Heh, after noticing the thread name "D hates to be dynamic linked" I should have probably renamed this thread with a funnier name such as "D stubbornly refuses to learn from mistakes and follow the principles of good language design". It took C# 4 years to get properties right. That period also included finishing a complete language specification document, totalling almost 500 pages. The property feature wasn't present in Pizza (2002), but Scala (2004 ->) had it. It was taken D 11 years to fail again and again miserably.

I am having difficulty understanding what you are trying to convey. Andrei
Mar 10 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 09:12 AM, retard wrote:
 Wed, 10 Mar 2010 08:53:57 -0600, Andrei Alexandrescu wrote:

 On 03/10/2010 04:38 AM, retard wrote:
 Wed, 10 Mar 2010 10:05:03 +0000, retard wrote:

 This is so unbelievable. I knew the property stuff was being
 redesigned since there was so much talk in the ng some time ago. But
 even now, why on earth doesn't it work like it should. Is it so hard
 to copy/steal the good ideas from the better languages. Guess how
 C#/Scala solved this - I bet having a PhD helps getting things right
 the first time..

should have probably renamed this thread with a funnier name such as "D stubbornly refuses to learn from mistakes and follow the principles of good language design". It took C# 4 years to get properties right. That period also included finishing a complete language specification document, totalling almost 500 pages. The property feature wasn't present in Pizza (2002), but Scala (2004 ->) had it. It was taken D 11 years to fail again and again miserably.

I am having difficulty understanding what you are trying to convey. Andrei

People very rarely find any issues worth complaining in the property system implementations of those languages. I also forgot to mention Object Pascal. Does this mean that C#/Scala/Pascal users are just complaining less or are their property systems just better? When a new system is adopted by D, does anyone really analyze the large body of existing work done on the field. We don't live in a dark and closed barrel, we can learn from others and try to avoid common problems.

Absolutely. A good way to go about that is to raise the issue herein and in bugzilla and also at best contribute patches that implement the improvements. There are some ways to go about improving things that are scraping the bottoms of some dubious barrels, and almost guaranteed to do more harm than good (primarily by making the perpetrator look like a fool). Latching on an obvious troll to make a point comes to mind. Also, I have difficulty understanding the concept of attempting to make Walter bad for not having a PhD, while at the same time relying on anonymity to cover insecurity about one's own achievements. One other thing that comes to mind is oscillating between being reasonable, then insulting, then apologetic, and then rinse lather and repeat. The logic is difficult to follow. Why bother apologizing if reenactment is a given? Getting to the topic at hand, there are two sides to the coin. D got right several things that other language authors are still scratching their heads about, such as proper integration of immutability with mutability, an expressive generics system, lightweight concepts, integration of interpretation with compilation, value range propagation in integral expressions, and I dare say concurrency. (I remember an interview with Hejlsberg and a colleague (Bentley?) in which he said they're having major headaches about modeling immutable data.) That doesn't make the authors of those languages incult or boneheaded. A language is a large ecosystem that makes it rather difficult to choose what to get busy with at any given time. Generalizing from one awkwardness (I agree that properties could receive more attention) to the entire language and even the author is not something anyone should be proud of. Andrei
Mar 10 2010
prev sibling parent retard <re tard.com.invalid> writes:
Wed, 10 Mar 2010 08:53:57 -0600, Andrei Alexandrescu wrote:

 On 03/10/2010 04:38 AM, retard wrote:
 Wed, 10 Mar 2010 10:05:03 +0000, retard wrote:

 This is so unbelievable. I knew the property stuff was being
 redesigned since there was so much talk in the ng some time ago. But
 even now, why on earth doesn't it work like it should. Is it so hard
 to copy/steal the good ideas from the better languages. Guess how
 C#/Scala solved this - I bet having a PhD helps getting things right
 the first time..

should have probably renamed this thread with a funnier name such as "D stubbornly refuses to learn from mistakes and follow the principles of good language design". It took C# 4 years to get properties right. That period also included finishing a complete language specification document, totalling almost 500 pages. The property feature wasn't present in Pizza (2002), but Scala (2004 ->) had it. It was taken D 11 years to fail again and again miserably.

I am having difficulty understanding what you are trying to convey. Andrei

People very rarely find any issues worth complaining in the property system implementations of those languages. I also forgot to mention Object Pascal. Does this mean that C#/Scala/Pascal users are just complaining less or are their property systems just better? When a new system is adopted by D, does anyone really analyze the large body of existing work done on the field. We don't live in a dark and closed barrel, we can learn from others and try to avoid common problems. e.g. to me a
 object.field += something;

/is/ a rather fundamental part of any property system. It's really surprising that it still doesn't work..
Mar 10 2010
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/09/2010 09:48 PM, Chad J wrote:
 I speak of the property rewriting where an expression like

      foo.prop++;

 is rewritten as

      auto t = foo.prop();
      t++;
      foo.prop(t);

This particular example has a number of issues. First off you need to rewrite expressions, not statements. Consider: auto x = foo.prop++; You'd need to assign to x the old value of foo.prop. So one correct rewrite is foo.prop++ into {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}() within an rvalue context, and into: {auto t = foo.prop; ++t; foo.prop = t; return t;}() within a void context. I'm pointing out that things may not always be very simple, but generally it's easy to figure out the proper rewrites if attention is given to detail.
 So, Walter or Andrei or someone on the planning behind the scenes,
 please lend me your thoughts:
 How much time is left to make this sort of thing happen?
 If a working patch for this showed up, would it have a reasonable chance
 of acceptance at this point?

The idea is sensible and is already in effect for the ".length" property of arrays.
 I really want to make this happen, even if I have to pay someone to do
 it or finish it off.  It's very close but I have almost nil free time
 for this stuff.

 Note that I have made it work and seen it in action.  There'd be a patch
 two months ago if I hadn't decided to rebel against the way DMD did things*.

Probably offering payment wouldn't be much of an enticement, but lobbying reasonable ideas here is a good way to go.
 Now I'll try and remind you why this patch is important:

 - I see no other way to make properties behave like lvalues in an
 elegant manner.  Explicit property syntax does not help this.  This is
 quite a semantics problem, and the syntax is completely orthogonal.

Good point.
 - Having property rewrites allows the special case for "array.length +=
 foo;" to be removed.  Property rewriting is the more general solution
 that will work for all properties and in arbitrary expressions.

Agreed. By the way, I'm a huge fan of lowering; I think they are great for defining semantics in a principled way without a large language core. In recent times Walter has increasingly relied on lowerings and mentioned to me that the code savings in the compiler have been considerable.
 - By treating opIndex and opIndexAssign as properties then that pair
 alone will make cases like "a[i]++;" work correctly without the need for
 opIndexUnary overloads.  Also "a[i] += foo" will work too, as well as
 anything else you haven't thought of yet.

Well operator overloading handles indexing differently, and arguably better than in your proposal. Ideally we'd define operators on properties in a manner similar to the way indexing works in the new operator overloading scheme. I'll talk to Walter about that. Andrei
Mar 10 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 The idea is sensible and is already in effect for the ".length" property 
 of arrays.

I didn't know that. So I have tried this code: void main() { int[] a; a.length++; a.length--; } The compiler shows the following errors, is this correct? test1.d(3): Error: a.length is not an lvalue test1.d(4): Error: a.length is not an lvalue So I have tried this, and this compiles: void main() { int[] a; ++a.length; --a.length; } Bye, bearophile
Mar 10 2010
next sibling parent =?ISO-8859-1?Q?Pelle_M=E5nsson?= <pelle.mansson gmail.com> writes:
On 03/10/2010 04:23 PM, bearophile wrote:
 Andrei Alexandrescu:
 The idea is sensible and is already in effect for the ".length" property
 of arrays.

I didn't know that. So I have tried this code: void main() { int[] a; a.length++; a.length--; } The compiler shows the following errors, is this correct? test1.d(3): Error: a.length is not an lvalue test1.d(4): Error: a.length is not an lvalue So I have tried this, and this compiles: void main() { int[] a; ++a.length; --a.length; } Bye, bearophile

May we wish for a better error message in this case? Anyway, I have always used += 1.
Mar 10 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 09:23 AM, bearophile wrote:
 Andrei Alexandrescu:
 The idea is sensible and is already in effect for the ".length" property
 of arrays.

I didn't know that. So I have tried this code: void main() { int[] a; a.length++; a.length--; } The compiler shows the following errors, is this correct? test1.d(3): Error: a.length is not an lvalue test1.d(4): Error: a.length is not an lvalue So I have tried this, and this compiles: void main() { int[] a; ++a.length; --a.length; } Bye, bearophile

That's a bug worth submitting. Andrei
Mar 10 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Andrei Alexandrescu:
 That's a bug worth submitting.

OK: http://d.puremagic.com/issues/show_bug.cgi?id=3927 (I have not started fixing bugs) Bye, bearophile
Mar 10 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 12:14 PM, bearophile wrote:
 Andrei Alexandrescu:
 That's a bug worth submitting.

OK: http://d.puremagic.com/issues/show_bug.cgi?id=3927 (I have not started fixing bugs) Bye, bearophile

BTW I noticed that you have recently submitted a great deal of bugs and good suggestions to bugzilla. Thanks! Andrei
Mar 13 2010
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 08:42 AM, Andrei Alexandrescu wrote:
 On 03/09/2010 09:48 PM, Chad J wrote:
 I speak of the property rewriting where an expression like

 foo.prop++;

 is rewritten as

 auto t = foo.prop();
 t++;
 foo.prop(t);

This particular example has a number of issues. First off you need to rewrite expressions, not statements. Consider: auto x = foo.prop++; You'd need to assign to x the old value of foo.prop. So one correct rewrite is foo.prop++ into {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}() within an rvalue context, and into: {auto t = foo.prop; ++t; foo.prop = t; return t;}() within a void context.

The latter should be: {auto t = foo.prop; ++t; foo.prop = t;}() because there's no need to return a value. Andrei
Mar 10 2010
parent reply Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/10/2010 10:48 AM, Andrei Alexandrescu wrote:
 On 03/10/2010 08:42 AM, Andrei Alexandrescu wrote:
 {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}()

 within an rvalue context, and into:

 {auto t = foo.prop; ++t; foo.prop = t; return t;}()

 within a void context.

The latter should be: {auto t = foo.prop; ++t; foo.prop = t;}() because there's no need to return a value. Andrei

no auto a = foo.prop++; ?
Mar 10 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 11:05 AM, Ellery Newcomer wrote:
 On 03/10/2010 10:48 AM, Andrei Alexandrescu wrote:
 On 03/10/2010 08:42 AM, Andrei Alexandrescu wrote:
 {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}()

 within an rvalue context, and into:

 {auto t = foo.prop; ++t; foo.prop = t; return t;}()

 within a void context.

The latter should be: {auto t = foo.prop; ++t; foo.prop = t;}() because there's no need to return a value. Andrei

no auto a = foo.prop++; ?

Not sure I understand the question. The statement you mention would end up lowered to: auto a = {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}(); which does what the user would expect. (Lowering is conceptual, e.g. inline code or an intrinsic named function could be used.) Andrei
Mar 10 2010
parent Ellery Newcomer <ellery-newcomer utulsa.edu> writes:
On 03/10/2010 11:10 AM, Andrei Alexandrescu wrote:
 On 03/10/2010 11:05 AM, Ellery Newcomer wrote:
 On 03/10/2010 10:48 AM, Andrei Alexandrescu wrote:
 On 03/10/2010 08:42 AM, Andrei Alexandrescu wrote:
 {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}()

 within an rvalue context, and into:

 {auto t = foo.prop; ++t; foo.prop = t; return t;}()

 within a void context.

The latter should be: {auto t = foo.prop; ++t; foo.prop = t;}() because there's no need to return a value. Andrei

no auto a = foo.prop++; ?

Not sure I understand the question. The statement you mention would end up lowered to: auto a = {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}(); which does what the user would expect. (Lowering is conceptual, e.g. inline code or an intrinsic named function could be used.) Andrei

oop. nevermind. missed the void context part.
Mar 10 2010
prev sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Andrei Alexandrescu wrote:
 On 03/09/2010 09:48 PM, Chad J wrote:
 I speak of the property rewriting where an expression like

      foo.prop++;

 is rewritten as

      auto t = foo.prop();
      t++;
      foo.prop(t);

This particular example has a number of issues. First off you need to rewrite expressions, not statements. Consider: auto x = foo.prop++; You'd need to assign to x the old value of foo.prop. So one correct rewrite is foo.prop++ into {auto t = foo.prop; auto t1 = t; ++t1; foo.prop = t1; return t;}() within an rvalue context, and into: {auto t = foo.prop; ++t; foo.prop = t; return t;}() within a void context. I'm pointing out that things may not always be very simple, but generally it's easy to figure out the proper rewrites if attention is given to detail.

Right. This one made itself easy to notice because if you either return a value in a void context (ex: expression statements) or fail to return in a non-void context (ex: conditions for if/for/while statements and the like) then further execution of semantic analysis will error. What I end up doing is generating a bunch of comma expressions that hold the rewritten property expression. So my rewrite for "auto x = foo.prop++;" actually looks like this: auto x = (auto t = foo.prop, (auto t1 = t++, (foo.prop = t, t1))); It's illegal D code, but only because of a check (in Expression->semantic() somewhere IIRC) that prevents declaration expressions from appearing in arbitrary places. Once you're past that check you can put them there and the backend knows what to do with them. I stick t1 in there at the end to make the comma expression evaluate to the value of t1 at the end of the calculations. If it's a void context, I don't stick t1 in there at the end, because if I did then it would complain about having no side-effects.
 So, Walter or Andrei or someone on the planning behind the scenes,
 please lend me your thoughts:
 How much time is left to make this sort of thing happen?
 If a working patch for this showed up, would it have a reasonable chance
 of acceptance at this point?

The idea is sensible and is already in effect for the ".length" property of arrays.
 I really want to make this happen, even if I have to pay someone to do
 it or finish it off.  It's very close but I have almost nil free time
 for this stuff.

 Note that I have made it work and seen it in action.  There'd be a patch
 two months ago if I hadn't decided to rebel against the way DMD did
 things*.

Probably offering payment wouldn't be much of an enticement, but lobbying reasonable ideas here is a good way to go.

I figure it might give an edge of motivation, especially to some of the talented college students around here. I would have probably done this kind of thing in college if the opportunity had popped up. I think Spring break is about here too.
 ...

 - Having property rewrites allows the special case for "array.length +=
 foo;" to be removed.  Property rewriting is the more general solution
 that will work for all properties and in arbitrary expressions.

Agreed. By the way, I'm a huge fan of lowering; I think they are great for defining semantics in a principled way without a large language core. In recent times Walter has increasingly relied on lowerings and mentioned to me that the code savings in the compiler have been considerable.

Interesting.
 - By treating opIndex and opIndexAssign as properties then that pair
 alone will make cases like "a[i]++;" work correctly without the need for
 opIndexUnary overloads.  Also "a[i] += foo" will work too, as well as
 anything else you haven't thought of yet.

Well operator overloading handles indexing differently, and arguably better than in your proposal. Ideally we'd define operators on properties in a manner similar to the way indexing works in the new operator overloading scheme. I'll talk to Walter about that. Andrei

I wouldn't want to have to define functions for side-effectful operators /in addition/ to the getter and setter. The opIndexUnary/ opIndexOpAssign things have bugged me a bit because I've felt that the value returned from opIndex should handle its own operator overloads. I wonder if we are talking about two different things. The extra opIndexUnary/opIndexOpAssign overloads could supersede the behavior of getting from opIndex, mutating a temporary, and calling opIndexAssign with the temporary. I'd still like to not /need/ to define the extra operator overloads though. Indexing seems to be the general case of properties: an indexed expression can be a getter/setter pair identified by both an identifier (the property's name: opIndex in this case) and some runtime variables (the indices). The properties are a getter/setter pair identified by only the property's name alone. This isn't much harder to deal with: foo[i]++; -> {auto t = foo.opIndex(i); t++; foo.opIndex(i,t) }() Now if the index itself has side effects, then that expression must be removed: foo[i++]++; -> {auto t = foo.opIndex(i); t++; foo.opIndexAssign(i,t) i++; }() // i++ is removed from the indexing expression. I think I've managed to successfully deal with that. I've also given thought to the notion of side-effects within side-effects, and I make sure those are safely removed so that things don't get executed twice or more in an unexpected manner. And... I also handled out and ref parameters in function calls. A property found used as a ref argument is extracted from the call and replaced with a temporary that is get and set. I feel that out parameters are similar to assignment, so a property found as an out argument will only have its setter called. I just need to get the blasted thing to mesh with dmd's manner of travelling the AST ;)
Mar 10 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/10/2010 09:14 PM, Chad J wrote:
 Andrei Alexandrescu wrote:
 Well operator overloading handles indexing differently, and arguably
 better than in your proposal. Ideally we'd define operators on
 properties in a manner similar to the way indexing works in the new
 operator overloading scheme. I'll talk to Walter about that.


 Andrei

I wouldn't want to have to define functions for side-effectful operators /in addition/ to the getter and setter. The opIndexUnary/ opIndexOpAssign things have bugged me a bit because I've felt that the value returned from opIndex should handle its own operator overloads. I wonder if we are talking about two different things.

I hear you.
 The extra opIndexUnary/opIndexOpAssign overloads could supersede the
 behavior of getting from opIndex, mutating a temporary, and calling
 opIndexAssign with the temporary.  I'd still like to not /need/ to
 define the extra operator overloads though.

Yah, ideally both options should be available.
 Indexing seems to be the general case of properties: an indexed
 expression can be a getter/setter pair identified by both an identifier
 (the property's name: opIndex in this case) and some runtime variables
 (the indices).  The properties are a getter/setter pair identified by
 only the property's name alone.  This isn't much harder to deal with:

      foo[i]++;

 ->

      {auto t = foo.opIndex(i);
       t++;
       foo.opIndex(i,t) }()

I considered and rejected that design because it has a number of important practical drawbacks, such as unsuitability for certain containers (hashes, sparse vectors) and inefficiency. Andrei
Mar 11 2010
parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
Andrei Alexandrescu wrote:
 On 03/10/2010 09:14 PM, Chad J wrote:
 ...
 Indexing seems to be the general case of properties: an indexed
 expression can be a getter/setter pair identified by both an identifier
 (the property's name: opIndex in this case) and some runtime variables
 (the indices).  The properties are a getter/setter pair identified by
 only the property's name alone.  This isn't much harder to deal with:

      foo[i]++;

 ->

      {auto t = foo.opIndex(i);
       t++;
       foo.opIndex(i,t) }()

I considered and rejected that design because it has a number of important practical drawbacks, such as unsuitability for certain containers (hashes, sparse vectors) and inefficiency. Andrei

Oh. What would those drawbacks be? Note that I made a typo and wrote opIndex instead of opIndexAssign. I really meant
      {auto t = foo.opIndex(i);
       t++;
       foo.opIndexAssign(i,t) }()


through indexing ops and not the overloading of opIndex to be a setter as well as a getter. Correct me if I'm wrong. - Chad
Mar 11 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 09 Mar 2010 22:48:08 -0500, Chad J  
<chadjoan __spam.is.bad__gmail.com> wrote:

 I speak of the property rewriting where an expression like

     foo.prop++;

 is rewritten as

     auto t = foo.prop();
     t++;
     foo.prop(t);

I think this is fine as long as we don't take it to the extreme. That is, I don't want to see this happening: foo.prop1.prop2++; is rewritten to auto p1 = foo.prop1; auto p2 = p1.prop2; p2++; p1.prop2 = p2; foo.prop1 = p1; I think one level of lowering is enough to handle the most common cases. Of course, if a property returns an lvalue, then it should just work. -Steve
Mar 10 2010
parent reply =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
On 03/10/2010 10:14 PM, Steven Schveighoffer wrote:
 I think this is fine as long as we don't take it to the extreme. That
 is, I don't want to see this happening:

 foo.prop1.prop2++;

 is rewritten to

 auto p1 = foo.prop1;
 auto p2 = p1.prop2;
 p2++;
 p1.prop2 = p2;
 foo.prop1 = p1;

 I think one level of lowering is enough to handle the most common cases.

 Of course, if a property returns an lvalue, then it should just work.

 -Steve

Why would you not want that? That's exactly what should happen! Why not? I'm sorry if I'm missing something obvious.
Mar 10 2010
parent =?UTF-8?B?UGVsbGUgTcOlbnNzb24=?= <pelle.mansson gmail.com> writes:
On 03/11/2010 09:37 PM, Steven Schveighoffer wrote:
 BTW, C# doesn't do this:
 -Steve

I still think D should, it makes little sense not to. What is gained from limiting arbitrarily?
Mar 12 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 10 Mar 2010 17:16:11 -0500, Pelle Månsson  
<pelle.mansson gmail.com> wrote:

 On 03/10/2010 10:14 PM, Steven Schveighoffer wrote:
 I think this is fine as long as we don't take it to the extreme. That
 is, I don't want to see this happening:

 foo.prop1.prop2++;

 is rewritten to

 auto p1 = foo.prop1;
 auto p2 = p1.prop2;
 p2++;
 p1.prop2 = p2;
 foo.prop1 = p1;

 I think one level of lowering is enough to handle the most common cases.

 Of course, if a property returns an lvalue, then it should just work.

 -Steve

Why would you not want that? That's exactly what should happen! Why not? I'm sorry if I'm missing something obvious.

Hm... on second thought you are right. I meant to say something like this: foo.prop1.prop2.bar(); should not be rewritten in a similar fashion. ++ or op= in most cases should change the value of the property, but arbitrary functions are not as obvious. The other thing I don't like about this is it is difficult for the compiler to determine what is considered an rvalue or lvalue. Consider something like this: struct S { int x; int *y; ref S opAssignOpBinary(string op)(int other) { mixin("*y " ~ op ~ " other"); return this; } } I believe the compiler considers S an rvalue, but the operator for += will work as if it was an lvalue. To do that whole property shell-game for this will be a complete waste. It makes things like smart pointers perform way worse than they should. What may be a good-enough rule is that the property rewriting is done only if the end property (the one being affected) is a pure value type, or a builtin and the compiler can tell that the operation only affects that property. Without full code inspection, the "right" solution can't be had. -Steve
Mar 11 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 10 Mar 2010 17:16:11 -0500, Pelle Månsson  
<pelle.mansson gmail.com> wrote:

 On 03/10/2010 10:14 PM, Steven Schveighoffer wrote:
 I think this is fine as long as we don't take it to the extreme. That
 is, I don't want to see this happening:

 foo.prop1.prop2++;

 is rewritten to

 auto p1 = foo.prop1;
 auto p2 = p1.prop2;
 p2++;
 p1.prop2 = p2;
 foo.prop1 = p1;

 I think one level of lowering is enough to handle the most common cases.

 Of course, if a property returns an lvalue, then it should just work.

 -Steve

Why would you not want that? That's exactly what should happen! Why not? I'm sorry if I'm missing something obvious.

BTW, C# doesn't do this: struct C { private int _x; public int x { get { return _x; } set { _x = value; } } } struct D { private C _c; public C c { get { return _c; } set { _c = value; } } } class X { static void Main() { D d = new D(); d.c.x += 5; } } testme.cs(38,11): error CS1612: Cannot modify a value type return value of `D.c'. Consider storing the value in a temporary variable testme.cs(1,8): (Location of the symbol related to previous error) Compilation failed: 1 error(s), 0 warnings -Steve
Mar 11 2010
prev sibling next sibling parent retard <re tard.com.invalid> writes:
Thu, 11 Mar 2010 15:37:59 -0500, Steven Schveighoffer wrote:

 On Wed, 10 Mar 2010 17:16:11 -0500, Pelle Månsson
 <pelle.mansson gmail.com> wrote:
 
 On 03/10/2010 10:14 PM, Steven Schveighoffer wrote:
 I think this is fine as long as we don't take it to the extreme. That
 is, I don't want to see this happening:

 foo.prop1.prop2++;

 is rewritten to

 auto p1 = foo.prop1;
 auto p2 = p1.prop2;
 p2++;
 p1.prop2 = p2;
 foo.prop1 = p1;

 I think one level of lowering is enough to handle the most common
 cases.

 Of course, if a property returns an lvalue, then it should just work.

 -Steve

Why would you not want that? That's exactly what should happen! Why not? I'm sorry if I'm missing something obvious.

BTW, C# doesn't do this: struct C { private int _x; public int x { get { return _x; } set { _x = value; } } } struct D { private C _c; public C c { get { return _c; } set { _c = value; } } } class X { static void Main() { D d = new D(); d.c.x += 5; } }

I hope that cannot be used as an argument against the feature. Scala supports chained properties merrily just like it should. Sorry for posting, I'll try to avoid that in the future.
Mar 11 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 12 Mar 2010 04:06:27 -0500, Pelle Månsson  
<pelle.mansson gmail.com> wrote:

 On 03/11/2010 09:37 PM, Steven Schveighoffer wrote:
 BTW, C# doesn't do this:
 -Steve

I still think D should, it makes little sense not to. What is gained from limiting arbitrarily?

It has been pointed out that C#'s properties are "right" after 4 years of development. I was just pointing out that C# doesn't agree with what has been proposed. I am undecided whether I like the idea of chained properties performing this trick. As long as we can guarantee that it truly is the correct choice on the compiler's part, I have no problem. This may mean disallowing compilation a la C# in certain cases where the compiler is unsure. You can always workaround by writing the steps manually (as the C# error message suggests). -Steve
Mar 12 2010