Difference between revisions of "Property Discussion Proposal 1"
(Created page with " # @property functions can't be called with parentheses: <code>x._property()</code> is illegal! (Normal functions are explicitly not affected by this rule!) ## The only except...") |
|||
Line 14: | Line 14: | ||
# @property on free functions | # @property on free functions | ||
# A setters parameter type and the corresponding getters return type must match | # A setters parameter type and the corresponding getters return type must match | ||
− | + | # Two categories: UFCS properties and global properties (see example) | |
− | + | ==== Proposal 1.1 ==== | |
− | + | # Like proposal 1, but instead of rule 7, returning ref values is allowed as long as there is not setter function. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | ==== Proposal 1.2 ==== | |
− | + | This is the same as proposal 1 except that marking a function or method as @property turns it into a variable-like entity, such that to the outside world, it behaves ''exactly'' like a variable, and ''not'' like a function: | |
− | @property | ||
− | |||
− | |||
− | + | # Writing "&prop" is legal only if (1) func returns by ref, and (2) there is no setter method prop(T). It simply returns the address of the returned value. It does '''not''' return a function pointer to prop. (To the outside world, prop is a variable, not a function.) | |
− | + | # It is illegal to call a @property function using regular function-call syntax: if you have @property void ufcsProp(U, T) {...}, then it is illegal to write "ufcsProp(u,t)". Instead, you must write "u.ufcsProp = t". | |
+ | # Assignment syntax f=x; is only valid if f is @property. It is illegal for non-@property f. Same goes for assignment-related operations like f++, f+=2, etc.. | ||
+ | <!-- MERGED | ||
+ | * I think most of this could me merged to 1. On a quick look the only difference seems to be this proposal has less traits (can't access getter/setter function) and this allows &prop. This proposal also makes it clear that properties are treated like/variables fields. The first proposal doesn't state it as clearly, but this is also meant in #1. How about merging the generic part into #1 and only listing those differences here? | ||
+ | --> | ||
− | + | ===== Clarifications with code examples ===== | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | == | ||
<syntaxhighlight lang="D"> | <syntaxhighlight lang="D"> | ||
alias Deleg = int delegate(); | alias Deleg = int delegate(); | ||
Line 75: | Line 45: | ||
@property Deleg delegRet() { return () => 42; } //OK | @property Deleg delegRet() { return () => 42; } //OK | ||
} | } | ||
− | |||
− | |||
− | |||
− | @property void external2(Test, int) { } //OK | + | // OK: returns void, has 1 parameter --> global setter |
− | @property int external2(Test) { return 42; } // | + | @property void external1(int) { } |
+ | // OK: returns !void, has 0 parameters --> global getter | ||
+ | @property int external1() { return 42; } | ||
+ | |||
+ | // OK: returns void, has 2 parameters --> global UFCS setter | ||
+ | @property void external2(Test, int) { } | ||
+ | // OK; returns !void, has 1 parameter --> global UFCS getter | ||
+ | @property int external2(Test) { return 42; } | ||
+ | |||
+ | //external2 can only be called on a Type instance. | ||
+ | //calling it manually "external2(Type(), 42)" is invalid | ||
void main() | void main() | ||
Line 93: | Line 70: | ||
external1(42); //invalid | external1(42); //invalid | ||
int b = external1; //OK | int b = external1; //OK | ||
− | 42.external1; //invalid | + | 42.external1; // invalid, a getter can't return void (void + 1 args is always a global variable property) |
+ | |||
t.external2 = 42; //OK | t.external2 = 42; //OK |
Revision as of 15:33, 27 January 2013
- @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 - @property on free functions
- A setters parameter type and the corresponding getters return type must match
- Two categories: UFCS properties and global properties (see example)
Proposal 1.1
- Like proposal 1, but instead of rule 7, returning ref values is allowed as long as there is not setter function.
Proposal 1.2
This is the same as proposal 1 except that marking a function or method as @property turns it into a variable-like entity, such that to the outside world, it behaves exactly like a variable, and not like a function:
- Writing "&prop" is legal only if (1) func returns by ref, and (2) there is no setter method prop(T). It simply returns the address of the returned value. It does not return a function pointer to prop. (To the outside world, prop is a variable, not a function.)
- It is illegal to call a @property function using regular function-call syntax: if you have @property void ufcsProp(U, T) {...}, then it is illegal to write "ufcsProp(u,t)". Instead, you must write "u.ufcsProp = t".
- Assignment syntax f=x; is only valid if f is @property. It is illegal for non-@property f. Same goes for assignment-related operations like f++, f+=2, etc..
Clarifications with code examples
alias Deleg = int delegate();
struct Test
{
int var;
@property void native2(int param = 1337) { } //invalid. default param creates invalid case with no args which returns void
@property void native(int) { } //OK
@property int native() { return 42; } //OK
@property void strange() { } //not allowed
@property Deleg delegRet() { return () => 42; } //OK
}
// OK: returns void, has 1 parameter --> global setter
@property void external1(int) { }
// OK: returns !void, has 0 parameters --> global getter
@property int external1() { return 42; }
// OK: returns void, has 2 parameters --> global UFCS setter
@property void external2(Test, int) { }
// OK; returns !void, has 1 parameter --> global UFCS getter
@property int external2(Test) { return 42; }
//external2 can only be called on a Type instance.
//calling it manually "external2(Type(), 42)" is invalid
void main()
{
Test t;
t.native = 42; //OK
t.native(42); //invalid
int a = t.native; //OK
t.strange; //invalid (definition of strange already invalid)
external1 = 42; //OK
external1(42); //invalid
int b = external1; //OK
42.external1; // invalid, a getter can't return void (void + 1 args is always a global variable property)
t.external2 = 42; //OK
auto c = t.external2; //OK
external2(t).external1; //invalid
t.delegRet; //basically valid, could complain about statement has no effect if delegRet getter is pure
t.delegRet(); //valid, gets delegate then calls it(but as the result
//of the delegate call isn't used the compiler could complain if the delegate is pure)
t.delegRet()(); //invalid
pragma(msg, is(typeof(t.var) == typeof(t.native))); //OK
pragma(msg, typeof(external1)); //int
import std.traits;
// pragma(msg, typeof(ReturnType!(typeof(external1)))); // error (typeof(external1) returns int, not a function type)
pragma(msg, typeof(&(t.delegRet))); //illegal, can't get address of temporary? (Not sure here)
pragma(msg, typeof(t.delegRet)); //int delegate()
pragma(msg, typeof(t.delegRet())); //OK (is this valid D code in general?)
pragma(msg, typeof(t.delegRet()())); //invalid
pragma(msg, ReturnType!(typeof(t.delegRet))); //int
}