September 29, 2008
Bill Baxter wrote:
> 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.

You first used "can't". That doesn't qualify as "can" in my book :o). The "can't" made me honestly think you didn't know about the ability to take the address of an overloaded function/method into a specifically-typed function or delegate respectively. One more instance of the proverbial no good deed that goes unpunished... :o)


Andrei
September 29, 2008
Chris R. Miller wrote:
> 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.

That was an interesting thread. I am glad you decided to learn STL, you will derive a lot of good stuff from there.

Andrei
September 29, 2008
On Mon, Sep 29, 2008 at 1:16 PM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> Bill Baxter wrote:
>>
>> 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.
>
> You first used "can't". That doesn't qualify as "can" in my book :o). The "can't" made me honestly think you didn't know about the ability to take the address of an overloaded function/method into a specifically-typed function or delegate respectively. One more instance of the proverbial no good deed that goes unpunished... :o)

Yeh, sorry about that.  I vaguely remembered that there was some way to do it, but that it wasn't something I would want to be forced to use whenever I wanted to get a delegate pointer.

--bb
September 29, 2008
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.
> 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.

Or just issue a warning if property syntax is used without the "property" keyword.

But hey, since all of you dislike warnings, this is surely not a solution, right?
September 29, 2008
On 2008-09-29 00:07:28 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> 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.

Ok, so you determine if a function can be called by "=" before checking for access rights.

> (Also Walter wants to introduce the fun() = void syntax to disallow copying; that might be useful in this context too.)

Ok, great. What do you do with this by the way:

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

Did the author want to eventually replace "prop()" with "prop() = void" to keep allowing "prop = 1", or does he want to simply remove it and disallow the "prop = 1" syntax?

I find it somewhat troubling that removing an unused function (especially if private) could invalidate code just because it is calling another overloaded function with the property syntax. I mean, now to know if you can safely remove a function, you have to check if anyone is using any of corresponding overloaded "setters" 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;
>     ...
> }

Hum, but if the setter is private, protected, or package, or if it just doesn't exist, that guarenty doesn't exist.

Another interesting case:

struct S5
{
	int prop();
	void prop(int x) const;
}

Not sure what to do with it though, but it certainly breaks your guarenty when S5 is invariant. So should the property syntax "s5.prop = x;" works only when S5 is mutable, or not at all, or in all cases?

Then what about this strange one:

struct S5
{
	int prop();
	string prop() invariant;
	void prop(int x) const;
}

Should the property syntax works when for S5, const(S5), invariant(S5) ?

>> 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.

Seems coherant.

>> 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))

And "out"?

>> 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.

Seems coherant, again.

>> 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.

Ok, let's clarify that:

struct S1
{
	int prop();
	static void prop(int x);
}

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

S1 s1;
s1.prop = 1; // ok, because s1.prop returns int.
S1.prop = 1; // error, since S1.prop does not exist.

S2 s2;
s2.prop = 1; // ok, because s1.prop returns int.

Is that right?

>> 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?

No answer to that one? Take this case:

struct S3
{
	int prop() invariant;
	void prop(int x);
}

S3 s3;
s3.prop = x; // error?

struct S4
{
	int prop();
	void prop(int x) invariant;
}

invariant(S4) s4;
s4.prop = x; // error?

I guess disallowing this wouldn't be too bad, since it's pretty ridiculous 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 thing is that you can't explain everything just by checking if you can compile "s.prop(s.prop)". If one of the two is private, you said it would be allowed anyway, it becomes "s.prop(s.prop) disregarding applicable protection attributes". Then you said that it the getter could be "= void", but obviously "s.prop(s.prop) wouldn't compile in that case, so you'd have to change it to "s.prop(s.prop) compiles disregarding applicable protection attributes and whether or the the getter is '= void'". Then you'll need to add something about the constness of the struct or class the function is scoped in, so it becomes:

	s.prop(s.prop) compiles disregarding applicable protection attributes,
	disregarding whether or the the getter is '= void', and disregarding
	the constness of the enclosing scope (class or struct).

That doesn't sound so simple, and I probably got it wrong. Can you define it better?

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

September 29, 2008
Chris R. Miller, el 28 de septiembre a las 19:50 me escribiste:
> >>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.

In Java you have the exact same problem. Since it doesn't support
properties, and people don't want to chage thousands of LOC if they need
to change a simple member variable to a function, they use functions just
in case. So when you look at a function, for example, getSomething() you
still wonder: is this just a simple return something;? Or is *really*
a function?

The problem is the same, you just have to use uglier syntax =)

-- 
Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/
----------------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------------
CHINO ATRAPA COTORRAS
	-- Crónica TV
September 29, 2008
Michel Fortin wrote:
> Ok, great. What do you do with this by the way:
> 
> struct S2
> {
>     void prop(int x);
>     deprecated int prop();
> }
> 
> Did the author want to eventually replace "prop()" with "prop() = void" to keep allowing "prop = 1", or does he want to simply remove it and disallow the "prop = 1" syntax?
> 
> I find it somewhat troubling that removing an unused function (especially if private) could invalidate code just because it is calling another overloaded function with the property syntax. I mean, now to know if you can safely remove a function, you have to check if anyone is using any of corresponding overloaded "setters" too.

I understand. I find it somewhat troubling too.

>> void generic(Node)(Node obj) {
>>     auto save = obj.indentLevel;
>>     scope(exit) obj.indentLevel = save;
>>     ...
>> }
> 
> Hum, but if the setter is private, protected, or package, or if it just doesn't exist, that guarenty doesn't exist.

That means indentLevel is not a property and consequently "generic" cannot work with that object.

> Another interesting case:
> 
> struct S5
> {
>     int prop();
>     void prop(int x) const;
> }
> 
> Not sure what to do with it though, but it certainly breaks your guarenty when S5 is invariant. So should the property syntax "s5.prop = x;" works only when S5 is mutable, or not at all, or in all cases?

It's very simple. If you have "entity.prop = x" first you check for "entity.prop(entity.prop)". Does that compile? If so, you rewrite "entity.prop = x" as entity.prop(x) and continue from there. In your question s5 can be invariant, const, or mutable. In either case the usual judgment applies.

> Then what about this strange one:
> 
> struct S5
> {
>     int prop();
>     string prop() invariant;
>     void prop(int x) const;
> }
> 
> Should the property syntax works when for S5, const(S5), invariant(S5) ?

Same answer.

>>> 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))
> 
> And "out"?

struct S {
    ref int prop();
    void prop(out int);
}

s.prop(s.prop) does compile, so s.prop = x; should be allowed. However, I agree it does something counterintuitive (modify the right-hand side).

> Ok, let's clarify that:
> 
> struct S1
> {
>     int prop();
>     static void prop(int x);
> }
> 
> struct S2
> {
>     static int prop();
>     void prop(int x);
> }
> 
> S1 s1;
> s1.prop = 1; // ok, because s1.prop returns int.
> S1.prop = 1; // error, since S1.prop does not exist.
> 
> S2 s2;
> s2.prop = 1; // ok, because s1.prop returns int.
> 
> Is that right?

Yah.

>>> 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?
> 
> No answer to that one? Take this case:
> 
> struct S3
> {
>     int prop() invariant;
>     void prop(int x);
> }
> 
> S3 s3;
> s3.prop = x; // error?

s3.prop(s3.prop) would not compile, so neither does the assignment form.

> struct S4
> {
>     int prop();
>     void prop(int x) invariant;
> }
> 
> invariant(S4) s4;
> s4.prop = x; // error?
> 
> I guess disallowing this wouldn't be too bad, since it's pretty ridiculous anyway.

This would not compile because s4.prop(s4.prop) does not compile.

> The thing is that you can't explain everything just by checking if you can compile "s.prop(s.prop)". If one of the two is private, you said it would be allowed anyway, it becomes "s.prop(s.prop) disregarding applicable protection attributes". Then you said that it the getter could be "= void", but obviously "s.prop(s.prop) wouldn't compile in that case, so you'd have to change it to "s.prop(s.prop) compiles disregarding applicable protection attributes and whether or the the getter is '= void'". Then you'll need to add something about the constness of the struct or class the function is scoped in, so it becomes:
> 
>     s.prop(s.prop) compiles disregarding applicable protection attributes,
>     disregarding whether or the the getter is '= void', and disregarding
>     the constness of the enclosing scope (class or struct).
> 
> That doesn't sound so simple, and I probably got it wrong. Can you define it better?

With this part I completely disagree. You artificially create complexity by bringing other rules into play that were in place before this discussion. By the same token you could add "s.prop(s.prop) compiles if s and prop are properly formed identifiers" and claim that that is an issue with properties.

The protection attributes do their job. The = void does (will do, actually) its job. Qualifiers do their job. It's business as usual. I did not invent those rules for the sake of properties.


Andrei
September 29, 2008
Mon, 29 Sep 2008 19:49:49 +0800,
KennyTM~ wrote:
> But hey, since all of you dislike warnings, this is surely not a solution, right?

Right now warnings in DMD are either not shown or are considered errors. I don't know if this is 2.019 only, but there are effectively no warnings: either there are less errors or more errors.
September 29, 2008
Andrei Alexandrescu wrote:
> Sergey Gromov wrote:
>> In article <gbjihf$2803$1@digitalmars.com>, frank@youknow.what.todo.interNETz says...
>>> Andrei Alexandrescu wrote:
>>>> Steven Schveighoffer wrote:
>>>>> You are assuming that the C language decision to require parentheses for all functions was a design mistake.  I would argue that the design was on purpose and correctly served that purpose.  The purpose was to remove ambiguity when faced with understanding code without all the context.
>>>> I have stated my assumption and its basis. What is the basis of yours?
>>> Yum. I think the problem is that when C was designed, K&R didn't consider
>>> the use of classes with properties using getter and setter methods.
>>> Therefore, it made sense to have the naked function name denote the
>>> function pointer, and make braces following an identifier the mark of a
>>> function call. I initially had some trouble accepting the explicit & in D
>>> to get the function pointer myself.
>>> I wouldn't go so far as to call that property of C a design mistake in
>>> light of the actual intensions of K&R. But I definitely would defend
>>> the choice of a language designer to change that feature in light of
>>> more modern programming paradigms.
>>
>> Functions in D are first-class values.  It's awkward that a first-class value cannot be accessed by its identifier.
> 
> I agree. But then use of functions for invocation dwarfs use of
> functions as first-class values, so I think requiring & for the latter
> is a sensible engineering decision.


By the way, D supports declarations of function type (not function pointer), though it's not documented in the spec. They always struck me as a really odd feature in C, and they're pretty confusing for newbies:

typedef void Func(int);

Then you get oddities

Func * f = &foo; // OK.

Func f = foo; // You might expect this to work, but it makes no sense.

This is a case where you do not intuitively expect "foo" (with no &) to be a function pointer. You might as well make it a function invocation.

It's pretty interesting to fool around with these fellas in an is() expression.

> Besides, it's more efficient to use them as alias parameters, so why not
> encourage that too. And aliases do not need a "&" :o).

Indeed.
September 29, 2008
Don wrote:
> Andrei Alexandrescu wrote:
>> Sergey Gromov wrote:
>>> In article <gbjihf$2803$1@digitalmars.com>, frank@youknow.what.todo.interNETz says...
>>>> Andrei Alexandrescu wrote:
>>>>> Steven Schveighoffer wrote:
>>>>>> You are assuming that the C language decision to require parentheses for all functions was a design mistake.  I would argue that the design was on purpose and correctly served that purpose.  The purpose was to remove ambiguity when faced with understanding code without all the context.
>>>>> I have stated my assumption and its basis. What is the basis of yours?
>>>> Yum. I think the problem is that when C was designed, K&R didn't consider
>>>> the use of classes with properties using getter and setter methods.
>>>> Therefore, it made sense to have the naked function name denote the
>>>> function pointer, and make braces following an identifier the mark of a
>>>> function call. I initially had some trouble accepting the explicit & in D
>>>> to get the function pointer myself.
>>>> I wouldn't go so far as to call that property of C a design mistake in
>>>> light of the actual intensions of K&R. But I definitely would defend
>>>> the choice of a language designer to change that feature in light of
>>>> more modern programming paradigms.
>>>
>>> Functions in D are first-class values.  It's awkward that a first-class value cannot be accessed by its identifier.
>>
>> I agree. But then use of functions for invocation dwarfs use of
>> functions as first-class values, so I think requiring & for the latter
>> is a sensible engineering decision.
> 
> 
> By the way, D supports declarations of function type (not function pointer), though it's not documented in the spec. They always struck me as a really odd feature in C, and they're pretty confusing for newbies:
> 
> typedef void Func(int);
> 
> Then you get oddities
> 
> Func * f = &foo; // OK.
> 
> Func f = foo; // You might expect this to work, but it makes no sense.
> 
> This is a case where you do not intuitively expect "foo" (with no &) to be a function pointer. You might as well make it a function invocation.
> 
> It's pretty interesting to fool around with these fellas in an is() expression.

Heh. That is eerily similar to the way C handles it - here's an example:

// this is C
typedef void foo(int);

int main()
{
    foo bar;
    foo baz;
    bar = baz;
}

The typedef goes through. The definitions of bar and baz go through (and are simply equivalent to an extern declaration!!!) The assignment won't go through because, bar is not an lvalue.


Andrei