January 11, 2009
"Bill Baxter" <wbaxter@gmail.com> wrote in message news:mailman.378.1231643869.22690.digitalmars-d@puremagic.com...
> 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'd consider those situations with delegates to be reason enough.

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

takesADelegate({ return foo.aProperty; });
takesADelegate((int newValue) { foo.aProperty = newValue });

Works with both variables and properties. Which is good, because the whole point of properties is to be used just like a variable. If you ever feel a need to use the property's actual setter or getter as a delegate, then something's been designed wrong.

Also:

void func(ref int x) {...}
func(foo.aProperty);

void func(int* x) {...}
func(&foo.aProperty);

*Should* work with both variables and properties (but might need some thinking in how to actually pull it off correctly. Someone mentioned C# just doesn't even bother with it.)


January 11, 2009
"Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:gkbtpo$1jq0$1@digitalmars.com...
> 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.
>

I think that's a big part of the problem though. The current strategy just leads to various fringe cases with their own specialized "solutions". I think we have sufficient reason to consider "implicit properties" a failed experiment.


January 11, 2009
On Thu, 08 Jan 2009 03:45:08 +0300, Vishaal <vishaal@nospam.com> wrote:

> Properties, such as array.length, should return lvalues to allow:
> a.length += 8;
> or other similar statements.

I'd like to point out another related issue that everyone seems to miss.

The following code is taken from a GUI library:

struct Rect
{
   int x, y, width, height;
   void reset() { x = y = width = height = 0; }
}

class Control
{
   //...

   Rect rect()
   {
       return _rect;
   }

   void rect(Rect newRect)
   {
       _resizeControl(newRect);
   }

   void _resizeControl(Rect rect)
   {
       // ...
   }

   private Rect _rect; 

   //...
}

Imagine rect is not a property but a field, first. How would you mutate it? I'd do it as follows:

Control c = new Control();
c.rect.reset();
c.rect.width = 100;

Both lines do nothing when field replaced with a property. Code is broken but there is no complains from compiler.
You should write the following code instead:

Control c = new Control();
auto tmp = c.rect;
tmp.reset();
c.rect = tmp;

auto tmp = c.rect;
tmp.width = 100;
c.rect = tmp;

but it doesn't look good. Compiler could be smarter in this regard - it could generate the code above automatically whenever a mutating method is called on a property.

And here is also another issue I can't wait to be fixed. The following syntax:
auto prop = foo.prop();

works for properties but not for fields, meaning that whenever code author decides to change it from property to field, user code is broken. Properties should not have optional braces!

As a result, properties and fields can not be used interchangeably until issues above are fixed.
January 11, 2009
On Sun, 11 Jan 2009 07:50:03 +0300, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

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

It was criticized many times by different people as being a too complex and obscure rule.

First, you allow to omit empty pair of braces. Now you understand that it was a bad idea as it bring to many problems and ambiguities. But instead of /fixing/ the source of the issues, you are trying to /reduce/ number of those issues (by narrowing range of cases where omitting parens allowed). That's a wrong way to go. You can reduce them, but you can't eliminate them entirely this way.

It doesn't allow authors to define what is callable with property syntax and what is not. For example, I know many people that never use default function arguments because they cause too many problems with inheritance (some of them come from languages that doesn't allow them at all). Instead, the define two distinct functions:

class Foo
{
   int bar(int i)
   {
       // do something more intelligent here
       return i;
   }

   int bar()
   {
       // call main function with default arguments.
       // Default arguments may be overridden in derived class.
       return bar(42);
   }
}

And a Pandora's Box is open:

Foo foo = new Foo();
foo.bar = -1;

One more case - properties can not return references:

class Foo
{
   ref int bar()
   {
       return _bar;
   }

   private int _bar;
}

Foo foo = new Foo();
foo.bar = -1;

test.d(17): function test.Foo.bar () does not match parameter types (int)
test.d(17): Error: expected 0 arguments, not 1

Nevertheless, let's assume it is a bug and it will be fixed (one more case where optional parens feature causes a compiler bug).
Then, what does the following code do:

class Foo
{
   ref int bar()
   {
       return _bar;
   }

   void bar(int i)
   {
       _bar = i;
   }

   private int _bar;
}

Foo foo = new Foo();
foo.bar = -1; // which one of the functions is called? Both are suitable.

Compare it with the following one:

class Foo
{
   void bar(int i = 0) {}	
   void bar() {}
}

Foo foo = new Foo();
foo.bar(); // which one is called?

DMC result for reference:
       a.foo();
             ^
test.cpp(20) : Error: ambiguous reference to symbol
Had: A::foo()
and: A::foo(int )

Besides, it has nothing to do with issue below:

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

They should not be forbidden, they should be allowed!

Imagine a struct that emulates virtual behavior with methods as first-class objects:

struct Message
{
   string text;

   void delegate() show() // property getter
   {
       return _showDg;
   }

   void show(void delegate(ref Message message) dg) // property setter
   {
       _showDg = () { dg(this); }
   }

   private void delegate() _showDg;
}

auto showDg1 = (ref Message msg) { writefln(msg.text); } // output to stdout
auto showDg2 = (ref Message msg) { MessageBox(0, "Message", msg.text, 0); } // show a message box

Message message;
message.text = "Hello World";

message.show = showDg1;
message.show()(); // works but requires two pair of braces

message.show = showDg2;
message.show();   // this is a goal

How would you /allow/ that? Why don't you see that non-mandatory parens bring more troubles than it /tries/ to solve.
January 11, 2009
dsimcha 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?

foo = foo + 1 vs foo += 1 is not the only issue here.  Indeed, it is rather one of the less important ones, since it tends to give you a compile error instead of sending you on hours of debugging.

Bill already mentioned that properties returning callable things were a problem.

There's another one too:

import tango.io.Stdout;

struct Foo
{
   char[] a = "Waait, something's wrong here.";
}

class Bar
{
    Foo m_foo;
    Foo foo() { return m_foo; }

    // Interestingly, write properties are completely optional
    //   when writing this kind of bug.
    // Foo foo( Foo value ) { return m_foo = value; }
}

void main()
{
    auto b = new Bar();
    b.foo.a = "Everything is fine.";
    Stdout( b.foo.a ).newline; //Prints "Waait, something's wrong here"
    // or writefln( b.foo.a ); if you prefer.
}

try it out (D1 code).

This demonstrates how easy it is to write broken properties.
It is related to foo = foo + 1 vs foo += 1 in that returning lvalues
might solve both of these.  However, as was mentioned before properties
are not lvalues, so making properties always return lvalues may very
well make more trouble for us.
January 11, 2009
Nick Sabalausky wrote:

> <Great Example>

Hear, hear!

-- 
Michiel

January 11, 2009
Denis Koroskin wrote:
> On Thu, 08 Jan 2009 03:45:08 +0300, Vishaal <vishaal@nospam.com> wrote:
> 
>> Properties, such as array.length, should return lvalues to allow:
>> a.length += 8;
>> or other similar statements.
> 
> I'd like to point out another related issue that everyone seems to miss.
<snip struct example>

C# also has that flaw. I ran into it a couple of times at work, and as a result, we have some types that could easily be POD structs but are classes instead.

On the other hand, gmcs (the Mono compiler) errors out when you try modifying a struct that you access by a property, and the Microsoft compiler probably does as well:
struct_prop.cs(24,21): error CS1612: Cannot modify a value type return value of `Foo.bar'. Consider storing the value in a temporary variable
struct_prop.cs(3,8): (Location of the symbol related to previous error)
struct_prop.cs(24,21): error CS0200: Property or indexer `Foo.bar' cannot be assigned to (it is read only)
Compilation failed: 2 error(s), 0 warnings
January 11, 2009
Bill Baxter wrote:
> 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.

It is more tied with the intuitive use of symbols. 'foo' is a function, add a '&' before and you take its address, add a '(...)' after and you make a call and take its return value. The implicit call syntax without parentheses breaks this intuitive use.

In fact, if you follow the principle of orthogonality, instead of calling the function without parameters, it would be much more sensible to have 'foo' return the function address, and deprecate the address operator. Then you could have functions and function/delegate pointers with similar semantics (principle of least surprise).

---------
	int func1() { return 1; }

	auto a = func1;		// 'a' is a pointer to func1
	auto b = &func1;	// ERROR, or deprecated way of above
	auto c = func1();	// calls func1 and returns int 1

	int function() func2 = func1;

	auto d = func2;		// 'd' is a pointer to func1
	auto e = &func2;	// 'e' is a ptr to ptr to func
	auto f = func2();	// calls func1 and returns int 1
---------

Note that this way you have less surprises. Both func1 (the real function) and func2 (pointer to function) behave in similar ways. In current D, 'a' will be different than 'd', while 'c' will be equal to 'f'.

The reasoning for deprecating &func1 is that func1 is not a variable, neither it is an lvalue.

But now it is quite already too late to fix this, unless we go through a long period of deprecating the current behavior, until we can implement the new sane one.

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

Yes, they are basically two distinct, mutually exclusive, compromises. A property is syntax sugar to a collection of delegates (all of them share the same scope). Properties are "views" that aim to behave as much as possible as variables. Optimally, you must ignore completely if a given symbol is a property or a variable. So, it makes little sense to pass a property as a delegate to a function.

But it is still possible, and the solution actually keeps the fundamental property that variables and properties should be almost indistinguishable:

----------
	int m_prop;

	property int prop {
		get() { return m_prop }
		set(int value) { m_prop = value; }
	}

	// lets say that func expects a setter
	func1( (int value){ prop = value; } );

	// and func2 expects a getter
	func2( (){ return prop; } );
----------

See? You passed delegates to the functions, and you kept the compromise that the code doesn't make distinction between properties and variables. The following code (taking the property out) is still valid:

----------
	int prop;
	func1( (int value){ prop = value; } );
	func2( (){ return prop; } );
----------

> So you'd need to have the
> property and a function that wraps the property.

Exactly, or, instead of a function, an inline delegate is enough. As shown above, you win because then the code becomes insensitive to whether prop is a property or a variable.

> Then you've just made more work for yourself.

This is such a corner case that I think that using inline delegates is a good enough solution.

>    Hmm, maybe properties should be
> allowed to be turned into delegates.

The compiler could provide a syntax for that. Like I said, a property is just a collection of delegates under a single identifier. For example, tentative syntax:

----------
	property int prop { ... }

	auto setter = property(prop).set;
	// setter has type 'void delegate(int)'

	auto getter = property(prop).get;
	// getter has type 'int delegate()'
----------

But I think this is a secondary feature (if we keep demanding solutions for the corner cases, we will never have the solution for the biggest problem). I could live fine without this feature.

January 11, 2009
Andrei Alexandrescu wrote:
> 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.

That would only patch a single consequence of the much bigger core problem that is the current property syntax.

Not to say that, although rare, it is perfectly possible to have write-only properties. I once wrote a microcontroller emulator that had a few write-only registers, serial port output, for example.
January 11, 2009
Miles:
> In fact, if you follow the principle of orthogonality, instead of calling the function without parameters, it would be much more sensible to have 'foo' return the function address, and deprecate the address operator. Then you could have functions and function/delegate pointers with similar semantics (principle of least surprise).

I agree.


> But now it is quite already too late to fix this, unless we go through a long period of deprecating the current behavior, until we can implement the new sane one.

Seeing how much experimental D2 is, I think there's time to fix design bugs still.

Bye,
bearophile