@property - take it behind the woodshed and shoot it?

John Colvin john.loughran.colvin at gmail.com
Sat Aug 10 13:53:34 PDT 2013


On Thursday, 24 January 2013 at 08:35:01 UTC, Walter Bright wrote:
> This has turned into a monster. We've taken 2 or 3 wrong turns 
> somewhere.

My 2 cents on this, very late in the day. Sorry for the essay, 
hopefully it'll be worth your time.

No fancy new ideas, but I *think* I have a nice solution that 
keeps everyone happy and avoids nasty corner-cases and 
ambiguities:


1) non-property functions must be called with parenthesis, 
*except* on UFCS calls. No assignment call syntax for 
non-properties.
   fun1.fun2; //illegal
   fun1.fun2(); //illegal
   fun1().fun2; //OK
   fun1().fun2(); //OK
   fun1; //illegal
   fun1(); //OK
   fun2 = var; //not a function call, but a normal assignment where
               //possible, e.g. initialising a function pointer.
   iota(100).map!"a+1".filter!"a%5".writeln;   //LEGAL :-)

Alternative way of saying it: optional paren for single argument 
calls in UFCS, enforced parenthesis elsewhere.

Reason: No one wants writeln = "Hello World!"; but a lot of 
people want "Hello World!".writeln; Also, this removes visual 
ambiguity between the name of a function and it's result. That 
only really matters with alias/variadic template parameters but 
it is very annoying there. That will never conflict with UFCS so 
everyone's happy.
Note that in the first two examples, fun1 is *NOT* a UFCS call 
and therefore cannot be done without parens. Preventing optional 
parens on non-UFCS stops any possible mixing of optional 
parenthesis and the unary & operator.


2) @property enforces parenthesis-less calling for getters and 
assignment syntax for setters. No properties with more than one 
argument allowed. If a property returns a callable, any 
parenthesis will be applied to the callable

Reason: Properties are not for general purpose work. The whole 
point of them is to be used roughly like variables and this is as 
close to that as we're gonna get.


3) Getting the address of a function is done with the & operator. 
Callables must always have parenthesis *except* when: Parenthesis 
would be optional for an equivalent normal function AND not when 
the callable is the result of an expression:
   42.aDelegate.foo; //OK
   42.returnsADelegate.foo; //OK, foo acts on the delegate
   42.returnsADelegate().foo; //OK, foo acts on the delegate
   42.returnsADelegate()().foo; //OK, foo acts on
                                //the return value of the delegate
   aDelegate; //illegal
   aDelegate() //OK
   "blah".aDelegate; //OK
   "blah".aDelegate(); //OK

   auto a = &returnsADelegate; //a is function pointer to
                               //returnsADelegate
   a; //illegal
   auto b = a(); //OK, calls a and b is a delegate
   b; //illegal
   b(); //OK, calls the delegate.

   //given some struct Bar that overloads opBinary!"+" and opCall
   Bar x,y;
   x; //illegal
   x(); //opCall
   (x+y)  //does not call opCall
   (x+y)() //does call opCall

Reason: This allows them to be used transparently like normal 
functions where possible, but removes ambiguity elsewhere.

3a) Explicitly taking the address of a property. Attempting to 
shoe-horn this in with normal syntax causes hell for the rest. I 
suggest some magic here, e.g. __traits(propertyGetterAddress, 
a.prop) and __traits(propertySetterAddress, a.prop)
A function pointer to a property must be called as a normal 
function pointer.

   auto v = __traits(propertyGetterAddress, a.prop);
   auto n = v; //n is a function pointer to the getter a.prop,
               //same as v
   auto n = v(); //n is equal to the return of the getter a.prop

   auto h = __traits(propertySetterAddress, a.prop);
   auto j = h; //j is a function pointer to the setter a.prop
               //same as h
   h = 3; //illegal
   h(3); //OK, equates to a.prop = 3;

Reason: This needs to be possible, but using & for it is broken. 
The most important thing is to keep the syntax of common 
use-cases clean and unsurprising. ***any other workable syntax 
for this is ok, __traits is just an (ugly, verbose) example***
Losing all property-ness when working through a function pointer 
keeps things simple.


4) OPTIONAL: Free functions can be marked as properties, with the 
same rules applied to them as properties in aggregates, except 
they must take 1 or 2 arguments instead of 0 or 1.

Reason: Free functions and ufcs => good for encapsulation and 
nice to look at. Is there any reason why not to allow us to 
declare free properties?


These rules should provide the least possible surprise and 
breakage, as far as I can tell. I don't think personally I would 
have to alter a single line of my code.

What inevitable corner-cases have I missed?


More information about the Digitalmars-d mailing list