Property Discussion Wrap-up
This is a wrap-up of the property discussion which happened around 25. January 2013. There have been some discussion before and there might be more in the future :-)
Except from off-topic, meta and personal discussions which are not summarized on this page these two issues where discussed:
Contents
Link to discussion
If you've got too much time, the original discussions can be read here:
- http://forum.dlang.org/thread/ceukykobasewoexsrveb@forum.dlang.org
- http://forum.dlang.org/thread/kdqrnl$13ft$1@digitalmars.com
Property
- Everyone agrees that @property doesn't work well right now. People disagree what's the actual problem with @property. Some people where arguing to remove @property completely. It's not clear if those where advertising making normal functions callable like @properties or removing @property without replacement.
Why @property
As there were suggestions removing @property completely we should think about what problem @properties should solve:
- Properties are a replacement for fields
- Unlike fields, they are polymorphic
- As they are supposed to be used as fields, they are for data access and nothing else
- They are especially not there to make function calls without parentheses. This is a different issue. To the developer properties are not functions, they're enhanced fields. The fact that they are implemented as functions is a implementation detail.
- D as a systems programming language should still provide a way to detect if something is a property and to get the getter/setter. This shouldn't be visible in normal usage.
- Properties allow executing additional code. Checking the supplied value, firing events,...
- As a property looks like data access, complexity should be kept constant.
An example for property usage:
struct A
{
int _days;
@property int weeks()
{
return _days/7;
}
@property void weeks(int value)
{
assert(value <= 42);
_days = value*7;
}
}
void use()
{
A a;
a.weeks = 3;
a.weeks++;
a.weeks -=2;
typeof(a.weeks) b = a.weeks;
}
Now consider how to implement the above if properties where completely removed, as proposed. The D1 solution allowed assignment syntax for normal functions. So the above example could work exactly the same way, but @property
wouldn't be used. This is a poor solution for properties as it allows to write nonsensical code:
void use()
{
writeln = 42; //would call writeln(42);
writeln += 1; //How would this even be implemented without @property?
}
Note that this is in no way related to the optional parentheses discussion: You can have parentheses, a @property implementation and still disallow code like writeln = 42;
Property declaration syntax
Some new syntaxes have bee proposed for property declaration:
- Could make declaring properties less verbose
- Would break lots of code
- Could be done without breakage with new attributes (
@prop
), but that's ugly
- Could be done without breakage with new attributes (
- There's no need for a new syntax. Although the current syntax is verbose, it does not have real issues. The issues happen when using properties, not when declaring them.
- The allowed function prototypes (ref) should be reviewed though and it should be made clear how they interact:
@property ref int prop();
@property int prop();
@property void prop(int value);
@property int prop(int value);
@property void prop(ref int value); //not as problematic
Implementation concerns
- Can we get a reference to the property? What does
&x.property_
mean? - How can we get the getter / setter functions? Do we need to get those?
- What is the type of the property? Return type, setter function type or getter function type? How to get the other types?
- What does
x.property_++
do? (Semantic rewriting) - Is returning ref values from the getter OK?
- Is taking ref values in the setter OK?
- Should all properties be nothrow? pure?
- Probably better as a rule for style guide.
opDispatch and properties
If you make opDispatch @property, then you can't use it with normal functions, and if you don't make it @property, then it can't be used with property functions. And you can't overload on @property, so opDispatch is pretty much screwed with regards to properties at this point.
Proposals
1
- @property functions can't be called with parentheses:
x._property()
is illegal! (Normal functions are explicitly not affected by this rule!)- The only exception to this rule: If the returned value can be called (delegate, function pointer, opCall), then
x._property()
calls the returned value! It essentially behaves exactly as a field would.
- The only exception to this rule: If the returned value can be called (delegate, function pointer, opCall), then
- Getting a reference is illegal:
&x._property
does not compile.- Rationale: Returning a reference to a temporary is not the same as taking the address of a field and is therefore confusing and dangerous in generic code. A real pointer could be returned if the property getter returned a value by ref. But then it would only work for some properties, could potentially bypass setters, would break if a setter was added later...
- To detect if something is a property or a field, use
__traits(isProperty, x._property)
! Do not use any tricks in user code to detect if something is a property or field. - The getter/setter function can be obtained via __trais:
__traits(propertyGetter, x._property)
__traits(propertySetter, x._property)
- The function types can be obtained like this:
typeof(__traits(propertyGetter, x._property)) == int delegate()
- The function types can be obtained like this:
- The type of the returned value is obtained with typeof:
typeof(x._property) == int
- The compiler uses semantic rewriting to make @property access as similar as field access as possible.
- Property functions cannot return ref values (as this could bypass the setter)
- The setter may take it's parameter by ref.
- The
-property
compiler switch is removed
1.1
- Like proposal 1, but instead of rule 6, returning ref values is allowed as long as there is not setter function.
Optional parentheses
Pro
- UFCS code looks nicer:
auto yesterday = 2.days.ago;
some!(e1).ufcs!(e2).chaining!(e3)
//instead of
some!(e1)().ufcs!(e2)().chaining!(e3)()
//We need a real world UFCS case demonstrating this. There are real world examples - even in that discussion thread - but I cant find them ;-)
Cons
- Ambiguous / complicated if a function returns a delegate or similar (solvable with special case rules)
Both issues are basically orthogonal. It's possible to have both, have none and have any mix of those.
The small issue where they interfere is code readability:
If optional parentheses are disallowed, it's easy to see that
auto var = new someClass();
auto x = var.something;
will always be either a field access or a property call. But according to property definition (as used by C# and other languages), properties should behave like fields (i.e. linear complexity, no side-effects,...). Therefore without optional parentheses it's possible to see if an access is actually a potentially heavy weight function call or data / property access.
Except from this, @property and optional parentheses are orthogonal.