January 11, 2009

Nick Sabalausky wrote:
> "Daniel Keep" <daniel.keep.lists@gmail.com> wrote in message news:gk9v33$1bvu$1@digitalmars.com...
>> [snip]
>> 
>> What would be the point of making a special syntax when you can already do that? :P
>>
> 
> Same reason we have "while" and "foreach" in addition to "for".
> 

How is this:

property int foo
{
    get
    {
        return compute_value();
    }
}

Appreciably better than this:

int foo()
{
    return compute_value();
}

We have for and foreach because iteration is a *very* common use for a loop, and you can screw it up when using while.  In this case, having property syntax for a computed field doesn't buy you anything whatsoever apart from more typing.

Yes, property syntax can simplify some cases, but this isn't one of them.

  -- Daniel

P.S.  Please don't get my hopes up by quoting my entire post and then only replying to one line; I thought I'd get to argue about my "80%" if nothing else.  :P
January 11, 2009
Daniel Keep wrote:
> Yes, property syntax can simplify some cases, but this isn't one of them.

One good distinction properties and normal functions ought to make is
that functions should never be called without (), and properties should
never have () unless it has a function type.

Current syntax allows crazy things like:

----------
	exit = 1;	// it is not an assignment
	x = toString = getenv = "PATH";	// creepy, but valid D

	if (fork == 1)	// not comparing the value of a variable
----------

Also, currently, in D, if you have a property of a delegate type, you will have a mess.

----------
	int func2() { return 42; }

	/* this is a property */
	int function() prop() {
		return &func2;
	}

	void main() {
		auto x = prop;		// ok, x is a func ptr
		auto y = prop();	// unexpected: y is not int :-(
		static assert (is(typeof(y) == int));
	}
----------

With properties, you forbid the above syntaxes.

So, what is "better" about properties is not shorter syntax, but giving proper semantics for a given symbol.

Also, properties can be part of interfaces, enforcing derived classes to implement them in a given way.
January 11, 2009
== Quote from Miles (_______@_______.____)'s article
> Daniel Keep wrote:
> > Yes, property syntax can simplify some cases, but this isn't one of them.
> One good distinction properties and normal functions ought to make is that functions should never be called without ()

Why?  This is really superfluous.  I for one think that code is often more readable without the annoying empty parentheses around functions that don't take any arguments.  This is especially true for member functions of classes and structs, even moreso when chaining them such as foo.bar.baz vs. foo().bar().baz().

> , and properties should
> never have () unless it has a function type.
> Current syntax allows crazy things like:
> ----------
> 	exit = 1;	// it is not an assignment
> 	x = toString = getenv = "PATH";	// creepy, but valid D
> 	if (fork == 1)	// not comparing the value of a variable
> ----------

So?  The spec is not there to prevent people from doing stupid things (making it harder to do stupid things *by accident* is a reasonable goal, but we're all consenting adults here).  Anyone who would actually do stuff like this on purpose in their code is an idiot and deserves what they get.  One could just as easily do some pretty stupid things with operator overloading, casts, templates, you name it.  Just because stupid things *can* be done with a feature doesn't mean the feature should be removed.

> Also, currently, in D, if you have a property of a delegate type, you will have a mess.
> ----------
> 	int func2() { return 42; }
> 	/* this is a property */
> 	int function() prop() {
> 		return &func2;
> 	}
> 	void main() {
> 		auto x = prop;		// ok, x is a func ptr
> 		auto y = prop();	// unexpected: y is not int :-(
> 		static assert (is(typeof(y) == int));
> 	}
> ----------
> With properties, you forbid the above syntaxes.
> So, what is "better" about properties is not shorter syntax, but giving
> proper semantics for a given symbol.
January 11, 2009
dsimcha wrote:
>  I for one think that code is often more
> readable without the annoying empty parentheses around functions that don't take
> any arguments.

It introduces ambiguities, and unexpected behavior, as I exemplified in the previous message.

Parentheses (when following an identifier) has a precise meaning of calling the named function. That is something most modern procedural languages do, it is something programmers are used to.

I see no advantage at giving the privilege to no-argument functions of being called without parentheses, while at the same time you lose the ability to distinguish between _calling_ the function from _referring_ to it.

> This is especially true for member functions of classes and structs,

Aren't these member functions good candidates to be made into real properties?

If you want or need to use an identifier without parentheses, you have the option of making it into a property. Very simple.

> even moreso when chaining them such as foo.bar.baz vs. foo().bar().baz().

From your example, it appears that the purpose of foo and bar, and perhaps baz are to return instances, so they could just be turned into properties, and have the exact syntax you expect.

> So?  The spec is not there to prevent people from doing stupid things (making it harder to do stupid things *by accident* is a reasonable goal, but we're all consenting adults here).  Anyone who would actually do stuff like this on purpose in their code is an idiot and deserves what they get.  One could just as easily do some pretty stupid things with operator overloading, casts, templates, you name it.  Just because stupid things *can* be done with a feature doesn't mean the feature should be removed.

You seem to be missing the point. In fact, you ignored the most important example in my previous message that shows how the current D's property syntax can be damaging.

Also, your reasoning fails to take into account the difference between _allowing_ a certain syntax, and _incentivating_ bad usage.
January 11, 2009
On Sun, Jan 11, 2009 at 11:27 AM, dsimcha <dsimcha@yahoo.com> wrote:
> == Quote from Miles (_______@_______.____)'s article
>> Daniel Keep wrote:
>> > Yes, property syntax can simplify some cases, but this isn't one of them.
>> One good distinction properties and normal functions ought to make is that functions should never be called without ()
>
> Why?  This is really superfluous.  I for one think that code is often more readable without the annoying empty parentheses around functions that don't take any arguments.  This is especially true for member functions of classes and structs, even moreso when chaining them such as foo.bar.baz vs. foo().bar().baz().
>
>> , and properties should
>> never have () unless it has a function type.
>> Current syntax allows crazy things like:
>> ----------
>>       exit = 1;       // it is not an assignment
>>       x = toString = getenv = "PATH"; // creepy, but valid D
>>       if (fork == 1)  // not comparing the value of a variable
>> ----------
>
> So?  The spec is not there to prevent people from doing stupid things (making it harder to do stupid things *by accident* is a reasonable goal, but we're all consenting adults here).  Anyone who would actually do stuff like this on purpose in their code is an idiot and deserves what they get.  One could just as easily do some pretty stupid things with operator overloading, casts, templates, you name it.  Just because stupid things *can* be done with a feature doesn't mean the feature should be removed.

I think the point is not that someone would do these things deliberately, it's that they might do them inadvertently.  It's not that hard to end up with property names that sound like verbs or vice versa.  Then the user of your code guesses that delay = true will be how to turn the 'delay' property on, and it compiles. But it turns out it it actually does a delay right then, and was really delay(bool yieldThread) or something like that, a parameterized action.

All this just means one more thing you have to keep in mind while you're developing.   You have to keep asking yourself "could somebody mistake this for a property?" every time you write a function with a short name.  Or "could someone mistake this for an action?" when you think you're writing a property.

Here's an issue I haven't seen discussed:  What's the type of property foo?  If a property can't be treated as a function, then it's type can't be the same as a function.   It also can't be an int or whatever the referent type is.  When you do __traits mojo and iterate over the members of the class, what should it return for a property?  Not a killer I suppose, but there are probably more issues like that that would need to be dealt with that are more important than what the declaration syntax looks like.

--bb
January 11, 2009
On Sun, Jan 11, 2009 at 12:01 PM, Miles <_______@_______.____> wrote:
> dsimcha wrote:
>>  I for one think that code is often more
>> readable without the annoying empty parentheses around functions that don't take
>> any arguments.
>
> It introduces ambiguities, and unexpected behavior, as I exemplified in the previous message.
>
> Parentheses (when following an identifier) has a precise meaning of calling the named function. That is something most modern procedural languages do, it is something programmers are used to.
>
> I see no advantage at giving the privilege to no-argument functions of being called without parentheses, while at the same time you lose the ability to distinguish between _calling_ the function from _referring_ to it.

Not sure what you mean by that.  It's just  foo vs &foo in most cases. ...Except for some cases like where you're trying to return a delegate.

I agree that 'except for' is not very pleasant.

>> This is especially true for member functions of classes and structs,
>
> Aren't these member functions good candidates to be made into real properties?
>
> If you want or need to use an identifier without parentheses, you have the option of making it into a property. Very simple.

He may want to retain the ability to pass it as a delegate to some other function too.  If you make properties distinct from functions, then I don't think this can be allowed.  So you'd need to have the property and a function that wraps the property.  Then you've just made more work for yourself.    Hmm, maybe properties should be allowed to be turned into delegates.


--bb
January 11, 2009
I'm starting to think that this properties thing is becoming a hopelessly complicated solution to a very simple problem.  The issue that brought this to a head in the first place was foo = foo + 1 vs. foo += 1.  Given that solving this case properly seems like a rather difficult problem, the solution of just defining foo += 1 to mean foo = foo + 1 (which is what was originally proposed before issues about properties of user-defined types were brought up) is starting to look appealing.  Yes, this might be inefficient on user defined types w/ operator overloading, but so is the equivalent in other languages:

SomeObject temp = myClass.getFoo();
myClass.setFoo(temp + 1);

I figure the vast majority of cases are going to be primitive types anyhow (mostly ints), and if someone defines operator overloads such that foo += 1 produces totally different observable behavior than foo = foo + 1, that's just too ridiculously bad a design to even take seriously.  What do you think?  Is it worth ignoring a few hard cases in exchange for solving most cases simply and elegantly and without adding any new constructs?
January 11, 2009
On Sun, Jan 11, 2009 at 12:52 PM, dsimcha <dsimcha@yahoo.com> wrote:
> I'm starting to think that this properties thing is becoming a hopelessly complicated solution to a very simple problem.  The issue that brought this to a head in the first place was foo = foo + 1 vs. foo += 1.  Given that solving this case properly seems like a rather difficult problem, the solution of just defining foo += 1 to mean foo = foo + 1 (which is what was originally proposed before issues about properties of user-defined types were brought up) is starting to look appealing.  Yes, this might be inefficient on user defined types w/ operator overloading, but so is the equivalent in other languages:
>
> SomeObject temp = myClass.getFoo();
> myClass.setFoo(temp + 1);
>
> I figure the vast majority of cases are going to be primitive types anyhow (mostly ints), and if someone defines operator overloads such that foo += 1 produces totally different observable behavior than foo = foo + 1, that's just too ridiculously bad a design to even take seriously.  What do you think?  Is it worth ignoring a few hard cases in exchange for solving most cases simply and elegantly and without adding any new constructs?

Well, foo+=1 is not the only issue.  It may have been the one that brought it to a head this time, but the issue has come up in the past with the focus on fixing the ambiguities around allowing functions to be called without ().  Mainly I think that ambiguity relates to properties that return callable things.   If foo is such a property then it's not clear how to call the thing returned by foo.  I think the only way to make such things make sense and be consistent is to disallow the implicit function call stuff.

--bb
January 11, 2009
Miles wrote:
> Daniel Keep wrote:
>> Yes, property syntax can simplify some cases, but this isn't one of them.
> 
> One good distinction properties and normal functions ought to make is
> that functions should never be called without (), and properties should
> never have () unless it has a function type.
> 
> Current syntax allows crazy things like:
> 
> ----------
> 	exit = 1;	// it is not an assignment
> 	x = toString = getenv = "PATH";	// creepy, but valid D
> 
> 	if (fork == 1)	// not comparing the value of a variable
> ----------

Walter and I see eye to eye that a possible solution would be to only allow the a = b syntax as an alternative for a(b) only if there's also a function a(). All of the above can be fixed within that framework.

> Also, currently, in D, if you have a property of a delegate type, you
> will have a mess.
> 
> ----------
> 	int func2() { return 42; }
> 
> 	/* this is a property */
> 	int function() prop() {
> 		return &func2;
> 	}
> 	
> 	void main() {
> 		auto x = prop;		// ok, x is a func ptr
> 		auto y = prop();	// unexpected: y is not int :-(
> 		static assert (is(typeof(y) == int));
> 	}
> ----------
> 
> With properties, you forbid the above syntaxes.

Well the above syntaxes could be forbidden other ways too.


Andrei
January 11, 2009
"Miles" <_______@_______.____> wrote in message news:gkbk5p$14gr$1@digitalmars.com...
>
> So, what is "better" about properties is not shorter syntax, but giving proper semantics for a given symbol.
>

I'd say the point of properties is both a matter of clean implementation syntax and proper calling semantics/syntax. In the case of the calling semantics/syntax, I see it as essentially boiling down to an issue of "nouns vs verbs" and proper abstraction.

The way I see it: Variables are the programming world's nouns, and functions are the programming world's verbs. Objects (nouns) have attributes (nouns) and are capable of doing things (verbs). Thus, things that are noun-like and can be thought of as an attribute of an object should be accessed using noun/variable syntax (this means no parentheses), and things that are conceptualized as actions the object can perform should be invoked using verb/function syntax (this means parentheses). Although if you're merely *referring* to a verb/function (as in the phrase "the act of walking"), then you're using the verb as a noun and should refer to the verb with the standard noun syntax (no parens).

Example of proper design strategy:
We want to programmatically represent the noun of "color". First thing we
need is a type. There's no built-in, so we need to define a custom type, a
class. Next thing we need is a list of attributes and actions associated
with "color":

Actions (things color can do):
Color isn't a physical object so there's not much it can actually do:
- Inspire an Artist (Artist being the grammatical "direct object", and
therefore the parameter)

Attributes (things color has):
- Hue
- Saturation
- Brightness/Darkness
- Red component
- Green component
- Blue component
- Yellow component
- Cyan component
- Magenta component
- Transparency/Opaqueness
- Shininess
- etc...

Now to translate into (psuedo)code:

void inspire(Artist artist) { /* magic code here */ }
val hue;
val saturation;
val blackness;
val red;
val green;
val blue;
val yellow;
val cyan;
val magenta;
val transparency
val shininess;
("val", of course, being a type representing the range of "max".."min")

Now at this point any sane programmer would see the redundancy (and potential for invalid, self-contradicting state) in storing HSV, RGB, *and* CMYK. They'd then probably choose a single internal format (we'll say RGB), and decide to calculate all of the redundant attributes from that. This is good design.

But here's the part where many people go wrong. They say "Since I'm calculating HSVCM and Y, that means 'function'" (And yes, it does mean "function", but only on the *implementation* side.) Then they do something like this:

val getHue();
val getSaturation();
val getBlackness();
val red; // Might be changed to getRed() for consistency
val green;
val blue;
val getYellow();
val getCyan();
val getMagenta();

Bad programmer! Bad, bad, bad programmer! Hue and Yellow aren't actions of color, they're attributes! Nouns, nouns, nouns! Turning them into verbs by sticking "get" in front just made a bastardized mockery out of high-level-language abstraction. Not only that but the implementation detail of "some of these are calculated instead of stored internally" has leaked out into the interface and slapped the idea of encapsulation in the face.

At this point, D comes in and tries to solve the problem with omittable parens on functions. But that puts the onus on the caller. So what, the caller is now supposed to be defining the interface? That's dumb because the author of the class has already decided whether a particular member is a noun or a verb by naming it "red" instead of "getRed" or vice versa.

If you've got something like "red component" that is conceptually understood as a noun, it should be accessed with noun syntax. Call it "good redundancy" (*nudge* *nudge* Walter?). Whether or not the class author decides to *implement* it as a computation or a simple memory access should not impact accessing it. As it is now, whenever a conceptual attribute is switched between variable and "property", the legality of sticking () on the end of it flip-flops. How is that desirable behavior?