forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathconst-faq.dd
More file actions
294 lines (242 loc) · 13.1 KB
/
const-faq.dd
File metadata and controls
294 lines (242 loc) · 13.1 KB
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
Ddoc
$(D_S const(FAQ),
$(P D の $(D const)/$(D immutable) システムは独特なものなので、
多くの質問があると思われます。)
$(UL
$(ITEMR const, 何故 D には const があるのか?)
$(ITEMR principles, D の const の設計を決める原理は何か?)
$(ITEMR transitive-const, $(I 推移的 const) とは何か?)
$(ITEMR head-const, $(I 頭部 const) とは何か?)
$(ITEMR tail-const, $(I 尾部 const) とは何か?)
$(ITEMR logical-const, $(I 論理的 const) とは何か?)
$(ITEMR readonly, 読み取り専用ビューの意味で $(I readonly) を使わないのは何故か?)
$(ITEMR java-const, 何故 Java は const を排除したのか?)
$(ITEMR cpp-const, C++ の const とどう違うのか?)
$(ITEMR invariant-strings, なぜ文字列がimmutableなのか?)
$(ITEMR const-parameters, 関数の引数がデフォルトで const でないのは何故か?)
$(ITEMR static-members, クラスの静的メンバは推移的 const でカバーされるのか?)
$(ITEMR invariant, $(I immutable) は何が嬉しいのか?)
)
$(ITEM const, 何故 D には const があるのか?)
$(P D 2.0 の const と immutable に対するフラストレーションが表明されることは多く、
どんな価値があるのかという疑問もよく耳にします。
)
$(OL
$(LI 関数のインターフェイスがより自己説明的 (self-documenting) になります。
推移的 const なしでは、ポインタ/参照に関する const 性は、
ドキュメントに頼るしかなくなってしまいます
(そしてドキュメントというのはいつも抜けがあったり古かったり間違っていたりするものです)
推移性のない C++ の const は、この目的ではほとんど意味のない物体であり、
それが C++ のプログラマが慣習によってこの問題を解決しがちな理由です。
)
$(LI インターフェイスを信頼できるものにします。
これは多くの人が同じコードに関わるようになればなるほど重要さが増してきます。
言葉を換えると、スケーラビリティが高いのです。
巨大なチームからなるプロジェクトに関わる人々は、
const なしではコンパイラに規約を強制させる術がないため、
物事がずっと大変になってしまうと言っています。チームが大きくなればなるほど自体は悪化します。
API の管理は巨大なプロジェクトではクリティカルな問題です - これが (極端な例でいえば) BASIC
がスケールしない理由です。
)
$(LI const の推移性は、いくつか興味深い最適化の機会を提供します。
この事実の価値はまだまだこれから考察や活用の余地があります。
)
$(LI そしてこれが最大のポイントです。ポイント 1 ~ 3 はこれに比較すると些細なものです。
これからのプログラミングは、マルチコア、マルチスレッドになります。
そのようなプログラミングを容易にする言語こそが求められるものとなるでしょう。
推移的 const は D をこのパラダイムへと移行させる鍵となります。
Haskell や Erlang の流行が、このようなトレンドが来ることの証拠とも言えます
(これらの言語の killer feature は、マルチプログラミングの容易さです)。
C++ は、簡単な方法でマルチプログラミングをサポートするように適応するのは難しいでしょう。
D もまだその段階ではありませんが、その方向へ向かっています。そして、
推移的 const がそのための絶対的な基礎となる技術です。
)
)
$(P もちろん、シングルスレッドのそんなに大きくないプログラムを一人で書く場合であれば、
const はそこまで有用ではないでしょう。
D の const は単にそれを使わないことで、あるいは D 1.0 を使うことで、
無視することができます。const が強制される唯一の箇所は、
書き換え不可の文字列型 string のみです。
)
$(ITEM principles, D の const の設計を決める原理は何か?)
$(OL
$(LI 数学的に健全であること。つまり、
合法的に const を逃げる術がないこと。)
$(LI すべての型を構造体でラップでき、ラップした構造体が
依然として同じ const に関する挙動を示すこと -
言い方を変えると、特定の型に対するマジカルな規則などがないこと。)
$(LI const の挙動が推移的であること。)
$(LI const の挙動がすべての型 T に関して同じであること。)
)
$(ITEM transitive-const, $(I 推移的 const) とは何か?)
$(P 推移的 const とは、型に const が適用されたら、
再帰的のその型の全ての部分にまで const が適用されることをいいます。したがって、次の例のようになります。
)
---
const(int*)** p;
p += 1; // ok, p は mutable
*p += 1; // ok, *p は mutable
**p += 1; // エラー, **p は const
***p += 1; // エラー, ***p は const
---
$(P 推移性があると、
$(I mutable int への const ポインタ) を定義することは不可能になります。
)
$(P C++ の const は推移的ではありません。)
$(ITEM head-const, $(I 頭部 const) とは何か?)
$(P 頭部 const は、const がそれに隣接した型にだけ適用されるタイプの
const です。例として
)
---
headconst(int**) p;
---
$(P は、p は $(I mutable int への mutable なポインタへの const なポインタ)
という意味になります。D には頭部constはありません (上の $(CODE headconst)
は単なる例示です) が、C++ の const
は頭部 const システムです。
)
$(ITEM tail-const, $(I 尾部 const) とは何か?)
$(P 尾部 const は頭部 const をちょうど反転したものです -
const 型から到達できるものは、トップレベルだけを除いて全て const になります。
例として:
)
---
tailconst(int**) p;
---
$(P は、p は $(I const int への const ポインタへの mutable ポインタ)
という意味になります。頭部 const と尾部 const を両方合わせると、
推移的 const になります。
D には $(CODE tailconst)
という特別な型コンストラクタはありません (上の例は単なる例示です)。
)
$(ITEM logical-const, $(I 論理的 const) とは何か?)
$(P $(I 論理的 const) とは、外側からは状態が変わらないように見えるが、
実際は物理的には const でないデータのことを言います。
例として、遅延評価をするオブジェクトがあります:)
---
struct Foo {
mutable int len;
mutable bool len_done;
const char* str;
int length()
{ if (!len_done)
{ len = strlen(str);
len_done = true;
}
return len;
}
this(char* str) { this.str = str; }
}
const Foo f = Foo("hello");
bar(f.length);
---
$(P この例では、$(CODE f.len) の計算は必要なときだけ行われます。
$(CODE Foo) は、外側からの観察者にとっては
オブジェクトから得られる値は構築後変化しないので、論理的const です。
$(CODE mutable) という修飾子は、
たとえ $(CODE Foo) のインスタンスが const であっても、そのフィールドが依然として変更可能なことを示します。
C++ では論理的 const の概念がサポートされていますが、D では違います。
また、D には $(CODE mutable) 修飾子もありません。
)
$(P 論理的 const の問題点は、const が推移的でなくなることです。
推移的でないということは、スレッドによるレース条件が潜在し、
opaque な const 型が mutable
なメンバを持つかそうでないか判別する術がなくなるということです。
)
$(P 参考文献:
$(LINK2 http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter08_025.html, mutable: bitwise vs. logical const)
)
$(ITEM readonly, 読み取り専用ビューの意味で $(I readonly) を使わないのは何故か?)
$(P $(I readonly)はソフトウェアではすでに
ROM / Read Only Memory / 書き換え不可メモリという意味が確立されています。
ハードウェアによるメモリ保護機能のあるコンピュータでは、
readonly にはメモリの内容が置き換わらないという意味もあります。
D で readonly をメモリの読み取り専用ビューの意味で使うことは、
別の参照やthread経由でデータが変更されうることを考えると、慣習と反しています。
)
$(ITEM java-const, 何故 Java は const を排除したのか?)
$(P $(LINK http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4211070)
)
$(ITEM cpp-const, C++ の const とどう違うのか?)
$(P C++ には他の全ての言語の中でもっとも D に近い const システムがありますが、
やはり大きな違いがあります:)
$(OL
$(LI const が推移的でない)
$(LI immutable がない)
$(LI const オブジェクトに mutable メンバを持たせられる)
$(LI const を合法的にキャスト外ししてデータを変更可能)
$(LI $(CODE const T) と $(CODE T) が必ずしも別の型になるとは限らない)
)
$(ITEM invariant-strings, なぜ文字列がimmutableなのか?)
$(P $(LINK2 http://drdobbs.com/blogs/architecture-and-design/228700475, Immutable Strings)
)
$(ITEM const-parameters, 関数の引数がデフォルトで const でないのは何故か?)
$(P ほとんど (ほぼ全て?) の関数の引数は変更されませんから、
デフォルトを const にして、
変更したいときにだけ特別な記述をする方が理にかなっているようにも思えます。
この方法の欠点は:
)
$(OL
$(LI 過去の D の習慣、そして
C, C++, Java, C#, などでの習慣を大きく乱すことになります。)
$(LI 新しい予約語、$(CODE mutable) のようなものが必要になってしまいます。)
$(LI 一番まずいのが、宣言に一貫性がなくなることです:
---
void foo(int* p) {
int* q;
...
}
---
$(CODE p) は const を指しますが、$(CODE q) は mutable を指すことになります。
この手の一貫性のなさはあらゆる種類のミスを呼びます。
また、
型を扱うジェネリックなコードを書くのが非常に難しくなってしまいます。
)
)
$(P $(CODE in) を使用することで、
$(CODE const) で修飾する醜さをやわらげることは可能です:)
---
void str_replace(in char[] haystack, in char[] needle);
---
$(ITEM static-members, クラスの静的メンバは推移的 const でカバーされるのか?)
$(P クラスの静的メンバはプログラムのグローバル状態の一部であり、
オブジェクトの状態の一部ではありません。従って、
mutable な静的メンバを持つクラスが
そのクラスのオブジェクトの推移的 const 性を違反するということはありません。
)
$(ITEM invariant, $(I immutable) は何が嬉しいのか?)
$(P immutable なデータは、一度初期化されたら二度と変化しません。
この性質には様々な利用法があります:
)
$(UL
$(LI immutable データへのアクセスは、
複数のスレッドによって読み取りが行われる場合でも同期は不要です)
$(LI Data race、tearing、順序一貫性 (sequential consistency)、
キャッシュ一貫性 (cache consistency) などは全て、
immutable なデータを扱うときには問題になりません。)
$(LI Pure 関数が引数にとることができるのは immutable データのみです。)
$(LI データ構造の $(I deep copy) を行うとき、
immutable な部分はコピーの必要がありません。)
$(LI immutable 性によって、
実際は参照で渡されるデータを値であるかのように扱うことが可能になります。
(文字列が、このケースに当てはまるよく使われる例です))
$(LI immutable 型は、プログラマに、より self-documenting
な情報を提供します。)
$(LI immutable データは、ハードウェア的に保護された読み取り専用メモリや、
ROMに配置することすら可能です。)
$(LI immutable データの中身が変わるとしたら、
それは確実にメモリ破壊のバグなので、
そのようなデータ一貫性を自動的にチェックすることが可能です。)
$(LI immutable 型によって、
さまざまな最適化の可能性がもたらされます。)
)
$(P $(I const) は mutable な世界と immutable な世界の橋渡しとなります。
const で引数を受け取ることで、一つの関数で mutable なデータと immutable
なデータの双方を扱うことができます。)
)
Macros:
TITLE=const(FAQ)
WIKI=constFAQ
CATEGORY_FAQ=$0
ITEMR=$(LI $(LINK2 #$1, $+))
ITEM=<hr><h3><a name="$1">$+</a></h3>