forked from dlang/dlang.org
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathdbc.dd
167 lines (145 loc) · 5.84 KB
/
dbc.dd
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
Ddoc
$(SPEC_S 契約プログラミング,
"契約" は、大きなプロジェクトのプログラミングの手間を減らす、
画期的な技術です。契約は、事前条件・事後条件・エラー・不変条件、
からなる概念です。
C++でも言語の変更無しに契約は実現できますが、
扱いにくく、
一貫性のないものになります。
<p>
言語レベルで契約のサポートを組み込むことには、こんな利点があります:
$(OL
$(LI 契約の記述の一貫性)
$(LI ツールのサポート)
$(LI 契約の記述に基づく、
コンパイラによるよりよいコード生成)
$(LI 契約の管理や強制の簡単化)
$(LI 契約の継承)
)
$(COMMENT <img src="images/d4.gif" alt="Contracts make D bug resistant" border=0>)
$(P "契約"は、考え方は簡単で、常にtrueにならなくてはいけない式、のことです。
trueでなければ契約が破られていて、つまり定義から、プログラムにバグがあることがわかります。
契約はプログラムの仕様の一部分で、それをドキュメントではなくコード自身に書くようにした、
とも言えます。プログラマの皆さんはご存じのように、ドキュメントというのは不完全で、
古かったり間違っていたり、時にはどこにも存在しなかったりします。
契約をコードの中に持ってくることで、プログラムによる検証が可能になります。)
<h2>表明契約</h2>
一番基本的な契約は、
$(GLINK2 expression, AssertExpression).
です。$(B assert) は、コードにチェック可能な式を埋め込み、
その式がtrueでなくてはならない、という条件を課します:
------
assert(expression);
------
C プログラマにはお馴染みでしょう。
しかし、C とは違い、関数本体での <code>assert</code>
<code>AssertError</code> を投げる働きをします。
これはcatchして対応することができます。 契約違反をcatchするのは、
絶対にフェイルプルーフでなくてはならないコードから他の動作の怪しいコードを
使う必要があるときや、デバッグ時には役に立ちます。
<h2>事前・事後条件</h2>
前契約では、文が実行される前に成立していなければならない事前条件を記述します。
もっとも典型的な使い方は、関数の入力引数の検査でしょう。後契約は、
文の実行結果を検査します。もっとも典型的な使い方は、
関数の返値と副作用の検査でしょう。
構文は次のようになります:
------
in
{
...事前条件...
}
out (result)
{
...事後条件...
}
body
{
...コード...
}
------
定義から、前契約違反であれば、
bodyは間違った引数を渡されていることになります。
そこで AssertError 例外が投げられます。後契約に対する違反があれば、body
にバグがあったことになります。このときは AssertError 例外が投げられます。
<p>
<code>in</code> と <code>out</code> 節のどちらかを省略することもできます。
関数本体のための <code>out</code> 節では、
変数 <code>result</code>
が宣言され、関数の返値が代入されます。
例えば、平方根の関数を実装してみましょう:
------
long square_root(long x)
in
{
assert(x >= 0);
}
out (result)
{
assert((result * result) <= x && (result+1) * (result+1) >= x);
}
body
{
return cast(long)std.math.sqrt(cast(real)x);
}
------
in, out, body の中の assert は <dfn>契約 (contract)</dfn>
と呼ばれます。
assert以外の任意のDの文や式も
記述することができますが、
そのコードに副作用が無いように注意することが重要です。
リリース版のコードは契約のコードに依存してはいけません。
リリースビルドでは、
in と out 用のコードは生成されません。
<p>
関数の返値型がvoidの時は、返値がないので、out節で変数
result を宣言するのは不可能です。
この場合は、次の形式を使います:
------
void func()
out
{
...契約...
}
body
{
...
}
------
out節では、$(I result)
は関数の返値で初期化されます。
<h2>in, out と、継承</h2>
$(P I関数が基底クラスの関数をオーバーライドしたものである時は、
その関数自身かまたは基底クラス達の
$(D in) 契約のうち、
どれか一つが満たされればよいことになります。
関数のオーバーライドは、
$(D in) 契約を $(I より緩く)
していることになります。
)
$(P $(D in) 契約を持たない関数は、
関数の引数として任意の値を許すという意味になります。これはつまり
$(D in) 契約を持たない関数が継承階層の中にあると、
その関数をオーバーライドした関数の $(D in)
契約は何も効果を持たないことになります。
)
$(P 逆に、$(D out) 契約は全て満たされる必要がありますから、
関数のオーバーライドは、
$(D out) 契約を
$(I より厳しく) していることになります。
)
<h2>クラス不変条件</h2>
$(P クラス不変条件は、
常に成り立っているクラスの性質を記述するものです。
(ただしメンバ関数の実行途中は除く。)
$(DDLINK class, Classes, クラス) の項で説明しています。
)
<h2>参考資料</h2>
$(LIST
$(LINK2 http://people.cs.uchicago.edu/~robby/contract-reading-list/, Contracts Reading List),
$(LINK2 http://jan.newmarch.name/java/contracts/paper-long.html, Adding Contracts to Java)
)
)
Macros:
TITLE=契約プログラミング
WIKI=DBC
CATEGORY_SPEC=$0