root/trunk/docsrc/rationale.dd

Revision 2040, 6.0 kB (checked in by walter, 2 years ago)

typography

  • Property svn:eol-style set to native
Line 
1 Ddoc
2
3 $(D_S Rationale,
4
5     $(P Questions about the reasons for various design decisions for
6     D often come up. This addresses many of them.
7     )
8
9 <h2>Operator Overloading</h2>
10
11 <h3>Why not name them operator+(), operator*(), etc.?</h3>
12
13     $(P This is the way C++ does it, and it is appealing to be able
14     to refer to overloading $(SINGLEQUOTE +) with $(SINGLEQUOTE operator+). The trouble is
15     things don't quite fit. For example, there are the
16     comparison operators <, <=, >, and >=. In C++, all four must
17     be overloaded to get complete coverage. In D, only a cmp()
18     function must be defined, and the comparison operations are
19     derived from that by semantic analysis.
20     )
21
22     $(P Overloading operator/() also provides no symmetric way, as a member
23     function, to overload the reverse operation. For example,
24     )
25
26 ------
27 class A
28 {
29     int operator/(int i);       // overloads (a/i)
30     static operator/(int i, A a)    // overloads (i/a)
31 }
32 ------
33
34     $(P The second overload does the reverse overload, but
35     it cannot be virtual, and so has a confusing asymmetry with
36     the first overload.
37     )
38
39 <h3>Why not allow globally defined operator overload functions?</h3>
40
41     $(OL
42     $(LI Operator overloading can only be done with an argument
43     as an object, so they logically belong as member functions
44     of that object. That does leave the case of what to do
45     when the operands are objects of different types:
46
47 ------
48 class A { }
49 class B { }
50 int opAdd(class A, class B);
51 ------
52
53     Should opAdd() be in class A or B? The obvious stylistic solution
54     would be to put it in the class of the first operand,
55
56 ------
57 class A
58 {
59     int opAdd(class B) { }
60 }
61 ------
62     )
63
64     $(LI Operator overloads usually need access to private members
65     of a class, and making them global breaks the object oriented
66     encapsulation of a class.
67     )
68
69     $(LI (2) can be addressed by operator overloads automatically gaining
70     "friend" access, but such unusual behavior is at odds with D
71     being simple.
72     )
73
74     )
75
76 <h3>Why not allow user definable operators?</h3>
77
78     $(P These can be very useful for attaching new infix operations
79     to various unicode symbols. The trouble is that in D,
80     the tokens are supposed to be completely independent of the
81     semantic analysis. User definable operators would break that.
82     )
83
84 <h3>Why not allow user definable operator precedence?</h3>
85
86     $(P The trouble is this affects the syntax analysis, and the syntax
87     analysis is supposed to be completely independent of the
88     semantic analysis in D.
89     )
90
91 <h3>Why not use operator names like __add__ and __div__ instead
92  of opAdd, opDiv, etc.?</h3>
93
94     $(P __ keywords should indicate a proprietary language extension,
95     not a basic part of the language.
96     )
97
98 <h3>Why not have binary operator overloads be static members, so both
99 arguments are specified, and there no longer is any issue with the reverse
100 operations?</h3>
101
102     $(P This means that the operator overload cannot be virtual, and
103     so likely would be implemented as a shell around another
104     virtual function to do the real work. This will wind up looking
105     like an ugly hack. Secondly, the opCmp() function is already
106     an operator overload in Object, it needs to be virtual for several
107     reasons, and making it asymmetric with the way other operator
108     overloads are done is unnecessary confusion.
109     )
110
111 <h2>Properties</h2>
112
113 <h3>Why does D have properties like T.infinity in the core language to give the
114 infinity of a floating point type, rather than doing it in a library like C++:
115     $(CODE std::numeric_limits<T>::infinity)
116 ?</h3>
117
118     Let's rephrase that as $(DOUBLEQUOTE if there's a way to express it in the existing
119     language, why build it in to the core language?)
120     In regards to T.infinity:
121
122     $(OL
123     $(LI Building it in to the core language means the core language knows
124     what a floating point infinity is. Being layered in templates, typedefs,
125     casts, const bit patterns, etc., it doesn't know what it is, and is
126     unlikely to give sensible error messages if misused.
127     )
128
129     $(LI A side effect of (1) is it is unlikely to be able to use it
130     effectively in constant folding and other optimizations.
131     )
132
133     $(LI Instantiating templates, loading $(CODE #include) files, etc., all costs
134     compile time and memory.
135     )
136
137     $(LI The worst, though, is the lengths gone to just to get at infinity,
138     implying $(DOUBLEQUOTE the language and compiler don't know anything about IEEE 754
139     floating point - so it cannot be relied on.) And in fact
140     many otherwise excellent C++ compilers
141     do not handle NaN's correctly in floating point comparisons.
142     (Digital Mars C++ does do it correctly.)
143     C++98 doesn't say anything about NaN or Infinity handling in expressions
144     or library functions. So it must be assumed it doesn't work.
145     )
146
147     )
148
149     $(P To sum up, there's a lot more to supporting NaNs and infinities than
150     having a template that returns a bit pattern. It has to be built in to
151     the compiler's core logic, and it has to permeate all the library code
152     that deals with floating point. And it has to be in the Standard.
153     )
154
155     $(P To illustrate, if either op1 or op2 or both are NaN, then:)
156
157 ------
158 (op1 < op2)
159 ------
160     $(P does not yield the same result as:)
161 ------
162 !(op1 >= op2)
163 ------
164     $(P if the NaNs are done correctly.)
165
166 <h2>Why use $(TT static if(0)) rather than $(TT if (0)?)</h2>
167
168     $(P Some limitations are:)
169
170     $(OL
171     $(LI if (0) introduces a new scope, static if(...) does not. Why does this
172     matter? It matters if one wants to conditionally declare a new variable:
173
174 ------
175 static if (...) int x; else long x;
176 x = 3;
177 ------
178
179     whereas:
180
181 ------
182 if (...) int x; else long x;
183 x = 3;    // error, x is not defined
184 ------
185     )
186
187     $(LI False static if conditionals don't have to semantically work. For
188     example, it may depend on a conditionally compiled declaration somewhere
189     else:
190
191 ------
192 static if (...) int x;
193 int test()
194 {
195     static if (...) return x;
196     else return 0;
197 }
198 ------
199     )
200
201     $(LI Static if's can appear where only declarations are allowed:
202
203 ------
204 class Foo
205 {
206     static if (...)
207         int x;
208 }
209 ------
210     )
211
212     $(LI Static if's can declare new type aliases:
213
214 ------
215 static if (0 || is(int T)) T x;
216     )
217 ------
218     )
219
220 )
221
222 Macros:
223     TITLE=Rationale
224     WIKI=Rationale
Note: See TracBrowser for help on using the browser.