September 29, 2008
Chris R. Miller wrote:
> Improper use of properties make code that looks ambiguous.  Is that a public member or a method?

Under what circumstances is that distinction important?

> It reduces codebase coherency

How? Why?

>, which is 
> normally not a huge problem except when new programmers are reading through the code to see how it works.

Why do they need to know whether it's a computed or a state value?

> If someone mistook that method call via property as a member, then they might use it as such in code they write.  Because it's actually a method, it has the potential to rope in serious amounts of processor work.

That is the complexity issue that was discussed a while ago in the digitalmars.d group. I agree that certain interfaces should come with a complexity guarantee. But the line is definitely not drawn at member/computed value. Besides public members are bad style to start with, so essentially the argument is that everybody must litter their code with (). Why? Because there is the implied assumption that what doesn't have the () is a member variable access. I am challenging that assumption.

> It's the same reason you don't want to overload simple operators in C++ with potentially large algorithms, since it creates an illusion that the operator is really a simple operation when in fact it could be quite tedious.

I agree. That is what the STL does.

> This is - of course - a matter of practice with C++, and isn't a language requirement, but it does illustrate that it's something to be conscious of.  Similarly, you wouldn't want to load up a D invariant with a lot of hard processor work, since that would incur a lot of overhead following each public function call.  There's nothing explicitly wrong about doing it, it's just bad practice (according to some).
> 
> I hope that was coherent.  I believe I read about it in /Code Complete/, if you want a source.

I don't have the latest, so a quote would be helpful.


Andrei
September 29, 2008
Chris R. Miller wrote:
> Improper use of properties make code that looks ambiguous.  Is that a public member or a method?

Under what circumstances is that distinction important?

> It reduces codebase coherency

How? Why?

>, which is 
> normally not a huge problem except when new programmers are reading through the code to see how it works.

Why do they need to know whether it's a computed or a state value?

> If someone mistook that method call via property as a member, then they might use it as such in code they write.  Because it's actually a method, it has the potential to rope in serious amounts of processor work.

That is the complexity issue that was discussed a while ago in the digitalmars.d group. I agree that certain interfaces should come with a complexity guarantee. But the line is definitely not drawn at member/computed value. Besides public members are bad style to start with, so essentially the argument is that everybody must litter their code with (). Why? Because there is the implied assumption that what doesn't have the () is a member variable access. I am challenging that assumption.

> It's the same reason you don't want to overload simple operators in C++ with potentially large algorithms, since it creates an illusion that the operator is really a simple operation when in fact it could be quite tedious.

I agree. That is what the STL does.

> This is - of course - a matter of practice with C++, and isn't a language requirement, but it does illustrate that it's something to be conscious of.  Similarly, you wouldn't want to load up a D invariant with a lot of hard processor work, since that would incur a lot of overhead following each public function call.  There's nothing explicitly wrong about doing it, it's just bad practice (according to some).
> 
> I hope that was coherent.  I believe I read about it in /Code Complete/, if you want a source.

I don't have the latest, so a quote would be helpful.


Andrei
September 29, 2008
Chris R. Miller wrote:
> Andrei Alexandrescu wrote:
>> Denis Koroskin wrote:
>>> On Sun, 28 Sep 2008 05:45:58 +0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>>>
>>>> Sergey Gromov wrote:
>>>>> Sat, 27 Sep 2008 15:19:01 -0500,
>>>>> Andrei Alexandrescu wrote:
>>>>>> My point is that I agree with all concerns you are raising but I am not
>>>>>> sure they warrant adding a language feature.
>>>>>  I hoped for some reason that these features could simplify the compiler.  Now when I think about it I conclude that I was probably wrong.  Explicit properties is definitely a feature, even though it seems easy to implement. Injectons could help if Walter were forced into supporting different scoping rules for unified call syntax, but if a.f(b) stays strictly a sugar for f(a,b) this feature helps nothing from a compiler standpoint.
>>>>>  So I'll probably agree that these features don't add much to the language, as D doesn't add much to C except safety, productivity, maintainability and claritiy.  The clarity/maintainability vs genericity is a tradeoff which is completely in Walter's hands.
>>>>
>>>> Very wise words.
>>>>
>>>> I think we all agree that there are some annoyances related to the whole property business, among which the main one is:
>>>>
>>>> writeln = 4;
>>>>
>>>> That is quite indefensible :o|. I consider the others rather minor, but that's just a personal opinion.
>>>>
>>>> How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas?
>>>>
>>>>
>>>> Andrei
>>>
>>> I think property syntax should be disallowed by default. Most of the functions aren't expected to be used as a property, anyway.
>>
>> By whom aren't they expected?
>>
>>> It would break the code, yes, but it is very easy to fix.
>>>
>>> Solution of my preference:
>>>
>>> class Foo
>>> {
>>>     // allow property syntax:
>>>     property void bar(int baz); // allows "foo.bar = 42;"
>>>
>>>     // allow omitting parens
>>>     property int bar();         // allows writing "int x = foo.bar;"
>>> }
>>>
>>> Think long term, don't be afraid to break existing code and introduce new features if they improve the language signaficantly. Wrong decision made today may make huge negative impact over time.
>>
>> Without solid argument, all this is just talk. What is the negative impact over time?
> 
> Improper use of properties make code that looks ambiguous.  Is that a public member or a method?  It reduces codebase coherency, which is normally not a huge problem except when new programmers are reading through the code to see how it works.  If someone mistook that method call via property as a member, then they might use it as such in code they write.  Because it's actually a method, it has the potential to rope in serious amounts of processor work.
> 
> It's the same reason you don't want to overload simple operators in C++ with potentially large algorithms, since it creates an illusion that the operator is really a simple operation when in fact it could be quite tedious.  This is - of course - a matter of practice with C++, and isn't a language requirement, but it does illustrate that it's something to be conscious of.  Similarly, you wouldn't want to load up a D invariant with a lot of hard processor work, since that would incur a lot of overhead following each public function call.  There's nothing explicitly wrong about doing it, it's just bad practice (according to some).
> 
> I hope that was coherent.  I believe I read about it in /Code Complete/, if you want a source.

Oh, besides - if computed properties are allowed, they also can perform arbitrary amounts of work. No?

Andrei
September 29, 2008
Andrei Alexandrescu wrote:
> Andrei Alexandrescu wrote:
>> How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas?
> 
> I actually did have something in mind when I wrote this, just didn't want to bias anyone.
> 

For the record, you have my vote on this idea. it would greatly simplify a lot of my aliases and template usage in particular (though I'm using D1)

+1
September 29, 2008
Andrei Alexandrescu wrote:
> Chris R. Miller wrote:
>> Improper use of properties make code that looks ambiguous.  Is that a public member or a method?
> 
> Under what circumstances is that distinction important?

My lack of example situations is offset by my fear of one day finding one as a problem.

>> It reduces codebase coherency
> 
> How? Why?

In Java, if something was called like a function with the () and everything, you could be pretty dang sure it's a method.  In D, if it doesn't have a () when you see it, it could still be a method called as a property.  The only way to find out is to look it up, which can be tedious and boring.  If you don't look it up, you may never know, which reduces the coherency of the code.

>> , which is normally not a huge problem except when new programmers are reading through the code to see how it works.
> 
> Why do they need to know whether it's a computed or a state value?

I prefer to know what I'm doing when using an API - or any other code, for that matter.  I think the more information you have about a system, the better able you are to make use of it.  A method call indicates a potential for additional machinery behind the scenes, which can be useful information when deciding how to craft an algorithm that makes use of other code.

>> If someone mistook that method call via property as a member, then they might use it as such in code they write.  Because it's actually a method, it has the potential to rope in serious amounts of processor work.
> 
> That is the complexity issue that was discussed a while ago in the digitalmars.d group. I agree that certain interfaces should come with a complexity guarantee. But the line is definitely not drawn at member/computed value. Besides public members are bad style to start with, so essentially the argument is that everybody must litter their code with (). Why? Because there is the implied assumption that what doesn't have the () is a member variable access. I am challenging that assumption.

We could just do as Objective-C and use that strange typid foo = [NSObject toString]; syntax, which would negate the need for the whole discussion.  ;-)

>> It's the same reason you don't want to overload simple operators in C++ with potentially large algorithms, since it creates an illusion that the operator is really a simple operation when in fact it could be quite tedious.
> 
> I agree. That is what the STL does.

STL is... strange, and I have not yet been well versed in it.  The likes of superdan has convinced me that there is something of substance in it, though until I can really find some time to sink my teeth into it, I can have but no comment.

>> This is - of course - a matter of practice with C++, and isn't a language requirement, but it does illustrate that it's something to be conscious of.  Similarly, you wouldn't want to load up a D invariant with a lot of hard processor work, since that would incur a lot of overhead following each public function call.  There's nothing explicitly wrong about doing it, it's just bad practice (according to some).
>>
>> I hope that was coherent.  I believe I read about it in /Code Complete/, if you want a source.
> 
> I don't have the latest, so a quote would be helpful.

Actually I think it was from "Game Coding Complete" (Mike McShaffery, 2003).  I seem to remember it being in proximity to another rant about how polydimensional vector addition using overloaded operators is a Bad Thing (TM) because junior programmers will do it left and right thinking "Eh, it's an operator, therefore fast!" when in fact is was a hugely expensive operation that should be avoided to speed up things.

My brain leaks, I may never fully remember where it came from until I find that book again (somewhere in my room, though not sure where).
September 29, 2008
On Mon, Sep 29, 2008 at 8:32 AM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> Andrei Alexandrescu wrote:
>>
>> How about this. Maybe if we attacked this annoyance in particular, that would be a large bang for the buck without a landslide change in the compiler. We only need some way to inform the compiler, "yes, it's ok to call a.b(c) as a.b = c". Ideas?
>
> I actually did have something in mind when I wrote this, just didn't want to bias anyone.
>
> My thinking is that the syntax "a.b = c" in lieu of a.b(c) for a function
> a.b(T x) should be allowed if and only if there also exists a function a.b()
> that returns a value of type T.
>
> Example:
>
> struct S1
> {
>    void prop(int x);
> }
>
> S1 s1;
> s1 = x; // error, prop is not a property
>
> struct S2
> {
>    void prop(int x);
>    int prop();
> }
>
> S2 s2;
> s2.prop = 42; // fine, prop is a property because it also has a getter
>
> This solution does not require any syntactic addition. Its drawback is that it makes it hard to define write-only properties. Are they important?

Seems a little too subtle to me.  Maybe no problem for the compiler, but yet another kooky rule the human has to hold in their brain when trying to read D code.

The rule is trivial, you may say.  But how about co-variant return types?  If FooSub is a subclass of Foo, then is it ok for the getter to return one and setter to take the other?  Or vice versa?  Or how about implicit conversions.  If my getter takes a long, but my setter returns an int, is that ok?  How do const variations fit in?  Probably there's a way to fit those all neatly in an automatic rule (or just disallow them), but it just makes the rule longer and even harder to keep in mind (or cuts off functionality people may need).

Also how about preventing the calling of property setters as functions?

s2.next(42) looks like it will do something quite different from s2.next = 42.  I would like for property syntax to also disallow function call syntax.

---
Somewhat unrelated, but there still exists the annoyance in D that if you have to functions with the same name and you want to take the address of one of them, you can't.  Furthermore I can't think of a reasonable syntax to do that easily.  For that reason,  I really think the best getter and setter functionality in D would be something where you have distinctly named *functions* getProp and setProp for when you want/need functions mirroring samely-named *properties* for which only property syntax would work.

Basically there's no convenient way to take the address of one of a getter/setter function pair, currently.  I think that should factor in the solution here.

--bb
September 29, 2008
On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> struct S2
> {
>      void prop(int x);
>      int prop();
> }
> 
> S2 s2;
> s2.prop = 42; // fine, prop is a property because it also has a getter
> 
> This solution does not require any syntactic addition. Its drawback is that it makes it hard to define write-only properties. Are they important?

I think having a function returing *the same type* is probably too much. What about this case:

struct S
{
	string str();
	void str(string s);
	void str(wstring s);
	void str(dstring s);
}

In this case, you can only do "str = x;" when x is an UTF-8 string. Setting UTF-16 and UTF-32 would (sadly) require using the parenthesis syntax under your proposal. So I think you should not check for the return type of the getter, just if there is a function of the same name with no parameter, to determine if the "=" syntax can be used.

...

Hum, and what do you do with this:

struct S2
{
	void prop(int x);
	private int prop();
}

S2 s2;
s2.prop = 42; // legal or not?

If it's legal, then I think it'll be used as a hack to do write-only properties.

And also, if you keep checking the return type of the getter, what about this:

struct S2
{
	void prop(Object x);
	const(Object) prop();
}

Should "prop = new Object;" be legal or not? What if prop() was returning an invariant(Object) instead (for which there is no allowed implicit conversions)? And what if there are two overloads for the setter: one with an invariant(Object) and one with an Object (mutable)? Are both allowed to be called with "="?

Then you should probably disallow "ref" and "out" too in the setter. I'm not sure either about what you'll do once "shared" gets in the picture. (And ain't "scope" comming at some point too?)

Also, what if one or the other is a static function? And what if you have an invariant(S2) and prop(Object x) is accessible (because it is invariant) while prop() is not (because not invariant), should the "=" syntax apply anyway?

...

Basically what I want to illustrate is that another drawback of this solution is that it will probably not be as straitforward to define, and then understand, as it seem at first glance.

The current syntax, with all its drawbacks, has the advantage of being very easy to understand: using "=" works all the time and it's the caller's responsibility to use the syntax which express better what he is doing. Personally, I have no problem with that.

-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

September 29, 2008
Bill Baxter wrote:
> On Mon, Sep 29, 2008 at 8:32 AM, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>> Andrei Alexandrescu wrote:
>>> How about this. Maybe if we attacked this annoyance in particular, that
>>> would be a large bang for the buck without a landslide change in the
>>> compiler. We only need some way to inform the compiler, "yes, it's ok to
>>> call a.b(c) as a.b = c". Ideas?
>> I actually did have something in mind when I wrote this, just didn't want to
>> bias anyone.
>>
>> My thinking is that the syntax "a.b = c" in lieu of a.b(c) for a function
>> a.b(T x) should be allowed if and only if there also exists a function a.b()
>> that returns a value of type T.
>>
>> Example:
>>
>> struct S1
>> {
>>    void prop(int x);
>> }
>>
>> S1 s1;
>> s1 = x; // error, prop is not a property
>>
>> struct S2
>> {
>>    void prop(int x);
>>    int prop();
>> }
>>
>> S2 s2;
>> s2.prop = 42; // fine, prop is a property because it also has a getter
>>
>> This solution does not require any syntactic addition. Its drawback is that
>> it makes it hard to define write-only properties. Are they important?
> 
> Seems a little too subtle to me.  Maybe no problem for the compiler,
> but yet another kooky rule the human has to hold in their brain when
> trying to read D code.
> 
> The rule is trivial, you may say.  But how about co-variant return
> types?  If FooSub is a subclass of Foo, then is it ok for the getter
> to return one and setter to take the other?  Or vice versa?  Or how
> about implicit conversions.  If my getter takes a long, but my setter
> returns an int, is that ok?  How do const variations fit in?  Probably
> there's a way to fit those all neatly in an automatic rule (or just
> disallow them), but it just makes the rule longer and even harder to
> keep in mind (or cuts off functionality people may need).

Yah, I was thinking of all these and then some more while I was posting.

I think it's another duck typing thing. If you can call:

entity.prop(entity.prop);

then you can consider prop a property, period. Entity could be anything that allows the dot member access (object, class, struct, union, template, or module). Given that there is no global scope in D, that takes care of everything. I think it's really hard to get any simpler than that.

> Also how about preventing the calling of property setters as functions?
> 
> s2.next(42) looks like it will do something quite different from
> s2.next = 42.  I would like for property syntax to also disallow
> function call syntax.

I'd also like plenty many things, but they cost.

> ---
> Somewhat unrelated, but there still exists the annoyance in D that if
> you have to functions with the same name and you want to take the
> address of one of them, you can't.  Furthermore I can't think of a
> reasonable syntax to do that easily.  For that reason,  I really think
> the best getter and setter functionality in D would be something where
> you have distinctly named *functions* getProp and setProp for when you
> want/need functions mirroring samely-named *properties* for which only
> property syntax would work.
> 
> Basically there's no convenient way to take the address of one of a
> getter/setter function pair, currently.  I think that should factor in
> the solution here.

Overloading is the issue, and that's quite a different story. If you know the exact type, you can take the address of something.

struct S
{
    int foo() {}
    void foo(int) {}
}

void main()
{
    S s;
    void delegate(int) x = &s.foo;
    int delegate() y = &s.foo;
}


Andrei
September 29, 2008
Michel Fortin wrote:
> On 2008-09-28 19:32:43 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
> 
>> struct S2
>> {
>>      void prop(int x);
>>      int prop();
>> }
>>
>> S2 s2;
>> s2.prop = 42; // fine, prop is a property because it also has a getter
>>
>> This solution does not require any syntactic addition. Its drawback is that it makes it hard to define write-only properties. Are they important?
> 
> I think having a function returing *the same type* is probably too much. What about this case:
> 
> struct S
> {
>     string str();
>     void str(string s);
>     void str(wstring s);
>     void str(dstring s);
> }
> 
> In this case, you can only do "str = x;" when x is an UTF-8 string. Setting UTF-16 and UTF-32 would (sadly) require using the parenthesis syntax under your proposal. So I think you should not check for the return type of the getter, just if there is a function of the same name with no parameter, to determine if the "=" syntax can be used.

Great point. I think the rule suggested in my latest post convers this. The rule was, if you can call s.str(s.str) then str is a property and allows the syntax "=". After than overloading will take care of all else.

> ...
> 
> Hum, and what do you do with this:
> 
> struct S2
> {
>     void prop(int x);
>     private int prop();
> }
> 
> S2 s2;
> s2.prop = 42; // legal or not?
> 
> If it's legal, then I think it'll be used as a hack to do write-only properties.

It is legal because access check is done after syntactic check. (Also Walter wants to introduce the fun() = void syntax to disallow copying; that might be useful in this context too.)

> And also, if you keep checking the return type of the getter, what about this:
> 
> struct S2
> {
>     void prop(Object x);
>     const(Object) prop();
> }
> 
> Should "prop = new Object;" be legal or not?

Great question - quite an arrow in the suggestion's Achille's tendon. Since I can't write s.prop(s.prop), that property would be ruled out.

This may in fact be a good thing. It would be nice to provide generic code with the guarantee that if the save some property of an object, they can restore it later:

void generic(Node)(Node obj) {
   auto save = obj.indentLevel;
   scope(exit) obj.indentLevel = save;
   ...
}

> What if prop() was returning an invariant(Object) instead (for which there is no allowed implicit conversions)?

Under the proposal entity.prop(entity.prop), that won't compile. (I will note that invariant(Object) does convert to const(Object)).

> And what if there are two overloads for the setter: one with an invariant(Object) and one with an Object (mutable)? Are both allowed to be called with "="?

Under the proposal entity.prop(entity.prop), that would compile.

> Then you should probably disallow "ref" and "out" too in the setter.

Consider:

struct ArrayAppender {
    ref Array host;
    void host(ref Array);
}

That should work as property, ain't it?

(Walter has introduced ref returns; I'm already working on an alpha that has the feature. Of course I found bugs, too. :o))

> I'm not sure either about what you'll do once "shared" gets in the picture. (And ain't "scope" comming at some point too?)

Same test entity.prop(entity.prop) rules'em all.

> Also, what if one or the other is a static function?

Depends on how you call it. If you call it with an object in the first place, it works. If not, it doesn't.

> And what if you have an invariant(S2) and prop(Object x) is accessible (because it is invariant) while prop() is not (because not invariant), should the "=" syntax apply anyway?
> 
> ...
> 
> Basically what I want to illustrate is that another drawback of this solution is that it will probably not be as straitforward to define, and then understand, as it seem at first glance.

I think it is very easy to define and moderately hard to implement.

> The current syntax, with all its drawbacks, has the advantage of being very easy to understand: using "=" works all the time and it's the caller's responsibility to use the syntax which express better what he is doing. Personally, I have no problem with that.

I'm not crazy about the syntax, but if it has notable advantages, sure.


Andrei
September 29, 2008
On Mon, Sep 29, 2008 at 12:53 PM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> Bill Baxter wrote:
>>
>> Basically there's no convenient way to take the address of one of a getter/setter function pair, currently.  I think that should factor in the solution here.
>
> Overloading is the issue, and that's quite a different story.

It's a different, but intertwined story.

> If you know
> the exact type, you can take the address of something.
>
> struct S
> {
>    int foo() {}
>    void foo(int) {}
> }
>
> void main()
> {
>    S s;
>    void delegate(int) x = &s.foo;
>    int delegate() y = &s.foo;
> }

Having to write out "delegate(argumenttype) doesn't qualify as
"convenient" in my book.


--bb