Walter Bright wrote: > Having optional parentheses does lead to unresolvable ambiguities. How > much of a problem that really is is debatable, but let's assume it > should be resolved. To resolve it, a property must be distinguishable > from a regular function. > > One way is to simply add a "property" attribute keyword: > > property bool empty() { ... } > property void empty(bool b) { ... } > > The problem is that: > > 1. there are a lot of keywords already > 2. keywords are global things > > The alternative is to have a unique syntax for properties. Ideally, the > syntax should be intuitive and mimic its use. After much fiddling, and > based on n.g. suggestions, Andrei and I penciled in: > > bool empty { ... } > void empty=(bool b) { ... } > > The only problem is when a declaration but not definition is desired: > > bool empty; > > but oops! That defines a field. So we came up with essentially a hack: > > bool empty{} > > i.e. the {} means the getter is declared, but defined elsewhere. > > What do you think?
I don't mind the syntax myself. I'm even sorta surprised the ng responded to it with such negativity. What I care more about is that some kind of rewriting rules are implemented on expressions that undergo some kind of mutation. The important thing being that whenever a property is mutated or a (possibly indirect) member of a property is mutated, then the setter is guaranteed to be invoked. This is the only way I've read so far to make things like "array.length++;" work without hacky things like proxy structs with exhaustive operator overloading, or exhaustive overload defining in the property itself. I'll put examples of the rewriting here, including a couple extras I've found. //================ // The basics; property member mutation: a.b.c = 3; // rewritten as auto t = a.b; t.c = 3; a.b = t; //================ // Deeper nesting: a.b.c.d = 3; // rewritten as auto t2 = a.b; auto t1 = t2.c; t1.d = 3; t2.c = t1; a.b = t2; //================ // Direct property mutations (++,--,+=,-=,*=,etc): a.b++; // rewritten as auto t = a.b; t++; a.b = t; //================ // More direct mutations a.b.c += 42; // rewritten as auto t2 = a.b; auto t1 = t2.c; t1 += 42; t2.c = t1; a.b = t2; //================ // Complicated lvalues: s[s.length ++] = foo; // rewritten as auto t = s.length; t++; s[(s.length = t)] = foo; // Yes this will make a bounds error. They totally deserve it ;) // If this is unacceptable, then more complicated rules could be created // for operations like ++ and -- that are calculated after being read. // The expression would have to be evaluated with the getter's value, // then the setter would be called after the temporary is mutated. // Like so: auto t = s.length; s[t] = foo; t++; s.length = t; //================ // Unary Expressions: class Foo { int count(){ return cnt++; } } Foo aFoo = new Foo; int i = aFoo.count++; //i = ? // rewritten as auto t = aFoo.count; t++; int i = (aFoo.count = t); //================ // rvalues such as a.b.c.d are /not/ rewritten: int foo = a.b.c.d; // a.b.c.d is not being mutated in any way, only read. //=========== [end] One interesting thing I realized about this is that you can probably apply this without special property syntax. With omissible parentheses and expression rewrites, almost everything would just work. /almost/. Yes, delegates would still be given the short end due to the parentheses ambiguity. Oh, and it would still be difficult/impossible for IDEs to determine which things are properties and which are not. Also, you'd be able to migrate fields to properties but not the other way around. If you were willing to implement attribute syntax, then you could probably please almost everyone (I hope). The omissible parentheses could be left in. It would be possible to create these entities that are both functions and properties at the same time. They wouldn't be hazardous anymore. @property could be used to mark a strict property declaration. Such a declaration would forbid being called with parentheses, and thus allow better enforcement of API conventions, better analysis by 3rd party tools and generative code, and allow delegates to be returned from properties. Fields could be migrated to properties or strict @properties with very little API breakage, while only strict @properties could be migrated to fields with (maybe) zero API breakage.