Difference between revisions of "Property Discussion Proposal 1"

From D Wiki
Jump to: navigation, search
(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...")
 
 
(2 intermediate revisions by 2 users not shown)
Line 13: Line 13:
 
# The <code>-property</code> compiler '''switch''' is removed
 
# The <code>-property</code> compiler '''switch''' is removed
 
# @property on free functions
 
# @property on free functions
 +
## Two categories: UFCS properties and global properties (see example)
 
# 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)
 
 
<syntaxhighlight lang="D">
 
struct Type
 
{
 
    @property void native(int);
 
}
 
 
//Global properties:
 
//returns void, has 1 parameter --> global setter
 
@property void external1(int);
 
//returns !void, has 0 parameters --> global getter
 
@property int external1();
 
 
//Used like a global variable:
 
void a()
 
{
 
    external1 = 41;
 
    // 41.external1; is
 
    // invalid, a getter can't return void (void + 1 args is always a global variable property)
 
    external1++;
 
    assert(external1 == 42);
 
}
 
  
//UFCS properties:
+
==== Proposal 1.1 ====
//returns void, has 2 parameters --> global setter
+
# Like proposal 1, but instead of rule 7, returning ref values is allowed as long as there is not setter function.
@property void external2(Type, int);
 
//returns !void, has 1 parameters --> global getter
 
@property int external2(Type);
 
 
 
//external2 can only be called on a Type instance.
 
//calling it manually "external2(Type(), 42)" is invalid
 
  
//Used like a property/field declared on Type:
+
==== Proposal 1.2 ====
void a()
+
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:
{
 
    Type t;
 
    t.external2 = 41;
 
    t.external2++;
 
    assert(t.external2 == 42);
 
    //all "t.external1" combinations are invalid!
 
}
 
</syntaxhighlight>
 
  
 +
# 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?
 +
-->
  
===== Unified test suite =====
+
===== 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 external1(int) { } //OK (Global setter)
 
@property int external1() { return 42; } //OK (Global getter)
 
  
@property void external2(Test, int) { } //OK (UFCS setter)
+
// OK: returns void, has 1 parameter --> global setter
@property int external2(Test) { return 42; } //OK (UFCS getter)
+
@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
Line 118: Line 96:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
 +
[[Category:Proposals]]

Latest revision as of 21:38, 20 May 2015

  1. @property functions can't be called with parentheses: x._property() is illegal! (Normal functions are explicitly not affected by this rule!)
    1. 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.
  2. Getting a reference is illegal: &x._property does not compile.
    1. 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...
  3. 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.
  4. The getter/setter function can be obtained via __trais: __traits(propertyGetter, x._property) __traits(propertySetter, x._property)
    1. The function types can be obtained like this: typeof(__traits(propertyGetter, x._property)) == int delegate()
  5. The type of the returned value is obtained with typeof: typeof(x._property) == int
  6. The compiler uses semantic rewriting to make @property access as similar as field access as possible.
  7. Property functions cannot return ref values (as this could bypass the setter)
  8. The setter may take it's parameter by ref.
  9. The -property compiler switch is removed
  10. @property on free functions
    1. Two categories: UFCS properties and global properties (see example)
  11. A setters parameter type and the corresponding getters return type must match

Proposal 1.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:

  1. 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.)
  2. 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".
  3. 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
}