try-catch方式・ハンドラ方式 - オブジェクト指向と型システムの狭間で例外を考える その3

 例外考察シリーズ。

try-catch方式のおさらい

 例外のthrowというのは、コンパイラあるいはランタイムのレベルで、メソッドの戻り値を拡張することで表現できる。

処理中になんらかの異常事態が発生して、文字列表現じゃない何かを返さざるをえない状況ってのに直面したとする。するとString型の戻り値じゃそんなの表現出来ないから困る。

 さてここで、原始的に解決する場合のコードを考えてみよう。

public static class Result {
    /** 戻り値 */
    public Object value;
    /** 成否フラグ */
    public boolean success;
}

といった型をつくり、すべてのメソッド呼び出しの戻り値をこの型にしてしまう。文字列型を戻り値にするから、失敗時の返却値に困るのだ。そうした状態をもった型で戻り値をラップしてやればいい。

 ここでは成否フラグだけを持たせたけども、必要に応じて失敗時の情報をより詳細にわかるように情報を追加することもできる。

オブジェクト指向と型システムの狭間で例外を考える - プログラマーの脳みそ

 このように戻り値を拡張することと、その戻り値によって分岐処理を行うこと。正常時に実行するロジック、異常時に実行するロジック、あるいはcatchされない場合はさらに呼び出し元にreturnする、といった条件分岐だ。

関数呼び出しの概念の拡張

 これは、関数の内部から何かの値を返した場合に、その呼び出し元で条件分岐をさせる、という新しいパラダイムだ。
 呼び出される関数(メソッド)の内側が、呼び出し元をコントロールしているんだ。この概念の拡張が汎化させ洗練すれば面白いものになるのか、あるいはgotoのような忌まれるものになるのかはよく分からない。

 例外処理は、あるメソッドの戻り値の型の特定のフラグの値と、その際に実行されるロジックの対応付けと考えることができる。Map<例外フラグ, 関数>といったモノを用意すればいい。

 Javaはロジックの塊を関数型言語のように自在に扱うことができないから、catch節はcatch節としてロジック中に不動であるわけだけども、catch節のようなブロックがそのまま関数として扱えるならば、という話。

try-catch方式、ハンドラ方式

 さて、Javaの例外処理ではこの例外フラグ-関数の対応付けMapはtry-catch節で表現される。このMapはメソッドの呼び出し元が持つ情報となる。

 これを、メソッドに渡して管理を任せてしまうという別体系の例外処理を考察してみよう。メソッドの戻り値で分岐するのではなく、メソッドに一緒にエラーハンドラを渡す、という考え方だ。

 try-catch方式ではメソッド呼び出しの戻り値を拡張した。ハンドラ方式ではメソッド呼び出しの入力を拡張する。

 メソッドは必要とされるハンドラをhandler句に並べるとしてみよう。

public void hoge() handler IOException, SQLException {
    // ...
}

 これに対して呼び出し元は関数をクロージャ的なブロックとして渡す、あるいは関数ポインタのような変数を用いて渡すことができるようにする。

hoge()
    IOException : {System.out.println("I/O例外");}
    SQLException : {System.out.println("SQL例外");}

 メソッドの多階層呼び出しの場合、現在のメソッドが保持しているハンドラを下位のメソッドに伝播させるとする。

public void piyo() handler IOException, SQLException {
    // ...
}
public void hoge() handler IOException, SQLException {
    piyo(); // ハンドラの指定が不要
}

 try-catch方式が、投げられる可能性のある検査例外すべてがcatchされることを検査するのに対し、ハンドラ方式は検査例外が発生する可能性のあるメソッドすべてに正しくハンドラが渡されていることを検査する

ハンドラの推論

 多階層でメソッドを呼び出す場合、必要とされるハンドラは自動的に検出して欲しい。先の例だと

public void piyo() handler IOException, SQLException {
    // ...
}
public void hoge() handler * {
    piyo(); // ハンドラの指定が不要
}

程度の記述か、あるいはhandler句が不要であって欲しい。ただし、これはIDEが高度に発達していて、その場で必要なハンドラがすぐに分かるという開発環境でないと不便だろう。コンパイラによって検査して報告するという手法もあるが、修正が試行錯誤的になるのがいただけない。

 また、全く利用されない無駄なハンドラもまた警告する必要があるだろう。

まとめ

 この稿では、

  • try-catch方式での例外はメソッド呼び出しの戻り値の拡張であることを指摘した
  • 戻り値のフラグによって呼び出し元で条件分岐を行うという拡張がされていることを指摘した
  • ハンドラ方式でメソッド呼び出しの入力を拡張し、例外処理関数を渡すという方式を提案した
  • ハンドラ方式でも検査例外を実装できる可能性を検討した

 ハンドラ方式のほうが概念として美しいかもしれないし、実は呼び出し元の条件分岐というのが汎用的で美しい理論となる可能性を秘めているのかもしれない。

 例外処理の方法論はもっといろんな視点があってもいいんじゃないだろうか。