Difference between revisions of "DIP28"
m (strict to struct) |
(→Properties methods) |
||
Line 57: | Line 57: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Whenever the property is used, it is automatically called. No exception. | + | Whenever the property is used as expression, it is automatically called. No exception. |
<syntaxhighlight lang="d"> | <syntaxhighlight lang="d"> | ||
Foo bar; | Foo bar; | ||
Line 95: | Line 95: | ||
&(bar.getter()); // Equivalent code if @property wasn't used. | &(bar.getter()); // Equivalent code if @property wasn't used. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | Whenever the property is used as symbol (alias, alias template parameters), then the symbol is passed and it behave just like the property : | ||
+ | <syntaxhighlight lang="d"> | ||
+ | Foo bar; | ||
+ | auto foo(alias T)() { | ||
+ | return T; | ||
+ | } | ||
+ | |||
+ | alias foo!(bar.getter) foobar; // No evaluation. | ||
+ | foobar(); // getter is evaluated. | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Beware, getter can match both value and alias parameters, just like variable declarations. Same resolution rules apply. | ||
== Properties as free functions == | == Properties as free functions == |
Revision as of 15:51, 13 March 2013
Title: | Functions |
---|---|
DIP: | 28 |
Version: | 1 |
Status: | Draft |
Created: | 2013-02-27 |
Last Modified: | 2013-02-27 |
Author: | Amaury SÉCHET |
Links: | DIP23, DIP27 |
Contents
Abstract
This DIP is the second of 3. It intend to define properties in D. Just like DIP27 it intend to be simple and avoid special cases, by defining properties as function that are called 100% of the time in 100% of the situations.
Rationale
If function are an abstraction for action, properties are abstraction for data. In order to achieve that goal, properties must behave in a way that is as close as possible to data, and avoid cases where it behave in a different way (an error if preferable when 100% compatibility with plain data isn't possible).
A user may want to use a property instead of plain data for the following reasons :
- To use contract to protect what can be done with actual data.
- To plug some debug informations or logging to know where the data is accessed/modified.
- To provide a data like interface to user as it is often nicer to use than setter/getter.
- To create write only/read only values.
This list is not exhaustive, but is clearly enough to show the need for a data oriented abstraction.
Properties methods
A property is defined as a method with the @property attribute :
struct Foo {
@property
ReturnType getter() {
// Function body.
}
@property
ReturnType setter(Parameter) {
// Function body.
}
}
Whenever the property is used as expression, it is automatically called. No exception.
Foo bar;
bar.getter; // getter property is called.
bar.getter(); // Equivalent code if @property wasn't used.
If a property method have a parameter, it is a setter. This property can only be used on the left side of an assignation expression.
Foo bar;
bar.setter = 3; // setter property is called with 3 as parameter.
bar.setter(3); // Equivalent code if @property wasn't used.
As a property is always called, explicitly calling it is either an error, or a call of the returned value if it is callable.
Foo bar;
bar.getter(); // getter is not callable (if ReturnType isn't callable).
// Call what is returned by getter otherwise.
bar.getter()(); // Equivalent code if @property wasn't used.
bar.setter(3); // Error.
As a property is always called, it has the type of its return value.
Foo bar;
static assert(is(typeof(bar.getter) == ReturnType)); // Pass
static assert(is(typeof(bar.getter()) == ReturnType)); // Equivalent code if @property wasn't used.
static assert(is(typeof(bar.setter = 3) == ReturnType)); // Pass
static assert(is(typeof(bar.setter(3)) == ReturnType)); // Equivalent code if @property wasn't used.
As a property is always called, taking its address take the address of the returned value (if allowed).
Foo bar;
&bar.getter; // Error, bar.getter isn't an lvalue.
&(bar.getter()); // Equivalent code if @property wasn't used.
Whenever the property is used as symbol (alias, alias template parameters), then the symbol is passed and it behave just like the property :
Foo bar;
auto foo(alias T)() {
return T;
}
alias foo!(bar.getter) foobar; // No evaluation.
foobar(); // getter is evaluated.
Beware, getter can match both value and alias parameters, just like variable declarations. Same resolution rules apply.
Properties as free functions
When defined as free function, a property is intended to be used as UFCS. It means that a property must have 1 argument for a getter or 2 arguments for a setter.
@property
ReturnType getter(X a) {
// Function body.
}
@property
ReturnType setter(X a, Y b) {
// Function body.
}
X a;
Y b;
a.getter; // getter is called.
a.setter = b; // setter is called.
Operator overloading
Assignation can present itself is many shapes. In this case, the expression is rewritten to involve only simple assignation. For instance :
@property
ReturnType prop(X a) {
// Function body.
}
@property
ReturnType prop(X a, uint i) {
// Function body.
}
X a;
a.prop++;
// Become :
{ auto tmp = a.prop; a.prop = tmp + 1; return tmp; }();
{ auto tmp = a.prop(); a.prop(tmp + 1); return tmp; }(); // Equivalent code if @property wasn't used.
a.prop += 42;
// Become :
a.prop = a.prop + 42;
a.prop(a.prop() + 42); // Equivalent code if @property wasn't used.
Conclusion
This DIP propose a data oriented abstraction for D users. It has no special case whatsoever in order to avoid corner cases or bad interactions with other features.
Copyright
This document has been placed in the Public Domain.