August 17, 2007
Deewiant wrote:
> Chad J wrote:
>> Properties are a blemish of the D language.  In the first part of this
>> post, I'll explain why and try to show how these properties are /not/
>> good or even better than properties in other languages.  Then I'll
>> suggest some possible solutions.  I don't care if the solutions I
>> suggest are used, but I will be very thankful if this gets fixed somehow.
>>
> 
> Great that you took the time to go through it properly.
> 
> I agree that all the issues you mentioned need to be fixed somehow.

Ditto.

> C# uses the "property" keyword IIRC, and even though adding new keywords is a
> big no-no I find this worth considering. Who uses "property" as an identifier
> name anyway?

Well, 'property' may be in use by people writing property-editor widgets like in IDEs.  But still, I'd go for a new 'property' keyword.  Will probably clash with far fewer existing uses that 'ref' did.

--bb
August 17, 2007
Bill Baxter wrote:
> Sean Kelly wrote:
>> Chad J wrote:
>>>
>>> This post is a bit lengthy.  So assuming you've read even part of it, thank you for your time.
>>
>> All good points.  I'd like properties in D to address them all somehow.  The 'ref' keyword in 2.0 solves some of the issues, but not others. If doing so requires a special syntax then I'm all for it.
> 
> What issues does ref fix?  You can't return a ref even in 2.0, AFAIK.

Oh.  I assume this is a planned feature then?  Otherwise I see little reason to change "inout" to "ref."


Sean
August 17, 2007
Sean Kelly wrote:
> Bill Baxter wrote:
>> Sean Kelly wrote:
>>> Chad J wrote:
>>>>
>>>> This post is a bit lengthy.  So assuming you've read even part of it, thank you for your time.
>>>
>>> All good points.  I'd like properties in D to address them all somehow.  The 'ref' keyword in 2.0 solves some of the issues, but not others. If doing so requires a special syntax then I'm all for it.
>>
>> What issues does ref fix?  You can't return a ref even in 2.0, AFAIK.
> 
> Oh.  I assume this is a planned feature then?  

I hope so.  That would be great.

> Otherwise I see little reason to change "inout" to "ref."

I thought the reason was more to do with const.  Given a const object passed by reference, calling it 'inout' doesn't make much sense.

--bb
August 17, 2007
BCS wrote:
> Reply to Chad,

> The basis of your thoughts seem to come from the idea that hiding the fact that properties are functions would be a good thing. I would assert that this is highly debatable.

Ah yes.  Although I generally agree with Chad, you did remind me of something.  In my Luigi GUI library I use properties for lots of widget settings (things like a button's label, etc), and also rely on being able to call those as delegates for signal-slot connections to set the properties.

That's handy, but it only works for members/properties that are written as functions.  What I should probably do is to auto-generate a delegate wrapper for pure data members if you try to use one as a slot for a delegate(T setval) type of signal.

The other issue it runs into is the overload ambiguity of
   void foo(int) {}
   int foo() {}
when you take the function reference.

One way to resolve the desire to use properties, not pretend they're functions, and still allow usage as a function when possible, would be to allow giving the functions names different from the property name.

So then you could do something like:

   void set_foo(int) {...}
   int get_foo() {...}
   property foo(get_foo,set_foo);

Or
   property foo {
     void set_foo(int) {...}
     int get_foo() {...}
   }

Of course now you can already make aliases if you just want unambiguous function names to take the address of.  (Doesn't prevent using foo() as a function though)
   void set_foo(int) {...}
   int get_foo() {...}
   alias set_foo foo;
   alias get_foo foo;  // not 100% sure this works...

--bb
August 17, 2007
Bill Baxter wrote:
> Sean Kelly wrote:
> 
>> Otherwise I see little reason to change "inout" to "ref."
> 
> I thought the reason was more to do with const.  Given a const object passed by reference, calling it 'inout' doesn't make much sense.

This is the explanation I remember as well.  But I'm not sure it justifies the addition of a general-purpose sounding keyword simply for parameter passing.  "inout" may have been a bit confusing in light of const reference passing, but is consistency with "in" and "out" made it arguably worth retaining.


Sean
August 17, 2007
Chad J wrote:
> Alright I forgot to mention what something like "a.prop.x = foo;" would expand to.
> 
> It would work like this:
> 
> a.prop.x = foo;
> 
> expands to
> 
> auto temp = a.prop;
> temp.x = foo;
> a.prop = temp;
> 
> Whether temp is a reference or value type need not matter, it should work exactly as it would if there was a variable there instead of a property.
> I suppose Sean's suggestion of allowing programmers to trap reference and value uses of properties would work, but I am skeptical that it might break the close compatibility between plain variables (fields) and properties.  It should be feasible as long as there is a way to make a property equivalent to a field wrt calling syntax/semantics.
> 
> Longer chains should be broken down as such:
> 
> a.prop1.prop2.field.prop3.x = foo;
> 
> ->
> 
> auto temp1 = a.prop1;
> auto temp2 = temp1.prop2;
> auto temp3 = temp2.field.prop3;
> temp3.x = foo;
> temp2.field.prop3 = temp3;
> temp1.prop2 = temp2;
> a.prop1 = temp1;
> 
> Hope that helps.

That makes some sense.  Although just avoiding the copying by allowing either references or a way to create a light proxy that overloads opDot would be more useful.  Then you could return a proxy from 'a' that contains just a pointer to prop2.

Either way, D sorely needs a way to overload opDot (akin to C++'s operator->).  Not having it eliminates a huge class of useful ways to create value wrappers.  There's some seriously cool potential for a CTFE template opDot that takes a string giving the member name.

--bb
August 17, 2007
Sean Kelly wrote:
> Bill Baxter wrote:
>> Sean Kelly wrote:
>>
>>> Otherwise I see little reason to change "inout" to "ref."
>>
>> I thought the reason was more to do with const.  Given a const object passed by reference, calling it 'inout' doesn't make much sense.
> 
> This is the explanation I remember as well.  But I'm not sure it justifies the addition of a general-purpose sounding keyword simply for parameter passing.  "inout" may have been a bit confusing in light of const reference passing, but is consistency with "in" and "out" made it arguably worth retaining.
> 

It's all the more perplexing given that the current D 2.x compiler segfaults on functions declared with const ref parameters.  If const refs were the reason for changing 'inout' to 'ref' it is odd that they don't work straight out of the gate.

--bb
August 18, 2007
BCS wrote:
> Reply to Chad,
> 
>> -------------------------------------(1)
>>
>> First, the very common ambiguity:
>>
>> Does
>> foo.bar++;
>> become
>> foo.bar( foo.bar + 1 );
>> or
>> foo.bar() + 1;
>> ??
>> This is why we can't write things like this:
>>
>> ubyte[] array = new ubyte[50];
>> //...
>> array.length++;
>> //...
>> array.length += 10;
>> // Note how builtin types use properties heavily.
>> Which means a large set of our beloved shortcut operators are
>> completely broken on properties.
>>
> 
> This is not an syntax  issue with how proposes are defined, but the semantics of there use.
> It can be fixed with minor changes.

What minor changes?

If you're going to suggest giving implicit properties lvalueness, then this would break any code that relies on the quirks of current implicit properties.  I was hoping to preserve reverse compatibility with my solution, especially since Walter seems conservative about doing anything that breaks existing code.

> 
>> -------------------------------------(2)
> 
> [...]
> 
> this is the same as #1 (or am i missing somthing)
> 

Promotion != lvalueness

The idea here is that #2 is a natural consequence of #1, thus you see them as the same.  #2 though, has a different consequence for the programmer.  #1 is merely very annoying, while #2 causes nasty code extensibility issues.

>> -------------------------------------(3)
>>
>> Third, there is a problem with value types and properties. Consider
>> the following code:
>>
> 
> short version:
> 
> struct S {int i; void I(int j){i=j;} }
> struct P {S s void GetS() { return s;} }
> 
> P p;
> p.GetS.I = 5; // assignes 5 to temp, not member of p
> 
> This is a reference semantics issue. The same isses happens to general functions.
> 

Exactly.  I don't want function semantics with my properties.  I already have function semantics with my functions.

>> -------------------------------------(4)
>>
>> Fourth, delegates get the shaft.
> 
> Short version:
> 
> void delegate() getDG();
> 
> getDG(); // calls getDG giving a delgate
> getDG(); // calls getDG, does not call returned delegate
> 
> 
> good point.
> 
>>
>> -------------------------------------(5)
>>
>> Fifth, delegates aren't over with yet.  This isn't nearly as bad as
>> the
>> other 4, but it should hit eventually.
>> Properties can be converted into delegates.
> [...]
>> The problem is, what if, for whatever reason, the author of similar
>> code
>> someday wanted to demote those properties back to fields?
>> In that case, the user code that relies on the delegate will fail.
> [...]
>> Realizing that this behavior of properties can be beneficial, it makes
>> sense to show that similar code can be written without properties and
>> without much added difficulty.
> [...]
>> struct Point
>> {
>> int x,y;
>> }
>> void main()
>> {
>> Point p;
>> int getX() { return p.x; }
>> int delegate() readX = &getX;
>> p.x = 2;
>> printf( "%d", readX() ); // prints 2
>> }
> 
> ouch! that sounds worse than the problem you point out.
> 

I disagree, but in light of what you value, it becomes an aesthetics vs functionality issue.

>> ========================================
>> How to fix it
>> ========================================
> [...]
>> *lvalue:  If foo is an lvalue, than foo = 5; should set foo's value to
>> 5, instead of evaluating to 5; and doing nothing.  Implicit write
>> properties fake this to some extent, but do not hold in the general
>> case.  Example (3) above and the foo++; and foo+=10; issues are
>> examples
>> where lvalueness is needed.
> 
> I would disagree. I would clam that the issue is that implicit properties can be used a lvalues or rvalues, but not both.
> 

Still doesn't handle promotion/demotion between fields and properties.
Also, if you have a way to make the lvalue&rvalue semantics work, let me hear it.

> 
> 
> [......]
> 
> The basis of your thoughts seem to come from the idea that hiding the fact that properties are functions would be a good thing. I would assert that this is highly debatable. I will concede that the current semantics of properties being not quite interchangeable with fields is sub optimal. However fixing this by creating a new construct that, in the end, is identical to a function but with a number of things forbidden seems somehow wrong.
> 

Actually, I am suggesting that properties not be functions at all.  They will of course share the notion of a body of executable code, but for all intents and purposes they are a way of accessing data while introducing side effects.

> As I see it the issues you bring up amount to:
> 
> 1> no L/R-value usage (++, +=, etc, use as an inout or out arg)
> 2> they can lead to hard to find reference semantics issues
> 3> syntax problems with properties retuning delegates
> 4> not interchangeable with fields
> 
> #1 can be fixed without explicit properties
how?
> #2 is not at all a problem in my book
I have suffered many hours of debugging thanks to this "feature".  Don't tell me it isn't a problem!
> #3 This is a tiny corner case
I disagree, but regardless let's at least fix it while we are dealing with the related stuff.
> #4 this is a good point, however, as stated above, I don't like the proposed solution. Also, the proposed solution doesn't fix the issue because taking the address of a field is allowed, even with explicit properties, this wouldn't work
> 

If the proposed solution is not good enough, please give a better one. Suggestions are welcome and encouraged.

As for the address of a field... perhaps if taking the address of a field returned a delegate... nah.
This is somewhat of a corner case, but you've got me there, for now.
I'd love to see someone come up with a slick solution to this.
I should also note that C# didn't seem to have this problem with its explicit properties.  That is probably because in C#, using pointers was considered "unsafe" and done very infrequently.  It makes me want to bust out C# again and see what happens when one tries to take the address of a property, and I probably will at some point.

Even if the addressing thing can't be fixed, I'd at least like to see the rest of the stuff get fixed.

> 
> IMHO there are a few minor issues with proposes that could uses a little work. However I don't see much to be gained by you proposal for explicit properties.
> 
>> This post is a bit lengthy.  So assuming you've read even part of it,
>> thank you for your time.
>> - Chad
> 
> 
> It's good to see people thinking about this stuff. I may not see your ideas to be the "right" way but, then again, I want different things out of D than many people do.
> 
> 
August 18, 2007
Reply to Chad,

> BCS wrote:
> 
>> Reply to Chad,
>> 
>>> -------------------------------------(1)
>>> 
>>> First, the very common ambiguity:
>>> 
>>> Does
>>> foo.bar++;
>>> become
>>> foo.bar( foo.bar + 1 );
>>> or
>>> foo.bar() + 1;
>>> ??
[...]
>> This is not a syntax  issue with how properties are defined, but the
>> semantics of there use.
>> It can be fixed with minor changes.
>
> What minor changes?
> 
> If you're going to suggest giving implicit properties lvalueness, then
> this would break any code that relies on the quirks of current
> implicit properties.  I was hoping to preserve reverse compatibility
> with my solution, especially since Walter seems conservative about
> doing anything that breaks existing code.
> 

How? Letting implicit properties be simultaneously L and R values (they can now be either, just not both at the same time) won't break anything that I know of. Anything that is legal now, still would be, and any cases where the behavior would be different, were illegal to begin with.

>>> -------------------------------------(2)
>>> 
>> [...]
>> 
>> this is the same as #1 (or am i missing somthing)
>> 
> Promotion != lvalueness
> 
> The idea here is that #2 is a natural consequence of #1, thus you see
> them as the same.  #2 though, has a different consequence for the
> programmer.  #1 is merely very annoying, while #2 causes nasty code
> extensibility issues.
> 

If you see them as disticntly different points, then we must be looking at things at different levels. I see both 1 and 2 as saying: "properties can be used with ++, += etc and members can". OK I'll grant that #1 doesn't cover a few cases that #2 does, but that just makes #1 a special case of #2.

>>> -------------------------------------(3)
>>> 
>>> Third, there is a problem with value types and properties. Consider
>>> the following code:
>>> 
>> short version:
>> 
>> struct S {int i; void I(int j){i=j;} }
>> struct P {S s void GetS() { return s;} }
>> P p;
>> p.GetS.I = 5; // assignes 5 to temp, not member of p
>>
>> This is a reference semantics issue. The same isses happens to
>> general functions.
>> 
> Exactly.  I don't want function semantics with my properties.  I
> already have function semantics with my functions.
> 

I see your point and I strongly disagree. Under the covers, it IS a function and I /don't/ want that hidden.

>>> -------------------------------------(5)
>>> 
>>> Fifth, delegates aren't over with yet.
>> [...]
>>> the user code that relies on the delegate will fail.
>> [...]
>> 
>> ouch! that sounds worse than the problem you point out.
>> 
> I disagree, but in light of what you value, it becomes an aesthetics
> vs functionality issue.
> 

Unless you can trust the compiler to inline delegate literals (and the potentially virtual function call underneath), it is a performance issue.


>>> ========================================
>>> How to fix it
>>> ========================================
>> [...]
>> 
>>> *lvalue:  If foo is an lvalue, than foo = 5; should set foo's value
>>> to
>>> 5, instead of evaluating to 5; and doing nothing.  Implicit write
>>> properties fake this to some extent, but do not hold in the general
>>> case.  Example (3) above and the foo++; and foo+=10; issues are
>>> examples
>>> where lvalueness is needed.
>>
>> I would disagree. I would clam that the issue is that implicit
>> properties can be used a lvalues or rvalues, but not both.
>> 
> Still doesn't handle promotion/demotion between fields and properties.

My point was not intended to. It is a minor issue claming that the mechanics of one of your (valid) issues with implicit properties is incorrectly stated.

> Also, if you have a way to make the lvalue&rvalue semantics work, let
> me hear it.
> 

I think that you actually described somewhere the solution I wold use.

Use the R-value property (getter) to get the value, do the operation, then use the L-value property (setter) to push it back in.

struct S { ... int i(); void i(int); }
S s;

s.i++;
// becomes
{
 auto __tmp = s.i;   __tmp++;
 s.i = __tmp;
}

If only one or the other property exits then it is assumed the author wants to disallow this type of operation.

>> [......]
>> 
>> The basis of your thoughts seem to come from the idea that hiding the
>> fact that properties are functions would be a good thing. I would
>> assert that this is highly debatable. I will concede that the current
>> semantics of properties being not quite interchangeable with fields
>> is sub optimal. However fixing this by creating a new construct that,
>> in the end, is identical to a function but with a number of things
>> forbidden seems somehow wrong.
>> 
> Actually, I am suggesting that properties not be functions at all.
> They will of course share the notion of a body of executable code, but
> for all intents and purposes they are a way of accessing data while
> introducing side effects.
> 

Will they be accessed by doing a call/return sequence at the machine code level? If so, I would assert they are functions for all purposes I'm interested in. If it would be implemented by some other means, then I have have missed some major point of your post.

D is a low level language in many respects, as such, if it looks like a function when it comes out of the compiler, I want to be able to treat it as a function when it goes in.

>> As I see it the issues you bring up amount to:
>> 
>> 1> no L/R-value usage (++, +=, etc, use as an inout or out arg)
>> 2> they can lead to hard to find reference semantics issues
>> 3> syntax problems with properties retuning delegates
>> 4> not interchangeable with fields
>> #1 can be fixed without explicit properties
>> 
> how? 

same as above

> 
>> #2 is not at all a problem in my book
>> 
> I have suffered many hours of debugging thanks to this "feature".
> Don't tell me it isn't a problem!
>

It's not a problem with /properties/. dropping properties compleaty wouldn't remove this type of isses. It might make some cases easyer to spot but nothing more. As to converting from a member to a property changing things, that is part of point #4.

>> #4 this is a good point, however, as stated above, I don't like the
>> proposed solution. Also, the proposed solution doesn't fix the issue
>> because taking the address of a field is allowed, even with explicit
>> properties, this wouldn't work
>> 
> If the proposed solution is not good enough, please give a better one.
> Suggestions are welcome and encouraged.
> 

Nothing yet. IMHO, your proposed solution, while maybe appropriate for higher level languages, is not right for D. If I see a solution that solves these issues without cause to many of its own problems, I would be interested.

> As for the address of a field... perhaps if taking the address of a
> field returned a delegate... nah.
> This is somewhat of a corner case, but you've got me there, for now.
> I'd love to see someone come up with a slick solution to this.
> I should also note that C# didn't seem to have this problem with its
> explicit properties.  That is probably because in C#, using pointers
> was
> considered "unsafe" and done very infrequently.  It makes me want to
> bust out C# again and see what happens when one tries to take the
> address of a property, and I probably will at some point.
> Even if the addressing thing can't be fixed, I'd at least like to see
> the rest of the stuff get fixed.

I'll bet you can't. You can't use one for an out or ref argument (I just cheked) but you can use a member.


August 18, 2007
BCS wrote:
> I see your point and I strongly disagree. Under the covers, it IS a function and I /don't/ want that hidden.

By the very virtue that D makes the syntax identical, it _is_ trying to hide that fact.  Either properties must try to act like members or the usage of properties should not mirror members.