www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP23 Counter Proposal

reply Timon Gehr <timon.gehr gmx.ch> writes:
As my posts in the DIP23 thread have been left unanswered, I have 
prepared a counter proposal for DIP23 during the last hour.

Everything DIP23 addresses is specified in the two short sub-sections 
"Optional parens" and " property: basic design".

Those in favour of what was called "semantic rewrites" in the DIP23 
thread should probably read on further.

All parts of this proposal are independent of DIP24 (which Andrei is 
preparing).

http://wiki.dlang.org/DIP23_Counter_Proposal

There are almost no examples yet, but in case it is not clear how some 
case would be handled, feel free to ask.


(Also feel free to fix the formatting.)
Feb 05 2013
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 05 Feb 2013 19:39:55 -0500, Timon Gehr <timon.gehr gmx.ch> wrote:

 As my posts in the DIP23 thread have been left unanswered, I have  
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections  
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23  
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is  
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some  
 case would be handled, feel free to ask.


 (Also feel free to fix the formatting.)
Has my vote. For what it's worth :) One thing that should be clarified, you should explicitly say "member function (static or instance)" instead of just member function. The "optional this" kind of takes care of it, but you have to read it carefully to get that. I think it should be more straightforward. -Steve
Feb 05 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 02:20 AM, Steven Schveighoffer wrote:
 On Tue, 05 Feb 2013 19:39:55 -0500, Timon Gehr <timon.gehr gmx.ch> wrote:

 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.


 (Also feel free to fix the formatting.)
Has my vote. For what it's worth :)
Thanks! :) The full proposal or just the basic design part? (I think the full "semantic rewrite" idea may have some issues regarding excessive postblit/destruction, so I am not entirely sure if it is a good idea, but it was requested.)
 One thing that should be clarified, you should explicitly say "member
 function (static or instance)" instead of just member function.  The
 "optional this" kind of takes care of it, but you have to read it
 carefully to get that.  I think it should be more straightforward.

 -Steve
Done. Another thing that was not specified yet was what the compiler is supposed to do when it encounters overloads where some are property and some are not. (I have added "It is illegal to overload property-qualified functions against non- property-qualified functions.")
Feb 05 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 05 Feb 2013 20:34:11 -0500, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 02/06/2013 02:20 AM, Steven Schveighoffer wrote:
 On Tue, 05 Feb 2013 19:39:55 -0500, Timon Gehr <timon.gehr gmx.ch>  
 wrote:

 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.


 (Also feel free to fix the formatting.)
Has my vote. For what it's worth :)
Thanks! :) The full proposal or just the basic design part? (I think the full "semantic rewrite" idea may have some issues regarding excessive postblit/destruction, so I am not entirely sure if it is a good idea, but it was requested.)
All that I really care about is the specification of what property does. The semantic rewrite stuff is something I don't feel strongly either way. It would be nice, but at the same time, it doesn't feel necessary to me. One can always simply avoid it by not using the operators on properties.
 One thing that should be clarified, you should explicitly say "member
 function (static or instance)" instead of just member function.  The
 "optional this" kind of takes care of it, but you have to read it
 carefully to get that.  I think it should be more straightforward.

 -Steve
Done. Another thing that was not specified yet was what the compiler is supposed to do when it encounters overloads where some are property and some are not. (I have added "It is illegal to overload property-qualified functions against non- property-qualified functions.")
Sounds correct. I was reexamining the portion on the __traits rewrite. I think it is interesting how you have simply made property rewrite to __traits(propertyAccessors, x). There are a couple of clarifications I was curious about: 1. Does &prop work? If so, what does it do? 1a. if &prop doesn't work, does &__traits(propertyAccessors, prop) work (and give you the delegate to the property function overload set)? I can only imagine this is the reason you have the rewrite to a __traits call... 2. Inside the rules for when you do prop = exp, you specify that if the return is void, the expression is rewritten as (__traits(propertyAccessors, prop)(exp), prop). I see some issues with this: 2a. If you are NOT using the expression beyond the assignment, why call the accessor again? 2b. This would preclude write-only properties (as the second element of the comma expression is invalid). Is that intentional? 2c. What about ref returning properties? In those cases, the rewrite should be __traits(propertyAccessors, prop)() = exp; -Steve
Feb 05 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 03:45 AM, Steven Schveighoffer wrote:
 ...

 There are a couple of clarifications I was curious about:

 1. Does &prop work?  If so, what does it do?
It attempts to take the address of the result of the getter call.
 1a. if &prop doesn't work, does &__traits(propertyAccessors, prop) work
 (and give you the delegate to the property function overload set)?  I
 can only imagine this is the reason you have the rewrite to a __traits
 call...
Yes, exactly, &__traits(propertyAccessors, prop) gives delegate/function pointer access to the property function overload set.
 2. Inside the rules for when you do prop = exp, you specify that if the
 return is void, the expression is rewritten as
 (__traits(propertyAccessors, prop)(exp), prop).  I see some issues with
 this:
     2a. If you are NOT using the expression beyond the assignment, why
 call the accessor again?
     2b. This would preclude write-only properties (as the second element
 of the comma expression is invalid).  Is that intentional?
Not really. Good catch. Refined.
     2c. What about ref returning properties?  In those cases, the
 rewrite should be __traits(propertyAccessors, prop)() = exp;
I had forgotten to specify this. (The setter rewrite should be performed only if it makes sense.) I also had forgotten to treat alias declarations and template alias parameters. This is fixed now as well.
Feb 05 2013
prev sibling parent reply Chad Joan <chadjoan gmail.com> writes:
On 02/05/2013 09:45 PM, Steven Schveighoffer wrote:
 ...

 The semantic rewrite stuff is something I don't feel strongly either
 way. It would be nice, but at the same time, it doesn't feel necessary
 to me. One can always simply avoid it by not using the operators on
 properties.
A core feature of properties is that variables can be migrated to them (and back) without breaking any of the caller's code. Forbidding more than half of D's operators on them will definitely break this attribute.
Feb 06 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 06 Feb 2013 23:26:03 -0500, Chad Joan <chadjoan gmail.com> wrote:

 On 02/05/2013 09:45 PM, Steven Schveighoffer wrote:
 ...

 The semantic rewrite stuff is something I don't feel strongly either
 way. It would be nice, but at the same time, it doesn't feel necessary
 to me. One can always simply avoid it by not using the operators on
 properties.
A core feature of properties is that variables can be migrated to them (and back) without breaking any of the caller's code. Forbidding more than half of D's operators on them will definitely break this attribute.
I find this explanation lacking. A field supports operations that are impossible to implement with properties, there will never be a non-code-breaking change from a field to a property. So it's a matter of "how much" breakage you want to allow. This is really a judgment call, there is no "right" answer. The closest you can get to a field is to ref an actual field. But in that case, I don't see the point of the property, since you give unfettered access to the field! The whole point of having properties that I see is to implement things you CAN'T implement with a field, or are more cumbersome. Like read-only fields. Such a change would necessarily and on purpose break code. -Steve
Feb 07 2013
next sibling parent "Regan Heath" <regan netmail.co.nz> writes:
On Thu, 07 Feb 2013 15:02:41 -0000, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 On Wed, 06 Feb 2013 23:26:03 -0500, Chad Joan <chadjoan gmail.com> wrote:

 On 02/05/2013 09:45 PM, Steven Schveighoffer wrote:
 ...

 The semantic rewrite stuff is something I don't feel strongly either
 way. It would be nice, but at the same time, it doesn't feel necessary
 to me. One can always simply avoid it by not using the operators on
 properties.
A core feature of properties is that variables can be migrated to them (and back) without breaking any of the caller's code. Forbidding more than half of D's operators on them will definitely break this attribute.
I find this explanation lacking. A field supports operations that are impossible to implement with properties, there will never be a non-code-breaking change from a field to a property. So it's a matter of "how much" breakage you want to allow. This is really a judgment call, there is no "right" answer. The closest you can get to a field is to ref an actual field. But in that case, I don't see the point of the property, since you give unfettered access to the field! The whole point of having properties that I see is to implement things you CAN'T implement with a field, or are more cumbersome. Like read-only fields. Such a change would necessarily and on purpose break code.
There are several "points" to properties it would seem: http://en.wikipedia.org/wiki/Property_(programming) I have heard the goal Chad mentions said numerous times in similar discussions (to allow migration of fields to properties and back without breaking caller code) and I imagine this would be true and work in all cases for languages which don't have pointers, perhaps. But as D has pointers there can and probably does exist caller code which takes the address of a public field and would therefore simply break if that field was changed into a "property" (AKA method call). Now, perhaps there is some magic the compiler could do to make it work again, but if to achieve this we really have to jump through some crazy hoops or add complexity and edge-cases to the language then it's just not worth it, IMO. And, as Steven has said implementing read-only fields and adding layers of functionality are 2 other goals/points of properties and these are certainly useful by themselves, with or without any other benefits. R -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Feb 07 2013
prev sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, February 07, 2013 10:02:41 Steven Schveighoffer wrote:
 On Wed, 06 Feb 2013 23:26:03 -0500, Chad Joan <chadjoan gmail.com> wrote:
 On 02/05/2013 09:45 PM, Steven Schveighoffer wrote:
 ...
 
 The semantic rewrite stuff is something I don't feel strongly either
 way. It would be nice, but at the same time, it doesn't feel necessary
 to me. One can always simply avoid it by not using the operators on
 properties.
A core feature of properties is that variables can be migrated to them (and back) without breaking any of the caller's code. Forbidding more than half of D's operators on them will definitely break this attribute.
I find this explanation lacking. A field supports operations that are impossible to implement with properties, there will never be a non-code-breaking change from a field to a property. So it's a matter of "how much" breakage you want to allow. This is really a judgment call, there is no "right" answer. The closest you can get to a field is to ref an actual field. But in that case, I don't see the point of the property, since you give unfettered access to the field! The whole point of having properties that I see is to implement things you CAN'T implement with a field, or are more cumbersome. Like read-only fields. Such a change would necessarily and on purpose break code.
It works as long as you can limit the variable in some way. But I believe that the main purpose in using public variables rather than actual property functions when all they would do is get or set the variable is to avoid lots of boilerplate code. It's incredibly common to get stuff like struct S { int i() safe const pure nothrow { return _i; } int i(int value) safe pure nothrow { return _i = value; } private int _i; } That's a lot of extra code whose only benefit is encapsulation. So, it would be great if you could just do struct S { int i; } And with the ability to replace the variable with a property function later if it ever needs to do more than just get or set makes it so that encapsulation isn't a problem - except for the fact that there are certain operations that you can do on a variable that you can't do on a property function (e.g. taking its address or passing it by ref). Simply making it so that doing struct S { property int i; } lowered into something like the first example would allow you to avoid all of that boilerplate code and still have full encapsulation. It's definitely true that you can never make a property function completely emulate a variable, but all of the boilerplate required for simple getters and setters is kind of ridiculous. I believe that avoiding that is the primary reason that people want to be able to swap between variables and property functions, and with a small addition to the language, we can obviate that boilerplate. - Jonathan M Davis
Feb 07 2013
next sibling parent reply "Dan" <dbdavidson yahoo.com> writes:
On Thursday, 7 February 2013 at 20:26:10 UTC, Jonathan M Davis 
wrote:
 It works as long as you can limit the variable in some way. But 
 I believe that
 the main purpose in using public variables rather than actual 
 property
 functions when all they would do is get or set the variable is 
 to avoid lots
 of boilerplate code. It's incredibly common to get stuff like

 struct S
 {
  int i()  safe const pure nothrow
  { return _i; }

  int i(int value)  safe pure nothrow
  { return _i = value; }

  private int _i;
 }

 That's a lot of extra code whose only benefit is encapsulation. 
 So, it would be
 great if you could just do

 struct S
 {
  int i;
 }

 And with the ability to replace the variable with a property 
 function later if
 it ever needs to do more than just get or set makes it so that 
 encapsulation
 isn't a problem - except for the fact that there are certain 
 operations that
 you can do on a variable that you can't do on a property 
 function (e.g. taking
 its address or passing it by ref). Simply making it so that 
 doing

 struct S
 {
   property int i;
 }

 lowered into something like the first example would allow you 
 to avoid all of
 that boilerplate code and still have full encapsulation.
If it lowered into something like the first example would there really be any encapsulation? Wouldn't that be exactly the same as public int i? If this is the goal - to get rid of the boilerplate of accessors, it makes sense but a new feature is not necessary. If you want read/write access to a member but the benefit of being able to intercept access in the future start with struct S { int i; }. When the need arises to change the behavior, switch to: struct S { int i() safe const pure nothrow { // SPECIAL SAUCE return _i; } int i(int value) safe pure nothrow { // SPECIAL SAUCE return _i = value; } private int _i; } If you don't have any SPECIAL SAUCE then you don't need to switch. If you want more encapsulation and specifically don't want to provide write access, just read access then a mixin can do the trick of providing read access to s.i. In the future you can change the read accessor and provide one with SPECIAL SAUCE. struct S { mixin ReadOnly!_i; private int _i; }
 It's definitely true that you can never make a property 
 function completely
 emulate a variable, but all of the boilerplate required for 
 simple getters and
 setters is kind of ridiculous. I believe that avoiding that is 
 the primary
 reason that people want to be able to swap between variables 
 and property
 functions, and with a small addition to the language, we can 
 obviate that
 boilerplate.
No addition needed. Thanks Dan
Feb 07 2013
parent reply Robert <jfanatiker gmx.at> writes:
 struct S
 {
   property int i;
 }
You missed the point. When this gets lowered to accessor functions and a private variable, you ensure that you can later on add your magic soup, without breaking code that relied on i being a real field. (E.g. taking the address, using +=, -=, ...) Although += or -= can still be added for properties, but the important thing is, that for an property declared field nothing is possible, that is not also possible with a property created by manually writing set/get methods.
Feb 07 2013
parent reply "Dan" <dbdavidson yahoo.com> writes:
On Thursday, 7 February 2013 at 21:05:49 UTC, Robert wrote:
 You missed the point. When this gets lowered to accessor 
 functions and a
 private variable, you ensure that you can later on add your 
 magic soup,
 without breaking code that relied on i being a real field. 
 (E.g. taking
 the address, using +=, -=, ...)
Quite likely I missed the point. Today I have: struct S { property int i; } Tomorrow I decide I need to track every time int i is read and written. How is that done? I assume that that sort of encapsulation is what we are after. Thanks Dan
Feb 07 2013
parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, February 07, 2013 22:19:53 Dan wrote:
 On Thursday, 7 February 2013 at 21:05:49 UTC, Robert wrote:
 You missed the point. When this gets lowered to accessor
 functions and a
 private variable, you ensure that you can later on add your
 magic soup,
 without breaking code that relied on i being a real field.
 (E.g. taking
 the address, using +=, -=, ...)
Quite likely I missed the point. Today I have: struct S { property int i; } Tomorrow I decide I need to track every time int i is read and written. How is that done? I assume that that sort of encapsulation is what we are after.
You rewrite it as explicit property functions. No code breaks, because it was really property functions all along. You just didn't have to type them. - Jonathan M Davis
Feb 07 2013
parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 8 February 2013 at 04:06:40 UTC, Jonathan M Davis 
wrote:
 On Thursday, February 07, 2013 22:19:53 Dan wrote:
 On Thursday, 7 February 2013 at 21:05:49 UTC, Robert wrote:
 You missed the point. When this gets lowered to accessor
 functions and a
 private variable, you ensure that you can later on add your
 magic soup,
 without breaking code that relied on i being a real field.
 (E.g. taking
 the address, using +=, -=, ...)
Quite likely I missed the point. Today I have: struct S { property int i; } Tomorrow I decide I need to track every time int i is read and written. How is that done? I assume that that sort of encapsulation is what we are after.
You rewrite it as explicit property functions. No code breaks, because it was really property functions all along. You just didn't have to type them.
That seems to me like one of the best proposed idea in many thread happening on the subject.
Feb 07 2013
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:


 struct S
 {
   property int i;
 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve. -Steve
Feb 07 2013
next sibling parent "Dan" <dbdavidson yahoo.com> writes:
On Thursday, 7 February 2013 at 21:12:35 UTC, Steven 
Schveighoffer wrote:
 struct S
 {
    mixin(boilerplateProperty("i"));
 }

 I don't see this as a difficult problem to solve.

 -Steve
+1
Feb 07 2013
prev sibling next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, February 07, 2013 16:12:34 Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis <jmdavisProg gmx.com>
 
 wrote:
 struct S
 {
 
  property int i;
 
 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve.
Except that mixins are generally unacceptable in APIs, because they don't end up in the documentation. That means that this works only if you don't care about documentation. So, it's almost useless. Putting property on there also looks better but isn't that big a deal. However, the lack of documentation _is_ a big deal. - Jonathan M Davis
Feb 07 2013
next sibling parent reply "Dan" <dbdavidson yahoo.com> writes:
On Thursday, 7 February 2013 at 21:55:12 UTC, Jonathan M Davis 
wrote:
 Except that mixins are generally unacceptable in APIs, because 
 they don't end
 up in the documentation. That means that this works only if you 
 don't care
 about documentation. So, it's almost useless. Putting  property 
 on there also
 looks better but isn't that big a deal. However, the lack of 
 documentation
 _is_ a big deal.
Again, why would you need to property for the case of a read/write pattern. Just make it public: struct S { // Best int ever int i; }, then switch to this as needed: struct S { < accessors > // Best int ever int _i; } when needed. This way you have documentation still in tact. If you want read accessor only from the start, go with: struct S { mixin ReadOnly!_i; // Best int ever int _i; } I can understand documenting a member - quite important. But documentation on a generated accessor is not necessary. And if you customize it you can document it. No extra feature needed. Thanks Dan
Feb 07 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, February 07, 2013 23:15:14 Dan wrote:
 On Thursday, 7 February 2013 at 21:55:12 UTC, Jonathan M Davis
 
 wrote:
 Except that mixins are generally unacceptable in APIs, because
 they don't end
 up in the documentation. That means that this works only if you
 don't care
 about documentation. So, it's almost useless. Putting  property
 on there also
 looks better but isn't that big a deal. However, the lack of
 documentation
 _is_ a big deal.
Again, why would you need to property for the case of a read/write pattern. Just make it public: struct S { // Best int ever int i; },
But if you do that, then code that uses S can do stuff like take the address of i or pass it by ref. That code will then break when you turn it into property functions later. That's unacceptable, meaning that in almost all cases, people just create getters and setters which do almost nothing. It's boilerplate that we shouldn't need. - Jonathan M Davis
Feb 07 2013
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Friday, 8 February 2013 at 05:49:39 UTC, Jonathan M Davis 
wrote:
 On Thursday, February 07, 2013 23:15:14 Dan wrote:
 On Thursday, 7 February 2013 at 21:55:12 UTC, Jonathan M Davis 
 wrote:
 Except that mixins are generally unacceptable in APIs, 
 because they don't end up in the documentation. That means 
 that this works only if you don't care about documentation. 
 So, it's almost useless. Putting  property on there also 
 looks better but isn't that big a deal. However, the lack of 
 documentation _is_ a big deal.
Again, why would you need to property for the case of a read/write pattern. Just make it public: struct S { // Best int ever int i; },
But if you do that, then code that uses S can do stuff like take the address of I or pass it by ref. That code will then break when you turn it into property functions later. That's unacceptable, meaning that in almost all cases, people just create getters and setters which do almost nothing. It's boilerplate that we shouldn't need.
I hope I'm not missing the point; But I see a big dilemma about the whole ref/not ref(anymore) issue. Then I begin thinking: If you don't Mark it as part of the public API, why should it matter? Unless it's just a storage for data (which you guarantee to be ref), or publicly noted for DDoc, then making use of a internal implementation is wrong (even if it isn't marked private). It may be similar to... struct S { int i; auto front() {return i;} /// enum empty = false; /// void popFront() {i++;} /// } //original void func(S input) { writeln(input.i); input.i++; } //turned into referring to a range void func(R)(R input) if (isRange!(R)) { //Initially built/tested with struct S and works //but forgot to update. Still works in his own code writeln(input.i); input.i++; } If on the other hand if 'i' was intended part of the API, you can rely on it being ref-able unless there's a major change in design. struct S { int i; /// iterator number, part of API ref auto front() {return i;} /// ... } Also if the programmer's code gets broke they can always avoid upgrading until later, and fix their code when they are ready for it. Most fixes will be minor (even if they show up dozens of times).
Feb 08 2013
parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, February 08, 2013 22:38:35 Era Scarecrow wrote:
 If on the other hand if 'i' was intended part of the API, you
 can rely on it being ref-able unless there's a major change in
 design.
 
 struct S {
 int i; /// iterator number, part of API
 ref auto front() {return i;} ///
 ...
 }
The idea is that it's supposed to be part of the public API and that you're supposed to be able to add code to it later which does additional checking or completely changes how its implemented internally. This is trivial to do with functions, because they provide proper encapsulation. You can change the implementation as much as you like without breaking the API. But many getter and setter functions just get and set a variable without doing anything else making them useless boilerplate beyond the fact that they provide encapsulation. So, what property functions are trying to do is allow you to declare it as a public variable first (avoiding the boilerplate) and then only turn into property functions (with the variable being private) if you need the getter or setter to do something extra or different later. The one hitch in that is the fact that there a few things that you can do with a public variable that you can't do with a property function - things that you _don't_ want anyone doing with the public variable (like taking its address or passing it by ref). So, lacking a way to mark a public variable as a property, choosing to start with a public variable makes it likely that you're going to break code when the variable is converted to a pair property functions later. On the other hand, if you can mark the variable as a property (in which case the compiler either makes it illegal to do anything with that variable that you couldn't do with property functions, or it just lowers the variable to a pair property functions, and the variable isn't actually public anymore), then you can avoid breaking code. But the idea is to do this in a way that's transparent to users of the struct or class. They shouldn't care how a property is implemented. We want the encapsulation even when it's declared as a public variable (because we're really just looking to avoid boilerplate code). it's what a lot of us would like to be able to do with D. - Jonathan M Davis
Feb 08 2013
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/7/13 4:55 PM, Jonathan M Davis wrote:
 On Thursday, February 07, 2013 16:12:34 Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis<jmdavisProg gmx.com>

 wrote:
 struct S
 {

  property int i;

 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve.
Except that mixins are generally unacceptable in APIs, because they don't end up in the documentation. That means that this works only if you don't care about documentation. So, it's almost useless. Putting property on there also looks better but isn't that big a deal. However, the lack of documentation _is_ a big deal.
Well there's the version(DDoc) ... etc. possibility. Andrei
Feb 07 2013
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Thursday, February 07, 2013 17:34:19 Andrei Alexandrescu wrote:
 On 2/7/13 4:55 PM, Jonathan M Davis wrote:
 On Thursday, February 07, 2013 16:12:34 Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis<jmdavisProg gmx.com>
 
 wrote:
 struct S
 {
 
  property int i;
 
 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve.
Except that mixins are generally unacceptable in APIs, because they don't end up in the documentation. That means that this works only if you don't care about documentation. So, it's almost useless. Putting property on there also looks better but isn't that big a deal. However, the lack of documentation _is_ a big deal.
Well there's the version(DDoc) ... etc. possibility.
Except then you're being forced to throw in a different sort of boilerplate code. version(D_Ddoc) { /// DDoc for prop int prop; } else { mixin(boilerplateProperty!int("prop")); } is enough extra that it's arguably worse than just declaring the property functions. If you could do /// DDoc for prop mixin(boilerplateProperty!int("prop")); then that would be okay (though it would be uglier than just putting property on it to do the same thing). But we can't do that, not and have it show up in the documentation. - Jonathan M Davis
Feb 07 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 07 Feb 2013 16:55:01 -0500, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Thursday, February 07, 2013 16:12:34 Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis  
 <jmdavisProg gmx.com>

 wrote:
 struct S
 {

  property int i;

 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve.
Except that mixins are generally unacceptable in APIs, because they don't end up in the documentation. That means that this works only if you don't care about documentation. So, it's almost useless. Putting property on there also looks better but isn't that big a deal. However, the lack of documentation _is_ a big deal.
True. But we can fix that just as easily (will ddoc currently parse mixin-generated docs? If not it should). -Steve
Feb 07 2013
prev sibling next sibling parent Robert <jfanatiker gmx.at> writes:
I am not completely against this, but it feels quite artificial to make
a builtin feature complete this way. If we implement the property stuff
completely via library methods, I would like it more, but if it is
documented properly, might not be too bad:

struct S {
	mixin(property(int, "i"));
}

On Thu, 2013-02-07 at 16:12 -0500, Steven Schveighoffer wrote:
 struct S
 {
     mixin(boilerplateProperty("i"));
 }
Feb 07 2013
prev sibling next sibling parent Robert <jfanatiker gmx.at> writes:
That is actually a good point.

On Thu, 2013-02-07 at 22:55 +0100, Jonathan M Davis wrote:
 Except that mixins are generally unacceptable in APIs, because they
 don't end 
 up in the documentation. That means that this works only if you don't
 care 
 about documentation. So, it's almost useless. Putting  property on
 there also 
 looks better but isn't that big a deal. However, the lack of
 documentation 
 _is_ a big deal.
Feb 07 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/7/13 4:12 PM, Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis
 <jmdavisProg gmx.com> wrote:


 struct S
 {
  property int i;
 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve. -Steve
I like that, too, with the note that various syntax highlighters etc. won't be able to see much about that entity. Andrei
Feb 07 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 07 Feb 2013 17:25:16 -0500, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 2/7/13 4:12 PM, Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis
 <jmdavisProg gmx.com> wrote:


 struct S
 {
  property int i;
 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve. -Steve
I like that, too, with the note that various syntax highlighters etc. won't be able to see much about that entity.
My syntax highlighter highlights string and writeln, even though they aren't language builtins. If we establish a standard mixin template, certainly editors can be made to highlight such a mixin, along with providing the expected code completion. -Steve
Feb 07 2013
parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 8 February 2013 at 00:33:47 UTC, Steven Schveighoffer 
wrote:
 On Thu, 07 Feb 2013 17:25:16 -0500, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:

 On 2/7/13 4:12 PM, Steven Schveighoffer wrote:
 On Thu, 07 Feb 2013 15:25:57 -0500, Jonathan M Davis
 <jmdavisProg gmx.com> wrote:


 struct S
 {
  property int i;
 }
struct S { mixin(boilerplateProperty("i")); } I don't see this as a difficult problem to solve. -Steve
I like that, too, with the note that various syntax highlighters etc. won't be able to see much about that entity.
My syntax highlighter highlights string and writeln, even though they aren't language builtins. If we establish a standard mixin template, certainly editors can be made to highlight such a mixin, along with providing the expected code completion.
Most does it when using the q string syntax.
Feb 07 2013
prev sibling parent "Geancarlo Rocha" <geancarlo.rocha gmail.com> writes:

of "initial encapsulation":

public int i { get; set; } //get and set can be modified with 
private etc

Then the getter and setter methods are automatically implemented.


I'm not closely following the discussions involved in the 
re-design of properties, but I disagree with the people that 
believe properties are glorified fields. IMO, they are 
essentially functions, accessed like fields, that can set and/or 
expose state. I'm also against 2-parametrized properties btw.

Example: define a interface for geometric figures with a "area" 
property and then implement it for square, circle, hexagon, 
triangle etc

On Thursday, 7 February 2013 at 20:26:10 UTC, Jonathan M Davis 
wrote:
 It works as long as you can limit the variable in some way. But 
 I believe that
 the main purpose in using public variables rather than actual 
 property
 functions when all they would do is get or set the variable is 
 to avoid lots
 of boilerplate code. It's incredibly common to get stuff like

 struct S
 {
  int i()  safe const pure nothrow
  { return _i; }

  int i(int value)  safe pure nothrow
  { return _i = value; }

  private int _i;
 }

 That's a lot of extra code whose only benefit is encapsulation. 
 So, it would be
 great if you could just do

 struct S
 {
  int i;
 }

 And with the ability to replace the variable with a property 
 function later if
 it ever needs to do more than just get or set makes it so that 
 encapsulation
 isn't a problem - except for the fact that there are certain 
 operations that
 you can do on a variable that you can't do on a property 
 function (e.g. taking
 its address or passing it by ref). Simply making it so that 
 doing

 struct S
 {
   property int i;
 }

 lowered into something like the first example would allow you 
 to avoid all of
 that boilerplate code and still have full encapsulation.

 It's definitely true that you can never make a property 
 function completely
 emulate a variable, but all of the boilerplate required for 
 simple getters and
 setters is kind of ridiculous. I believe that avoiding that is 
 the primary
 reason that people want to be able to swap between variables 
 and property
 functions, and with a small addition to the language, we can 
 obviate that
 boilerplate.

 - Jonathan M Davis
Feb 07 2013
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/5/13 7:39 PM, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.


 (Also feel free to fix the formatting.)
I salute the appeal to rigor and comprehensiveness. This is not a contest or a competition so I allowed myself to make this its own DIP: http://wiki.dlang.org/DIP24 Andrei
Feb 05 2013
next sibling parent reply "David Nadlinger" <see klickverbot.at> writes:
On Wednesday, 6 February 2013 at 01:37:32 UTC, Andrei 
Alexandrescu wrote:
 This is not a contest or a competition so I allowed myself to 
 make this its own DIP: http://wiki.dlang.org/DIP24
Converted the old page into a redirect and added it to the index (Timon, I hope you don't mind). David
Feb 05 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 02:42 AM, David Nadlinger wrote:
 On Wednesday, 6 February 2013 at 01:37:32 UTC, Andrei Alexandrescu wrote:
 This is not a contest or a competition so I allowed myself to make
 this its own DIP: http://wiki.dlang.org/DIP24
Converted the old page into a redirect and added it to the index (Timon, I hope you don't mind). David
Perfect, thanks!
Feb 05 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 02:37 AM, Andrei Alexandrescu wrote:
 On 2/5/13 7:39 PM, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.


 (Also feel free to fix the formatting.)
I salute the appeal to rigor and comprehensiveness.
Thanks!
 This is not a contest or a competition so I allowed myself to make this
 its own DIP: http://wiki.dlang.org/DIP24
Makes sense. (As the two proposals agree on a meaning for most expressions I was not sure if that was enough for a full-blown DIP.)
Feb 05 2013
prev sibling next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 6 February 2013 at 00:39:55 UTC, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I 
 have prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short 
 sub-sections "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the 
 DIP23 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which 
 Andrei is preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear 
 how some case would be handled, feel free to ask.
I have to spend more time on the property part. For the optional () part, I want to raise concern about point 2 (the others make sense). In all other points, foo is either a call to function foo or the function foo itself. In point 2, we introduce a 3rd meaning of foo, identical to weird stuff we have in C/C++ (as we obviously aren't taking the address either of the function or of its result when executed). This object is an error of C and C++ we must get rid of. Point 1, 4 and 5 make sense. Point 3 is nonsensial if we want to really be rigorous as : returnType foo(TemplateArgs)(args) {} is equivalent to template foo(TemplateArgs) { returnType foo(args) {} } In this case foo is a template, not a function. foo!args is the function hen eponymous trick is used.
Feb 05 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 04:31 AM, deadalnix wrote:
 On Wednesday, 6 February 2013 at 00:39:55 UTC, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.
I have to spend more time on the property part. For the optional () part, I want to raise concern about point 2 (the others make sense). In all other points, foo is either a call to function foo or the function foo itself. In point 2, we introduce a 3rd meaning of foo, identical to weird stuff we have in C/C++ (as we obviously aren't taking the address either of the function or of its result when executed). This object is an error of C and C++ we must get rid of.
1. This would break a lot of code. 2. How to get a first-class function value (function pointer, delegate) from a function name otherwise?
 Point 1, 4 and 5 make sense.

 Point 3 is nonsensial if we want to really be rigorous as :
 returnType foo(TemplateArgs)(args) {}

 is equivalent to

 template foo(TemplateArgs) {
      returnType foo(args) {}
 }

 In this case foo is a template,
Yes, a function template. Note that functions overload against function templates.
 not a function. foo!args is the function
 hen eponymous trick is used.
I think you misunderstand. If we have eg: void foo(){ return 2; } void foo(int x)(){ return 2; } then we want: static assert(foo!2==2); Without rule 3, the code would be rewritten to static assert(foo()!2==2); And this would fail.
Feb 05 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 6 February 2013 at 03:43:28 UTC, Timon Gehr wrote:
 For the optional () part, I want to raise concern about point 
 2 (the
 others make sense).

 In all other points, foo is either a call to function foo or 
 the
 function foo itself. In point 2, we introduce a 3rd meaning of 
 foo,
 identical to weird stuff we have in C/C++ (as we obviously 
 aren't taking
 the address either of the function or of its result when 
 executed). This
 object is an error of C and C++ we must get rid of.
1. This would break a lot of code. 2. How to get a first-class function value (function pointer, delegate) from a function name otherwise?
2. I see no point in not making foo a first class function value. Not having it does complicate things for no benefit. Any complication should be properly motivated. This does not imply that optional () should be removed, as it is a mostly orthogonal problems (as scala shows). 1. As of backward compat, & can be kept and follow the usual deprecation process.
 Point 1, 4 and 5 make sense.

 Point 3 is nonsensial if we want to really be rigorous as :
 returnType foo(TemplateArgs)(args) {}

 is equivalent to

 template foo(TemplateArgs) {
     returnType foo(args) {}
 }

 In this case foo is a template,
Yes, a function template. Note that functions overload against function templates.
 not a function. foo!args is the function
 hen eponymous trick is used.
I think you misunderstand. If we have eg: void foo(){ return 2; } void foo(int x)(){ return 2; } then we want: static assert(foo!2==2); Without rule 3, the code would be rewritten to static assert(foo()!2==2); And this would fail.
Well the thing that a function CANNOT be used before template parameter (so function will be removed from overload set and only template will remains).
Feb 05 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 06:12 AM, deadalnix wrote:
 On Wednesday, 6 February 2013 at 03:43:28 UTC, Timon Gehr wrote:
 For the optional () part, I want to raise concern about point 2 (the
 others make sense).

 In all other points, foo is either a call to function foo or the
 function foo itself. In point 2, we introduce a 3rd meaning of foo,
 identical to weird stuff we have in C/C++ (as we obviously aren't taking
 the address either of the function or of its result when executed). This
 object is an error of C and C++ we must get rid of.
1. This would break a lot of code. 2. How to get a first-class function value (function pointer, delegate) from a function name otherwise?
2. I see no point in not making foo a first class function value. Not having it does complicate things for no benefit. Any complication should be properly motivated. This does not imply that optional () should be removed, as it is a mostly orthogonal problems (as scala shows).
So you would prefer Scala-like syntax? Scala uses suffix _ for what we use prefix &. Without something like that, type deduction is crippled.
 1. As of backward compat, & can be kept and follow the usual
 deprecation process.
I think this language change is not part of the DIP that fixes optional parens and property, but a separate improvement.
 ...
 Point 3 is nonsensial if we want to really be rigorous as :
 returnType foo(TemplateArgs)(args) {}

 is equivalent to

 template foo(TemplateArgs) {
     returnType foo(args) {}
 }

 In this case foo is a template,
Yes, a function template. Note that functions overload against function templates.
 not a function. foo!args is the function
 hen eponymous trick is used.
I think you misunderstand. If we have eg: void foo(){ return 2; } void foo(int x)(){ return 2; } then we want: static assert(foo!2==2); Without rule 3, the code would be rewritten to static assert(foo()!2==2); And this would fail.
Well the thing that a function CANNOT be used before template parameter (so function will be removed from overload set and only template will remains).
foo _is_ the overload set. In order to decide whether to rewrite it to foo() or not, it is necessary to look at the context ("before template parameter"), which is what rule 3 states. My example is still valid if you make the first overload templated. The rewrites must happen before overload resolution. In fact, they may decide which overload is ultimately chosen. (You can be certain that my working implementation of optional parens would cease to work if I removed rule 3.)
Feb 06 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-02-06 04:43, Timon Gehr wrote:

 Yes, a function template. Note that functions overload against function
 templates.
Maybe I'm misunderstanding something there but since when? Example: void foo () {} void foo (T) (T t) {} void main () {} Error: template main.foo(T)(T t) conflicts with function main.foo at... -- /Jacob Carlborg
Feb 05 2013
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, February 06, 2013 08:34:32 Jacob Carlborg wrote:
 On 2013-02-06 04:43, Timon Gehr wrote:
 Yes, a function template. Note that functions overload against function
 templates.
Maybe I'm misunderstanding something there but since when? Example: void foo () {} void foo (T) (T t) {} void main () {} Error: template main.foo(T)(T t) conflicts with function main.foo at...
They're _supposed_ to, but it's a long-standing bug that they don't, and AFAIK, it has yet to be fixed. - Jonathan M Davis
Feb 05 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 6 February 2013 at 07:34:33 UTC, Jacob Carlborg 
wrote:
 On 2013-02-06 04:43, Timon Gehr wrote:

 Yes, a function template. Note that functions overload against 
 function
 templates.
Maybe I'm misunderstanding something there but since when? Example: void foo () {} void foo (T) (T t) {} void main () {} Error: template main.foo(T)(T t) conflicts with function main.foo at...
That is dmd's implementation, but not what the spec says.
Feb 06 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-02-06 09:07, deadalnix wrote:

 That is dmd's implementation, but not what the spec says.
I can't see that the spec says anything about this. It doesn't say it's legal neither illegal. -- /Jacob Carlborg
Feb 06 2013
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, February 06, 2013 09:42:49 Jacob Carlborg wrote:
 On 2013-02-06 09:07, deadalnix wrote:
 That is dmd's implementation, but not what the spec says.
I can't see that the spec says anything about this. It doesn't say it's legal neither illegal.
I don't know what the spec says (I'd have to go digging through it), but TDPL is quite clear on the matter. And it's still in bugzilla as a bug: http://d.puremagic.com/issues/show_bug.cgi?id=1528 I really don't think that there's any question that it's supposed to work. It's just that the implementation hasn't been fixed yet. - Jonathan M Davis
Feb 06 2013
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-02-06 01:39, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.


 (Also feel free to fix the formatting.)
+1 I would be nice with a couple of more examples. -- /Jacob Carlborg
Feb 05 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/5/13 7:39 PM, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.
So syntactic case (2) prescribes that when foo is in address-taken position "&foo", that means take its address as opposed to evaluate foo. That makes the use of "&" here sheer punctuation, as opposed to operator. There's also "(Note that redundant parentheses are assumed to be dropped in the parsing stage.)" It's unclear whether this makes &(foo) and &(((foo))) identical to &foo. If they don't, then "&" is not an operator in the expression "&foo" because an operator should be applicable to a parenthesized expression. If they do, in this context parens are also punctuation, not expression grouping; they don't enclose an expression, but instead define &(((...(foo)...))) as a sole syntactic unit (!). This also leads to potential confusion, seeing as &(foo) takes the address of foo, but &( true ? foo : bar ) does, in fact, take the address of whatever foo returns. This makes the role of "&", "(", and ")" in the proposal as both punctuation and operator painfully visible. This all makes DIP24 fail meet its own definition of success as far as I understand it, i.e. keeping "&" to mean operator and parens to mean grouping. In my opinion, it also makes DIP24 fail to improve over DIP23. DIP23 has in fact /fewer/ such problems, because it clarifies that &foo and &expr.foo are indivisible syntactic units; thus parentheses are not ascribed a special meaning by DIP23. On the contrary, as soon as parens are used, as in &(foo) or &( true ? foo : bar ), the usual meaning of parens enters in action and give the expression inside the usual meaning. I should point out that that's all okay, but we got to be clear about it. Tokens are used as both punctuation and operators all the time. For example, it may sound weird that (stuff) and ((stuff)) should ever mean distinct things, but writeln((1, 2)) does something different from writeln(1, 2). This is because the outer layer is punctuation and the inner layer is expression grouping. And nobody blinks an eye. The language just needs to be as clear as possible about which is which. If DIP25 gets approved, taking the address of ref results will be banned, and therefore that potential meaning of "&" disappears, which simplifies things. But we'll still have to live with the reality that in the constructs "&foo" and "&expr.foo", "&" is not an operator. And that's okay. Andrei
Feb 06 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 04:32 PM, Andrei Alexandrescu wrote:
 On 2/5/13 7:39 PM, Timon Gehr wrote:
 As my posts in the DIP23 thread have been left unanswered, I have
 prepared a counter proposal for DIP23 during the last hour.

 Everything DIP23 addresses is specified in the two short sub-sections
 "Optional parens" and " property: basic design".

 Those in favour of what was called "semantic rewrites" in the DIP23
 thread should probably read on further.

 All parts of this proposal are independent of DIP24 (which Andrei is
 preparing).

 http://wiki.dlang.org/DIP23_Counter_Proposal

 There are almost no examples yet, but in case it is not clear how some
 case would be handled, feel free to ask.
So syntactic case (2) prescribes that when foo is in address-taken position "&foo", that means take its address as opposed to evaluate foo. That makes the use of "&" here sheer punctuation, as opposed to operator.
Here and everywhere else. auto x = 5; auto y = &5; // error! auto z = &x; // ok!
 There's also "(Note that redundant parentheses are assumed to be dropped
 in the parsing stage.)" It's unclear whether this makes &(foo) and
 &(((foo))) identical to &foo. ...
That is exactly the case it describes. Yes it makes them identical.
 If they do, in this context parens are also
 punctuation, not expression grouping; they don't enclose an expression,
 but instead define &(((...(foo)...))) as a sole syntactic unit (!).
They are neither. They are just insignificant information thrown away by the parser.
 This also leads to potential confusion, seeing as &(foo) takes the
 address of foo, but &( true ? foo : bar ) does, in fact, take the
 address of whatever foo returns.
Ah, very good point! But please note that this is 100% a ternary operator issue. Interestingly, what you describe is how it actually works in Scala. I do not feel strongly about this, but we might as well do it the other way. I have clarified what it means to occur in a context. I think the ternary expression is the only composite expression that can occur in lvalue position that behaves this way. This change also 'fixes' the related case (true ? foo : bar)().
 This makes the role of "&", "(", and
 ")" in the proposal as both punctuation and operator painfully visible.
The syntax tree looks like this: & | ? / | \ / | \ true foo bar There are no "(" ")". Why would they affect semantics?
 This all makes DIP24 fail meet its own definition of success as far as I
 understand it, i.e. keeping "&" to mean operator and parens to mean
 grouping.
1. There is no point where the DIP calls "&" an "operator". 2. It succeeds in keeping parens mean grouping. (Given any of the two behaviours of ternary expressions.) DIP24 meets its own definition of success.
 In my opinion, it also makes DIP24 fail to improve over DIP23.
 ...
In my opinion too. It conveniently also makes all other statements true, as the line of reasoning crucially depends on fallaciously ascribing properties of the ternary operator to the parens enclosing it. (The second fallacy is ascribing the properties of rvalue vs. lvalue distinction inherited from C to DIP24.)
 DIP23 has in fact /fewer/ such problems, because it clarifies that &foo
 and &expr.foo are indivisible syntactic units; thus parentheses are not
 ascribed a special meaning by DIP23.
That is inaccurate, and it is special enough that I do not care to fully formalize the special meaning ascribed to parentheses by DIP23. Basically it makes use of parens affect whether some position in the syntax tree is treated as lvalue or rvalue position in some special cases.
 On the contrary, as soon as parens
 are used, as in &(foo) or &( true ? foo : bar ), the usual meaning
 of parens enters in action and give the expression inside the usual meaning.
You mean the usual rvalue position meaning, eliminating the lvalue position meaning, even if the expression actually occurs in an lvalue position. That is unusual.
 I should point out that that's all okay, but we got to be clear about
 it. Tokens are used as both punctuation and operators all the time. For
 example, it may sound weird that (stuff) and ((stuff)) should ever mean
 distinct things,
They never do and never should.
 but writeln((1, 2)) does something different from
 writeln(1, 2).
I guess this attempts to instantiate 'stuff' with "1, 2"? The substring "((1, 2))" in the first example does not correspond to a node in the syntax tree. What is the point?
 This is because the outer layer is punctuation and the
 inner layer is expression grouping.
One of the usages is unary and the other is binary. Another example of this is ~x and x~y. This also shows that the two usages do not have to be related in any way, so nothing is weird.
 And nobody blinks an eye.
Of course they do. This situation is one cause of the extensive built-in tuple discussions.
 The language just needs to be as clear as possible about which is which.

 If DIP25 gets approved, taking the address of ref results will be
 banned, and therefore that potential meaning of "&" disappears, which
 simplifies things.
How? It only adds code to my front-end to make sure the case is actually caught and properly reported. It does not even simplify the byte code compiler. (Because it uses similar code paths for the &foo() and foo()=exp cases.)
 But we'll still have to live with the reality that in
 the constructs "&foo" and "&expr.foo", "&" is not an operator.
As well as in all other constructs where it is used in unary form.
 And that's okay.
Except for the unfortunate confusion it can cause.
Feb 06 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 2/6/13 12:41 PM, Timon Gehr wrote:
 If they do, in this context parens are also
 punctuation, not expression grouping; they don't enclose an expression,
 but instead define &(((...(foo)...))) as a sole syntactic unit (!).
They are neither. They are just insignificant information thrown away by the parser.
Nononono. Hold them horses. We can't gloss over things like that. That means in this context parens don't have the usual meaning of expression grouping, because what's inside is not an expression. They are punctuation that makes &((...(symbol)...)) one syntactic unit identical to &symbol.
 This also leads to potential confusion, seeing as &(foo) takes the
 address of foo, but &( true ? foo : bar ) does, in fact, take the
 address of whatever foo returns.
Ah, very good point! But please note that this is 100% a ternary operator issue.
Not at all. What do these mean? &({ return fun; }()) &(fun, fun) The last one is really fun because the same thing may be evaluated first and then have its address taken. Wow. But of course it doesn't. Why? Because "&" and parens have special meaning for &(fun), which is my point.
 This all makes DIP24 fail meet its own definition of success as far as I
 understand it, i.e. keeping "&" to mean operator and parens to mean
 grouping.
1. There is no point where the DIP calls "&" an "operator".
It doesn't have to. It's everywhere else in the language. That's my point: new meaning of "&".
 2. It succeeds in keeping parens mean grouping. (Given any of the two
 behaviours of ternary expressions.)

 DIP24 meets its own definition of success.
Of course, if moving the goalposts is an option. My understanding was that the express purpose of DIP24 was to avoid potential confusion and avoid ascribing new meaning to "&" or parens. As far as I can tell it failed at both: "&" is sometimes not an operator, and parens sometimes don't encompass expressions.
 In my opinion, it also makes DIP24 fail to improve over DIP23.
 ...
In my opinion too. It conveniently also makes all other statements true, as the line of reasoning crucially depends on fallaciously ascribing properties of the ternary operator to the parens enclosing it.
That got destroyed above.
 (The second fallacy is ascribing the properties of rvalue vs. lvalue
 distinction inherited from C to DIP24.)

 DIP23 has in fact /fewer/ such problems, because it clarifies that &foo
 and &expr.foo are indivisible syntactic units; thus parentheses are not
 ascribed a special meaning by DIP23.
That is inaccurate, and it is special enough that I do not care to fully formalize the special meaning ascribed to parentheses by DIP23.
DIP23 does not ascribe any special meaning to parentheses. That's actually part of the point and a good thing about it. I think there's a misunderstanding somewhere.
 Basically it makes use of parens affect whether some position in the
 syntax tree is treated as lvalue or rvalue position in some special cases.
No, that is incorrect.
 If DIP25 gets approved, taking the address of ref results will be
 banned, and therefore that potential meaning of "&" disappears, which
 simplifies things.
How? It only adds code to my front-end to make sure the case is actually caught and properly reported. It does not even simplify the byte code compiler. (Because it uses similar code paths for the &foo() and foo()=exp cases.)
I think DIP24 means more code to the front end, more special rules, more explaining, and more confusion than DIP23.
 But we'll still have to live with the reality that in
 the constructs "&foo" and "&expr.foo", "&" is not an operator.
As well as in all other constructs where it is used in unary form.
 And that's okay.
Except for the unfortunate confusion it can cause.
And that DIP24 does not get rid of. Andrei
Feb 06 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/06/2013 08:09 PM, Andrei Alexandrescu wrote:
 On 2/6/13 12:41 PM, Timon Gehr wrote:
 If they do, in this context parens are also
 punctuation, not expression grouping; they don't enclose an expression,
 but instead define &(((...(foo)...))) as a sole syntactic unit (!).
They are neither. They are just insignificant information thrown away by the parser.
Nononono. Hold them horses. We can't gloss over things like that.
We don't! What I have stated is accurate. (You can check the DMD source, if that helps.)
 That means in this context parens don't have the usual meaning of expression
 grouping, because what's inside is not an expression.
PrimaryExpression: Identifier ...
 They are punctuation that makes &((...(symbol)...)) one syntactic unit
identical
 to &symbol.
Again, &((...(symbol)...)) and &symbol are both serialized representations of & | symbol I.e. they mean the same thing.
 This also leads to potential confusion, seeing as &(foo) takes the
 address of foo, but &( true ? foo : bar ) does, in fact, take the
 address of whatever foo returns.
Ah, very good point! But please note that this is 100% a ternary operator issue.
Not at all.
I meant 'as opposed to a paren issue'. &( true ? foo : bar ) // <-- note the ternary operator
 What do these mean?

 &({ return fun; }())
That is equivalent to &(&__dgliteral0)() where auto __dgliteral0(){ return fun; }
 &(fun, fun)
Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
 The last one is really fun because the same thing may be evaluated first
 and then have its address taken. Wow. But of course it doesn't. Why?
 Because "&" and parens have special meaning for &(fun), which is my point.
Now you are saying that parens _do_ have a special meaning?
 This all makes DIP24 fail meet its own definition of success as far as I
 understand it, i.e. keeping "&" to mean operator and parens to mean
 grouping.
1. There is no point where the DIP calls "&" an "operator".
It doesn't have to. It's everywhere else in the language.
No way. I repeat: auto x = 5; auto y = &5; // error! auto z = &x; // ok!
 That's my point: new meaning of "&".
...
Obviously your reading of my post was quite shallow.
 2. It succeeds in keeping parens mean grouping. (Given any of the two
 behaviours of ternary expressions.)

 DIP24 meets its own definition of success.
Of course, if moving the goalposts is an option.
It is. DIP24 is in draft stage. Moving the goalposts was not necessary so far though.
 My understanding was that the express purpose of DIP24 was to avoid potential
confusion and avoid ascribing new meaning to "&" or parens.
 As far as I can tell it failed at both: "&" is sometimes not an operator, and
parens sometimes don't encompass expressions.
"&" is never an operator and parens always encompass expressions. Additionally, there is a bunch of rewrite rules to get parentheses on function calls optional.
 In my opinion, it also makes DIP24 fail to improve over DIP23.
 ...
In my opinion too. It conveniently also makes all other statements true, as the line of reasoning crucially depends on fallaciously ascribing properties of the ternary operator to the parens enclosing it.
That got destroyed above.
No. It's just that both ternary expressions and comma expressions can propagate lvalue-positions, and I had forgotten about the comma expression as I was in a hurry.
 (The second fallacy is ascribing the properties of rvalue vs. lvalue
 distinction inherited from C to DIP24.)

 DIP23 has in fact /fewer/ such problems, because it clarifies that &foo
 and &expr.foo are indivisible syntactic units; thus parentheses are not
 ascribed a special meaning by DIP23.
That is inaccurate, and it is special enough that I do not care to fully formalize the special meaning ascribed to parentheses by DIP23.
DIP23 does not ascribe any special meaning to parentheses.
It does. To the point that the parser has to be changed, just in order to implement rewrite rules.
 That's actually part of the point and a good thing about it.
 I think there's a misunderstanding somewhere.
Definitely.
 Basically it makes use of parens affect whether some position in the
 syntax tree is treated as lvalue or rvalue position in some special cases.
No, that is incorrect.
It is correct. &fun // <-- fun occurs in lvalue position &(fun) // <-- (fun) occurs in lvalue position You are now arguing for making the 'fun''s in "fun" and "(fun)" behave differently. The first should behave as if it was in address-taken (lvalue) position and the second should behave as if it was not (as if it was in rvalue position). The rules for context dependency were intended to closely parallel those of lvalue position propagation in order to minimize the amount of new rules. If you see any other differences, please step up.
 If DIP25 gets approved, taking the address of ref results will be
 banned, and therefore that potential meaning of "&" disappears, which
 simplifies things.
How? It only adds code to my front-end to make sure the case is actually caught and properly reported. It does not even simplify the byte code compiler. (Because it uses similar code paths for the &foo() and foo()=exp cases.)
I think DIP24 means more code to the front end, more special rules,
Well, you are wrong, and as far as I can tell, the speculation is entirely unfounded. DIP23 is not even _implementable_ currently, because it does not even attempt to handle all cases that are necessary to handle. In order to do that, it would need to copy most of the rules of DIP24. Then, making &fun and &(fun) behave differently is more work than making them behave the same.
 more explaining,  and more confusion than DIP23.
I do not have any hard data on this, but &fun and &(fun) meaning different things is extremely confusing. Note that this is not actually part of DIP23 yet for non- property functions. (It is just left unspecified.) I guess this is an oversight?
 But we'll still have to live with the reality that in
 the constructs "&foo" and "&expr.foo", "&" is not an operator.
As well as in all other constructs where it is used in unary form.
Still holds.
 And that's okay.
Except for the unfortunate confusion it can cause.
And that DIP24 does not get rid of.
Neither does DIP23. That is not their purpose.
Feb 06 2013
next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
 &(fun, fun)
Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
Adding more special cases are not gonna create a good DIP. Can't we make foo evaluate the the first class function object ? And then figure out if/when it is safe to auto evaluate first class functions ? It seems to me like the only direction that do not ends up in an explosion of special cases.
Feb 06 2013
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 06 Feb 2013 21:53:25 -0500, deadalnix <deadalnix gmail.com> wrote:

 On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
 &(fun, fun)
Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
Adding more special cases are not gonna create a good DIP.
I don't they are so much a special case, as they are a clarification. The two "exceptions" are simply explaining that because ternary operator and comma operators evaluate to an lvalue, it is equivalent to putting the & on the resulting lvalue. -Steve
Feb 07 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/07/2013 02:56 PM, Steven Schveighoffer wrote:
 On Wed, 06 Feb 2013 21:53:25 -0500, deadalnix <deadalnix gmail.com> wrote:

 On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
 &(fun, fun)
Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
Adding more special cases are not gonna create a good DIP.
I don't they are so much a special case, as they are a clarification. The two "exceptions" are simply explaining that because ternary operator and comma operators evaluate to an lvalue, it is equivalent to putting the & on the resulting lvalue. -Steve
Exactly. Those operators already have this behaviour in other contexts. OT: One of my favourite comma/ternary-isms is the following: import std.typecons : tuple; void foo(int x){} void main(){ int x; foo((x++,tuple.expand),x,(x++,tuple.expand)); foo(true?tuple.expand:tuple.expand,x,); } I think it is valid code. :-) DMD cannot handle it though.
Feb 07 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 7 February 2013 at 13:56:17 UTC, Steven 
Schveighoffer wrote:
 On Wed, 06 Feb 2013 21:53:25 -0500, deadalnix 
 <deadalnix gmail.com> wrote:

 On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr 
 wrote:
 &(fun, fun)
Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
Adding more special cases are not gonna create a good DIP.
I don't they are so much a special case, as they are a clarification. The two "exceptions" are simply explaining that because ternary operator and comma operators evaluate to an lvalue, it is equivalent to putting the & on the resulting lvalue.
You have the following behavior : - & take the address of what is before. - foo is call to function foo. - special case : &foo is the first class function foo. (here expression foo have a new meaning). - In a comma expression, if the last item is a function, it is evaluated, unless the comma expression is in an address of context, in which case the function pointer is returned. - Same goes for ternary, except that both branches are evaluated in that context. This is exactly how special case (3) turtle down to definition of many other languages constructs. For instance, int a; int foo() { return a } static assert(is(typeof(foo) == typeof(a))); // Pass condition ? foo : a; // OK. &(condition ? foo : a); // Type error between foo of unexpressible type (static array of instructions) and a of type int.
Feb 07 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02/08/2013 05:22 AM, deadalnix wrote:
 ...

 You have the following behavior :
   - & take the address of what is before.
   - foo is call to function foo.
   - special case : &foo
foo(); foo!() tmpl!foo alias sym=foo;
 is the first class function foo. (here expression foo have a new meaning).
Again, & behaves like that even for variables. auto x = 5; auto y = &5; // error auto z = &x; // ergo: this 'x' is different assert(x==5); // from this one
   - In a comma expression, if the last item is a function, it is
 evaluated, unless the comma expression is in an address of context, in
 which case the function pointer is returned.
   - Same goes for ternary, except that both branches are evaluated in
 that context.

 This is exactly how special case (3) turtle down to definition of many
 other languages constructs.
I think ternary and comma is a comprehensible list of the many other language constructs.
 For instance,

 int a;
 int foo() { return a }

 static assert(is(typeof(foo) == typeof(a))); // Pass
 condition ? foo : a; // OK.
 &(condition ? foo : a); // Type error between foo of unexpressible type
 (static array of instructions)
(This does not make any sense. Instructions are often variable-size.)
 and a of type int.
The error message is a QOI issue. This is what DMD reports if foo is a double, and a is an int: Error: cast(double)a is not an lvalue Error: cannot implicitly convert expression (condition ? & foo : (__error)) of type double* to double* (Another point: As you may have noticed, the constructs &(a?b:c) and &(a,b) are "special cased" _anyway_.) IMHO this is roughly what should be reported in both the case that foo is a function and that foo is a double, maybe giving away a little more information: error: cannot take address of expression '(condition ? foo : a)' &(condition ? foo : a); ^~~~~~~~~~~~~~~~~~~~~~ I think DIP24 is a valid way to go. The simplest alternative design I'd be fine with would be to remove property, mandate parens on non-ufcs calls, identify function names with their function pointers/delegates, disallow foo = functionArgument and &foo for foo a function or method, and call it a day. (This would not simplify lvalue/rvalue rules, but reduce the number of cases where they apply.)
Feb 08 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 07 Feb 2013 23:22:45 -0500, deadalnix <deadalnix gmail.com> wrote:

 On Thursday, 7 February 2013 at 13:56:17 UTC, Steven Schveighoffer wrote:
 On Wed, 06 Feb 2013 21:53:25 -0500, deadalnix <deadalnix gmail.com>  
 wrote:

 On Wednesday, 6 February 2013 at 21:30:10 UTC, Timon Gehr wrote:
 &(fun, fun)
Agh, comma strikes again. It should be handled analogous to the ternary expression. i.e. the expression above evaluates fun and then returns the function pointer. The DIP now states this. (the second fun is in address-taken position.) This is in agreement to how lvalue positions propagate into comma expressions.
Adding more special cases are not gonna create a good DIP.
I don't they are so much a special case, as they are a clarification. The two "exceptions" are simply explaining that because ternary operator and comma operators evaluate to an lvalue, it is equivalent to putting the & on the resulting lvalue.
You have the following behavior : - & take the address of what is before. - foo is call to function foo. - special case : &foo is the first class function foo. (here expression foo have a new meaning). - In a comma expression, if the last item is a function, it is evaluated, unless the comma expression is in an address of context, in which case the function pointer is returned.
I think it's consistent, and no special cases. foo is treated as a fucntion name, until it is evaluated without any adornments, in which case it becomes a function call, or when the address operator is applied to it. In other words: &(foo, foo); => foo(); &(foo); => foo(); &foo; (foo, foo); => foo(); foo; => foo(); foo(); Let's note here that by opting on the side of NOT adding optional parens by default, both cases are possible: &(foo, foo()); and &(foo, foo); Under DIP23 (and the current implementation), both are equivalent, and expressive power is less.
   - Same goes for ternary, except that both branches are evaluated in  
 that context.
I would see the expression the same way, foo's meaning is not evaluated until the ternary expression is resolved. For example: condition ? foo : foo; => foo; => foo(); &(condition ? foo : foo) => &(foo); => &foo;
 This is exactly how special case (3) turtle down to definition of many  
 other languages constructs. For instance,

 int a;
 int foo() { return a }

 static assert(is(typeof(foo) == typeof(a))); // Pass
I'm unsure whether this should pass or fail. It seems disingenuous to make typeof(foo) equivalent to typeof(foo()), since foo is NOT an integer. If we err on the side of expressiveness, typeof(foo) should be the function type, not an int.
 condition ? foo : a; // OK.
I would say not ok. you have to do: condition ? foo() : a;
 &(condition ? foo : a); // Type error between foo of unexpressible type
Same, also not ok. This all seems consistent to me. It is more consistent than either the current dmd implementation, or the proposed DIP23. I can see however, that no matter what, there is going to be confusion. The simple rule is that optional parentheses only apply after all other reductions do not consider the function name as a function. The meaning of foo could be explicitly considered as a function call by including the parentheses. -Steve
Feb 08 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 06 Feb 2013 16:30:09 -0500, Timon Gehr <timon.gehr gmx.ch> wrote:

 I do not have any hard data on this, but &fun and &(fun) meaning  
 different things is extremely confusing.
I can vouch that I find it confusing. At least on one other occasion, Walter nixed a feature because of this. It was for const(X) to mean tail-const and const X to mean full const. The point was made that const(X) and const X should be equivalent, and to do otherwise will result in tremendous confusion. I see a similar pattern here. -Steve
Feb 07 2013
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 6 February 2013 at 15:32:33 UTC, Andrei 
Alexandrescu wrote:
 So syntactic case (2) prescribes that when foo is in 
 address-taken position "&foo", that means take its address as 
 opposed to evaluate foo. That makes the use of "&" here sheer 
 punctuation, as opposed to operator.
That is one of the node of the optional () problem. Your DIP have similar issue with &.
 This also leads to potential confusion, seeing as &(foo) takes 
 the address of foo, but &( true ? foo : bar ) does, in fact, 
 take the address of whatever foo returns. This makes the role 
 of "&", "(", and ")" in the proposal as both punctuation and 
 operator painfully visible.
As ell, your proposal have similar issues.
 This all makes DIP24 fail meet its own definition of success as 
 far as I understand it, i.e. keeping "&" to mean operator and 
 parens to mean grouping. In my opinion, it also makes DIP24 
 fail to improve over DIP23.

 DIP23 has in fact /fewer/ such problems, because it clarifies 
 that &foo and &expr.foo are indivisible syntactic units; thus 
 parentheses are not ascribed a special meaning by DIP23. On the 
 contrary, as soon as parens are used, as in &(foo) or &( true ? 
 foo : bar ), the usual meaning of parens enters in action and 
 give the expression inside the usual meaning.
I do see special meaning as an issue, as it complexity the language. DIP24 is an improvement in that regard over DIP23.
 If DIP25 gets approved, taking the address of ref results will 
 be banned, and therefore that potential meaning of "&" 
 disappears, which simplifies things. But we'll still have to 
 live with the reality that in the constructs "&foo" and 
 "&expr.foo", "&" is not an operator. And that's okay.
That is cascading the problem, not solving it.
Feb 06 2013