forked from DlangRen/Programming-in-D
-
Notifications
You must be signed in to change notification settings - Fork 1
/
ufcs.d
223 lines (170 loc) · 5.94 KB
/
ufcs.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
Ddoc
$(DERS_BOLUMU $(IX UFCS) $(IX universal function call syntax) Universal Function Call Syntax (UFCS))
$(P
UFCS is a feature that is applied by the compiler automatically. It enables the member function syntax even for regular functions. It can be explained simply by comparing two expressions:
)
---
variable.foo($(I arguments))
---
$(P
When the compiler encounters an expression such as the one above, if there is no member function named $(C foo) that can be called on $(C variable) with the provided arguments, then the compiler also tries to compile the following expression:
)
---
foo(variable, $(I arguments))
---
$(P
If this new expression can indeed be compiled, then the compiler simply accepts that one. As a result, although $(C foo()) evidently has been a regular function, it gets accepted to be used by the member function syntax.
)
$(P
We know that functions that are closely related to a type are defined as member functions of that type. This is especially important for encapsulation as only the member functions of a type (and that type's module) can access its $(C private) members.
)
$(P
Let's consider a $(C Car) class which maintains the amount of fuel:
)
---
$(CODE_NAME Car)class Car {
enum economy = 12.5; // kilometers per liter (average)
private double fuelAmount; // liters
this(double fuelAmount) {
this.fuelAmount = fuelAmount;
}
double fuel() const {
return fuelAmount;
}
// ...
}
---
$(P
Although member functions are very useful and sometimes necessary, not every function that operates on a type should be a member function. Some operations on a type are too specific to a certain application to be member functions. For example, a function that determines whether a car can travel a specific distance may more appropriately be defined as a regular function:
)
---
$(CODE_NAME canTravel)bool canTravel(Car car, double distance) {
return (car.fuel() * car.economy) >= distance;
}
---
$(P
This naturally brings a discrepancy in calling functions that are related to a type: objects appear at different places in these two syntaxes:
)
---
$(CODE_XREF Car)$(CODE_XREF canTravel)void main() {
auto car = new Car(5);
auto remainingFuel = $(HILITE car).fuel(); // Member function syntax
if (canTravel($(HILITE car), 100)) { // Regular function syntax
// ...
}
}
---
$(P
UFCS removes this discrepancy by allowing regular functions to be called by the member function syntax:
)
---
if ($(HILITE car).canTravel(100)) { // Regular function, called by the
// member function syntax
// ...
}
---
$(P
This feature is available for fundamental types as well, including literals:
)
---
int half(int value) {
return value / 2;
}
void main() {
assert(42.half() == 21);
}
---
$(P
As we will see in the next chapter, when there are no arguments to pass to a function, that function can be called without parentheses. When that feature is used as well, the expression above gets even shorter. All three of the following statements are equivalent:
)
---
result = half(value);
result = value.half();
result = value.half;
---
$(P
$(IX chaining, function call) $(IX function call chaining) UFCS is especially useful when function calls are $(I chained). Let's see this on a group of functions that operate on $(C int) slices:
)
---
$(CODE_NAME functions)// Returns the result of dividing all of the elements by
// 'divisor'
int[] divide(int[] slice, int divisor) {
int[] result;
result.reserve(slice.length);
foreach (value; slice) {
result ~= value / divisor;
}
return result;
}
// Returns the result of multiplying all of the elements by
// 'multiplier'
int[] multiply(int[] slice, int multiplier) {
int[] result;
result.reserve(slice.length);
foreach (value; slice) {
result ~= value * multiplier;
}
return result;
}
// Filters out elements that have odd values
int[] evens(int[] slice) {
int[] result;
result.reserve(slice.length);
foreach (value; slice) {
if (!(value % 2)) {
result ~= value;
}
}
return result;
}
---
$(P
When written by the regular syntax, without taking advantage of UFCS, an expression that chains three calls to these functions can be written as in the following program:
)
---
$(CODE_XREF functions)import std.stdio;
// ...
void main() {
auto values = [ 1, 2, 3, 4, 5 ];
writeln(evens(divide(multiply(values, 10), 3)));
}
---
$(P
The values are first multiplied by 10, then divided by 3, and finally only the even numbers are used:
)
$(SHELL
[6, 10, 16]
)
$(P
A problem with the expression above is that although the pair of $(C multiply) and $(C 10) are related and the pair of $(C divide) and $(C 3) are related, parts of each pair end up written away from each other. UFCS eliminates this issue and enables a more natural syntax that reflects the actual order of operations:
)
---
writeln(values.multiply(10).divide(3).evens);
---
$(P
Some programmers take advantage of UFCS even for calls like $(C writeln()):
)
---
values.multiply(10).divide(3).evens.writeln;
---
$(P
As an aside, the entire program above could have been written in a much simpler way by $(C map()) and $(C filter()):
)
---
import std.stdio;
import std.algorithm;
void main() {
auto values = [ 1, 2, 3, 4, 5 ];
writeln(values
.map!(a => a * 10)
.map!(a => a / 3)
.filter!(a => !(a % 2)));
}
---
$(P
The program above takes advantage of $(LINK2 /ders/d.en/templates.html, templates), $(LINK2 /ders/d.en/ranges.html, ranges), and $(LINK2 /ders/d.en/lambda.html, lambda functions), all of which will be explained in later chapters.
)
Macros:
SUBTITLE=Universal Function Call Syntax (UFCS)
DESCRIPTION=Universal function call syntax: The ability to call regular functions by the member function syntax.
KEYWORDS=d programming lesson book tutorial encapsulation