@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