September 29, 2008
"Andrei Alexandrescu" wrote
> Steven Schveighoffer wrote:
>> "Steven Schveighoffer" wrote
>>> "Andrei Alexandrescu" wrote
>>>>> Not really.  A function symbol by itself is the address of the function. A pointer symbol by itself is the address of the data it points to. It's the same.  I don't think you understood the detail that I stated, 'get address it's pointing to', not 'get address of the pointer'.
>>>> I understood very well. Your point is off by a mile, and getting only farther. Please understand how you are making an elementary mistake.
>>>>
>>>> Consider:
>>>>
>>>> int x;
>>>>
>>>> Then if I only use "x", I am getting x's value (an lvalue in fact), not its address. Now consider:
>>>>
>>>> int * x;
>>>>
>>>> Then if I only use "x", I am getting x's value, which is the address of an int.
>>>>
>>>> In neither case is it possible for x and &x to mean the same thing. For functions some really weird stuff happens:
>>>>
>>>> // this is C
>>>> #include <assert.h>
>>>> void foo() {}
>>> How does this make any difference to whether you can call a function without parentheses or not?  You are arguing that for a function defined as foo(), foo; should mean call function foo, and I'm arguing that it should be a syntax error.  Neither of us want the C behavior of evaluating to the function address.
>>>
>>> I think &foo should be the proper method of taking the address of a function, as it is in D.
>>
>> This is what happens when you can't explain yourself correctly.  Stop typing your vehement response about how I am an idiot right now :)  I realize my mistake in the statements above.
>>
>> I think the problem you cited with how foo is equivalent to &foo in C is bad.  I think we are getting sidetracked here, though.  The point I really want to state is that foo; shouldn't mean calling a function named foo.
>
> "State" is very accurate, because aside from stating the point you bring nothing to support it.

Again with the 'nothing to support it'.  If you're not going to listen to my arguments, or even acknowledge that I made them, then there really is no point to this discussion.

>> I don't think it should mean the address of the function, although it seems consistent with C pointers (IMO, it seems like &foo shouldn't even compile in C if foo means 'address of foo' already).  &foo sounds correct to me, "address of foo."
>
> No. It does NOT seem and is NOT even remotely consistent with C pointers because x is not the same thing as &x for all symbols in C except functions. Please work through this with books and test programs and all, until you fully understand it. It is important.

You are completely misunderstanding the scope of what I'm saying.  I'll just leave it at that.

>> Bottom line, I'm not defending the way C takes addresses of functions, but I think the requirement C has to use parentheses to call functions helps to clarify the intentions of the function author, and makes the code clearer.
>
> I agree, and I'd agree even more if you replaced "think" with "believe".


Think and believe to me both mean 'This is true in my mind'.  If you find one to be more appealing, so be it.

> I hope you also agree you have brought nothing of significance to support your thought/belief, while you have received plenty against.

Again, you are either not paying enough attention, or you refuse to accept my arguments as valid arguments.  I'll stop bashing my head against this brick wall of your inability to comprehend or refusal to accept my arguments, and just let this conversation die.

> I also agree that Sergey, Bill, and others did bring up good points in support for the required trailing "()". It would be more fertile to continue discussing those.

If the end result is that it gets fixed, I'm all for it.

-Steve


September 29, 2008
Steven Schveighoffer wrote:
> "Andrei Alexandrescu" wrote
>> No. It does NOT seem and is NOT even remotely consistent with C pointers because x is not the same thing as &x for all symbols in C except functions. Please work through this with books and test programs and all, until you fully understand it. It is important.
> 
> You are completely misunderstanding the scope of what I'm saying.  I'll just leave it at that.

You said something wrong. I understood what you said, I understood in what way it was wrong, and then I explained to you why it was wrong. How did I completely misunderstood the scope of what you are saying? In what way are pointers to functions consistent with other pointers in C, because in the way you mentioned, they aren't?

>>> Bottom line, I'm not defending the way C takes addresses of functions, but I think the requirement C has to use parentheses to call functions helps to clarify the intentions of the function author, and makes the code clearer.
>> I agree, and I'd agree even more if you replaced "think" with "believe".
> 
> 
> Think and believe to me both mean 'This is true in my mind'.  If you find one to be more appealing, so be it.

Thinking evokes process. Someone starts from a belief A, does some thinking, and reaches B. Then A is a belief and B is a thought resulting from that belief. (Euclidean axioms -> thinking -> sum of angles in a triangle is 180.) A philosopher (http://en.wikipedia.org/wiki/Constantin_Noica) wrote a book on logic that clarifies this relationship beautifully. His thesis is that logic essentially does not bring anything new, it's just a rehash of the beliefs stated in the axioms. To him logic was in a sense "conservation" because it only shuffled facts, not discovering new ones.

I _believe_ a language should obey the principles 1 and 2 (economy of syntax and giving syntactic priority to frequent use cases). Based on that belief, I _think_ D should drop the trailing parens.

I agree that somebody could _believe_ in some other principles that replace or override mine and then _think_ that keeping trailing parens is a good thing. Sergey did just that by showing us some very good examples when absence of trailing parens leads to ambiguity. Then there is room for meaningful discussion.

It is also possible that somebody simply _believes_ the trailing parens should be kept because they make code clearer to them. That's fair too, as long as there is understanding that that belief comes to a certain extent at odds with principles 1 and 2 (which anyone is free to simply not believe).

The only place that needs work is when the connection between belief and thought is unclear, or when the belief is simply wrong.


Andrei
September 29, 2008
Leandro Lucarella wrote:
> 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 =)

In Java for some reason the "Industry Standard" was that using non-constant public members was a Bad Thing (TM), and that getters and setters were the only acceptable thing.  For most things this was more or less just an annoying function set that looked like this:

class Foo {
	private String bar;
	public void setBar(String bar){
		this.bar=bar;
	}
	public String getBar(){
		return this.bar;
	}
}

Other times (rare, I never personally needed it) they were important to allow input checking (what we'd do like this:

void setBar(char[] bar)
 in {
	assert(bar.length>=3);
 } body {
	this.bar=bar;
 }

And some times (such as in certain Swing/AWT code) they would immediately affect something, eg. setting the text on a window would immediately send a message to the rendering engine and make a change happen the next time the window was painted.

Overall it wasn't a fault with Java, just with the Java programmers. They were so used to using getFoo/setFoo they just used it all the time.
September 29, 2008
Bill Baxter wrote:
> On Tue, Sep 30, 2008 at 4:08 AM, KennyTM~ <kennytm@gmail.com> wrote:
>> Bill Baxter wrote:
>>> On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer
>>> <schveiguy@yahoo.com> wrote:
>>>>> There is no ambiguity either case. You evaluate Stdout.newline. The
>>>>> evaluation yields a value of some type. Then you evaluate formatln
>>>>> against
>>>>> that value.
>>>> OK, then tell me what this does:
>>>>
>>>> x.y.z();
>>>>
>>>> Is y a property/field of x or a function call with no args?  I see a
>>>> benefit
>>>> to being able to understand a line of code without requiring lots of
>>>> extra
>>>> context.  I have to do less lookups of the source of a function or
>>>> property.
>>> The problem with this argument is that unless you disallow properties
>>> altogether, you still won't know whether y is actually a field or a
>>> call to a property method without looking it up.
>>>
>>> --bb
>> I think the distinction of with and without () is pretty stylistic, because
>> the same argument can even be applied to operator overloading (does a=b
>> means pointing the variable a to b, or calling a.opAssign(b)?)
>>
>> For me, I would use () if the function do modify the object itself.
> 
> I think the counter-argument to what I just said is that if properties
> had to be declared explicitly then at least we would know that .y is
> either a field or else something the designer *intended* to behave
> like a property.  Which does tell us something.  It most likely means
> that it's not an extremely heavy-weight operation, just a lightweight
> accessor with maybe a little logic on top.  That could be wrong, but
> in that case it's the class designer's fault for misleading us.

So the logic is:

"A field can be accessed with a.b, but not with a.b(). Therefore if someone writes a.b, it may be a field or code, but it someone writes a.b() it will always be code. Given that a field is cheap and code ranges from cheap to expensive, good style is to have a.b always refer to cheap stuff, and a.b() always refer to not-expected-to-be-cheap stuff."

I agree with that logic. It makes for a nice convention. (That convention is already broken by the built-in dup and sort, but let past mistakes be past mistakes.) My response to that is that concerns of keeping the language simple and of facilitating generic code are counterarguments that should be weighed in as well.

An experience anecdote - when I wrote Loki I went for the convention that if a type template argument only accepts built-in types it should be written "class T", otherwise it should be written "typename T". The convention was a failure: lack of compiler enforcement meant that "bugs" would be in there without being noticed (and readers would subsequently complain about inconsistencies), at times it was even unclear whether a template would accept certain built-in types or not and there was no way to check that, etc. A similar fate could happen for that convention. It could be nice, yes, but it could also become a nuisance ("was I expected to put parens here, or not? Hell, nobody seems to care anyway, at least this version of the library.").


Andrei
September 29, 2008
Chris R. Miller wrote:
> Leandro Lucarella wrote:
>> 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 =)
> 
> In Java for some reason the "Industry Standard" was that using non-constant public members was a Bad Thing (TM), and that getters and setters were the only acceptable thing.  For most things this was more or less just an annoying function set that looked like this:
> 
> class Foo {
>     private String bar;
>     public void setBar(String bar){
>         this.bar=bar;
>     }
>     public String getBar(){
>         return this.bar;
>     }
> }
> 
> Other times (rare, I never personally needed it) they were important to allow input checking (what we'd do like this:
> 
> void setBar(char[] bar)
>  in {
>     assert(bar.length>=3);
>  } body {
>     this.bar=bar;
>  }
> 
> And some times (such as in certain Swing/AWT code) they would immediately affect something, eg. setting the text on a window would immediately send a message to the rendering engine and make a change happen the next time the window was painted.
> 
> Overall it wasn't a fault with Java, just with the Java programmers. They were so used to using getFoo/setFoo they just used it all the time.

Great point. This getFoo/setFoo crap is so retarded. It is a good illustration of the road to hell paved with good intentions. Yes, it's good to give the object a crack to enforce its invariant upon setting and maybe to compute/adjust a value upon getting. Here things took the wrong turn in Java - instead of unifying fields with properties, they postulated there must be no public fields.


Andrei
September 30, 2008
On 2008-09-29 09:48:31 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

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

Ok, lets look back at what I wrote and what you replied in your previous post:

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

But "s2.prop(s2.prop)" wouldn't compile in the context where "s2.prop = 42" is called, because s2.prop is private. You say say that it doesn't matter, so I take it there is an exception for protection attributes. Or perhaps you're checking if "s2.prop(s2.prop)" compiles in another context I don't know about.

About the "= void" thing, my interpretation is that it's a way to tell the compiler that this function doesn't exist (like making a copy constructor private in C++), but I admit I'm not sure what I'm talking about here. Anyway, my interpretation is that if you have:

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

S2 s2;
s2.prop;

the last line wouldn't compile since s2.prop can't be called. How can "s2.prop(s2.prop)" compile then?

As for the last one, I think I decided myself that it wouldn't be very good for a function to be a property or not depending on the constness of the object. My previous examples were badly written however. Imagine you have this:

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

This design is probably a little strange, but lets look at it anyway:

S2 s2;
const(S2) constS2;
invariant(S2) invariantS2;

s2.prop = 1; // compiles because s2.prop(s2.prop) compiles.
constS2.prop = 1; // does not compile because const(S2).prop isn't defined
invariantS2.prop = 1; // compiles because invariantS2.prop(invariantS2.prop) compiles

Basically, whether prop(int x) is a property or not depends on the various qualifiers given to prop() and the constness of S2. I think I wrongly assumed you'd want to avoid this since I had in mind you'd not forbid the proprety syntax when given a private accessor.

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

September 30, 2008
On 2008-09-29 12:55:18 -0400, "Steven Schveighoffer" <schveiguy@yahoo.com> said:

> Stdout.newline.formatln("Hi there {}", var);
> 
> Is newline a property of Stdout, on which I'm calling formatln, or is
> newline a function that I'm calling, and then using the result of that
> function to call formatln?  Compare with:
> 
> Stdout.newline().formatln("Hi there {}", var);
> 
> There is no ambiguity.  newline is clearly a function, not a property.

Interesting. To me both of these things have exactly the same meaning. Perhaps it's because I'm used to D, but I think it's more because I can't count how many times I've seen this pattern in C++:

SomeSimgleton::Instance().doSomething();

where Instance() is simply an accessor.

In my opinion, "newline" isn't a good function name for what it does because it has no verb. Having no verb makes it look like an accessor much more than the absence or presence of parenthesis.

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

September 30, 2008
Michel Fortin wrote:
> On 2008-09-29 09:48:31 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:
> 
>>>     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.
> 
> Ok, lets look back at what I wrote and what you replied in your previous post:
> 
>>> 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.)
> 
> But "s2.prop(s2.prop)" wouldn't compile in the context where "s2.prop = 42" is called, because s2.prop is private. You say say that it doesn't matter, so I take it there is an exception for protection attributes. Or perhaps you're checking if "s2.prop(s2.prop)" compiles in another context I don't know about.

It's my fault, I misunderstood you. Sorry. This case could go either way, and I agree it does add complexity to the feature. If the feature is thought as a syntactic rewrite (my opinion), then s2.prop = 42 should always rewrite into s2.prop(42), and then whether that works or not should be left to the protection in vigor for S2.prop(int) - public in this case. But it could also be argued that the access check should be done first, in which case the rewrite won't happen unless the code in question has access to S2's privates. Probably the latter is actually the better decision.

> About the "= void" thing, my interpretation is that it's a way to tell the compiler that this function doesn't exist (like making a copy constructor private in C++), but I admit I'm not sure what I'm talking about here.

Yes, that is the intent.

> Anyway, my interpretation is that if you have:
> 
> struct S2
> {
>     void prop(int x);
>     int prop() = void;
> }
> 
> S2 s2;
> s2.prop;
> 
> the last line wouldn't compile since s2.prop can't be called. How can "s2.prop(s2.prop)" compile then?

It should not compile. Making a function = void actively makes it unreachable and unusable in all contexts.

> As for the last one, I think I decided myself that it wouldn't be very good for a function to be a property or not depending on the constness of the object. My previous examples were badly written however. Imagine you have this:
> 
> struct S2
> {
>     int prop();
>     int prop() invariant;
>     void prop(int x) const;
> }
> 
> This design is probably a little strange, but lets look at it anyway:
> 
> S2 s2;
> const(S2) constS2;
> invariant(S2) invariantS2;
> 
> s2.prop = 1; // compiles because s2.prop(s2.prop) compiles.
> constS2.prop = 1; // does not compile because const(S2).prop isn't defined
> invariantS2.prop = 1; // compiles because invariantS2.prop(invariantS2.prop) compiles
> 
> Basically, whether prop(int x) is a property or not depends on the various qualifiers given to prop() and the constness of S2. I think I wrongly assumed you'd want to avoid this since I had in mind you'd not forbid the proprety syntax when given a private accessor.

Indeed, whether or not something can be used as a property will depend on the qualifiers in vigor.


Andrei
September 30, 2008
KennyTM~ Wrote:

> Bill Baxter wrote:
> > On Tue, Sep 30, 2008 at 3:36 AM, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> >>> There is no ambiguity either case. You evaluate Stdout.newline. The evaluation yields a value of some type. Then you evaluate formatln against that value.
> >> OK, then tell me what this does:
> >>
> >> x.y.z();
> >>
> >> Is y a property/field of x or a function call with no args?  I see a benefit to being able to understand a line of code without requiring lots of extra context.  I have to do less lookups of the source of a function or property.
> > 
> > The problem with this argument is that unless you disallow properties altogether, you still won't know whether y is actually a field or a call to a property method without looking it up.
> > 
> > --bb
> 
> I think the distinction of with and without () is pretty stylistic, because the same argument can even be applied to operator overloading (does a=b means pointing the variable a to b, or calling a.opAssign(b)?)
> 
> For me, I would use () if the function do modify the object itself.
> 

I assume you'd also want parens if the function modified global state? If so, I believe you'd want parens with all impure member function calls.

Obviously, we're talking about property getters and not setters. I was told once already that such things are off topic :) I think it's relevant to the spirit of the conversation.
September 30, 2008
On 2008-09-29 23:12:44 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> said:

> Indeed, whether or not something can be used as a property will depend on the qualifiers in vigor.

And I think this is why binding the availability of the proprety syntax of the setter to the existance and visibility of a getter is not a good idea.

Basically, doing this makes the "()" syntax safer, and the "=" version more fragile, as the later could become non-functionnal in all kinds of situations where the getter becomes unavailable (removal, deprecation, change in protection, change in constness of the parent, etc.). It means that if I remove or change the visibility of a getter in a program (because it has been found to be erroneous, dangerous, and/or badly used), I will have to change all the calls to the corresponding setter to use the parenthesis form.

This can be a nuisance while refactoring. It also means that I can't just remove the getter and attempt to recompile to check if it's called somewhere without inducing compilation errors where the setter is used. And I'm sure it'll encourage some silly ones who want to write write-only properties to add a dummy getter that throws an exception, disregarding static checking. I can already see some people recommanding always using the parenthesis syntax when calling setters because then you know you won't have to change it later when you refactor your code; I know I would consider it if I got biten by this, after all we're already doing this in other languages and it's not that painfull.

So I think this idea brings unnecessary coupling between functions, with more drawbacks than benefits. Don't mistake me, I liked your idea at first, especially because I'm not fond of adding a new syntax just for diallowing another, but now I realise don't like its implications of the implicit coupling of the setter with the getter.

I believe explicit coupling would be better, and that probably means an explicit property syntax as others have requested. But then again, I don't mind much if things stay like they are.

- - -

Another funny example:

module a;
void func(int a);

module b;
int func();

module c;
import a;
import b;
void main()
{
	func(func); // compiles... so
	func = 5; // compiles too!
}

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