property syntax strawman

Chad J chadjoan at __spam.is.bad__gmail.com
Sun Aug 2 07:31:48 PDT 2009


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.



More information about the Digitalmars-d mailing list