読者です 読者をやめる 読者になる 読者になる

契約プログラミングとエラー処理

関数のシグネチャは、その関数がどのように使われるべきものかを規定する。これはその関数を使うユーザとの間の取り決めのようなものである。

しかし、シグネチャだけでは表現の幅に限界がある。例えば以下の関数を考えてみる。

double sqrt(double num);

これは数値の平方根を計算するありきたりな関数のシグネチャだ。この段階で、この関数の引数と戻り値の型が規定され、使い方はかなり限定されている。

しかし、この関数はあくまでユーザが0以上の数値しか入力しないという期待の上に成り立っている。このような場合、通常関数内でエラーチェックを行うことだろう。

double sqrt(double num){
    // 入力の正しさをチェック
    assert(num >= 0);
    // 処理の本体
}

また、自分で実装した関数の場合、戻り値も論理的に整合性のある値になっているか確かめたいときがある。

double sqrt(double num){
    assert(num >= 0);
    // 処理の本体

    // 出力の正しさをチェック
    assert(result >= 0);
    return result;
}

このように、ある関数を使うユーザは、関数が望む入力を与えるという義務を果たす代わりに、適切な計算結果を受け取る権利を得る。これをユーザと関数の間の契約であると考えるのが、契約プログラミングの基本的な考え方だ。

Dは契約プログラミングを明示的にサポートしている。上のsqrtのコードは以下のように契約を記すことができる。

double sqrt(double num)
// 入力チェック
in{
    assert(num >= 0);
}
// 出力チェック
out(result){
    assert(result >= 0);
}
// 関数本体
body{
    // 処理の本体
    return result;
}

このように、inのあとのスコープに入力に関する制約を、outのあとに出力に関する制約を書くというスタイルによって、契約が守られているかを確かめることができる。

ここまで話を聞くと、「全ての関数の入力と出力を契約プログラミングのスタイルで書けば安全でハッピーじゃん」と思うかもしれないが、ここで1つ疑問がある。エラーチェックの方法は契約だけではないのだ。Dには契約による方法の他に、assert、enforce、例外などのエラー処理機構が用意されている。これらはどのように使い分けるのが正しいのだろう?

ここで重要な事実が1つある。DではC言語などと同様に、適切なコンパイルオプションを付けることで、最終的なリリース版のプログラムからはassertを除外することが可能なのだ。assertだけではない、契約に関する処理も、-releaseというコンパイルオプションを付けることで除外可能だ。これにより、わずかでもプログラムの処理速度を向上させることができる。

一方、enforceはassertとほぼ同じ機能を持っているが、-releaseを付けてもコンパイラに除去されない。例外処理は言わずもがなである。

以上の事実と、「プログラミング言語D」に書かれていた説明を私なりに解釈して、以下のようにまとめてみる。

  • プログラムの内部ロジック的に、間違っていることが許されない関数の入出力チェックには契約を用いる
  • プログラムの内部ロジック的に、間違っていることが許されない何かしらのエラーチェックに対してはassertを用いる
  • ユーザからの入力を受け付けたり、標準ライブラリのように誤った入力を与えられる可能性があったりと、"状態としては取り得るが間違っている"ような場合で、エラーチェックに失敗したら即プロセスを殺したい場合はenforceを使う
  • 上記のような場合で、エラーチェックに失敗した後に何かしらの処理を行いたい場合は例外を使う

上記の分類は私なりの解釈であり、間違っているかもしれない。恐らくだいたい合ってるだろう。

契約を使うべき場面の例としては、privateなメソッドが一番分かり易いだろう。これはユーザから直接触れることができないが、見えないところできっちり仕事をこなしている必要がある。

エラーチェックはプログラミングの中でも欠かしてはならないものである。Dでこれを論理的に美しく扱えるようになっているのは非常に嬉しい。私自身、使い所がまだ完全に理解できていないが、これからたくさんコードを書きながら勘を掴んでいきたい。

今日は本当はdelegateについて書きたかったが、不勉強ゆえにまだ書くことができなかった。これもDの特徴が現れている面白い機能なので、そのうち書きたいと思う(次回書くかはわからない)。

参考:プログラミング言語D

プログラミング言語D

プログラミング言語D