例外処理

id:lethevert:20050714:p2 の続き。
Javaなどで使われている例外処理の機構だけれど、どれだけ書いても好きになれないのは僕だけなのでしょうか?例外が発生した時点で、例外を検知してより後のコードへジャンプして、そこで例外処理を書くというアイデアそのものにいまいち納得できないことがあるのです。つまり、例外処理とは、goto的であり、さらに例外の発生箇所が明示的でないという意味でgotoより悪い(可読性を低めている)と思うのです。
つまり、こんなコードがあったとして、

void main()
{
  int x = -1;
  try{
    x = exceptionalFunction(x);
    System.out.println("=======結果=======");
    System.out.println(x);
  }catch(SomeException e){
    System.err.println("入力値の範囲が不正です");
  }
}

int exceptionalFunction(int x)
  throws SomeException
{
  if(x <= 0){
    throw new SomeException;
  }else{
    return 2*x*x;
  }
}

サンプルを書いているだけで、気持ち悪いのですが、なぜ、こう書かないのか?

void main()
{
  int x = -1;
  if(x <= 0){
    System.err.println("入力値の範囲が不正です");
  }else{
    x = conditionalFunction(x);
    System.out.println("=======結果=======");
    System.out.println(x);
  }
}

int conditionalFunction(x)
  with x > 0
{
  return 2*x*x;
}

with句は創作ですが、引数の値に条件をつけるということを表しています。
あらかじめ、呼び出す前に引数の範囲チェックをして、チェックを通った場合のみ、関数を呼び出すほうが、明らかに意図もプログラムの動作もわかりやすくなります。しかし、この引数の範囲チェックという機能を保証してくれるコンパイラには、残念ながらいまだに出会ったことがありません。(もし、存在しているなら、教えてください → 通行人の方へ)
さらに、Javaではおせっかいなことに、throws句があるとtryを要求されるので、呼出前チェックをやろうとすると、こんな冗長で気持ちの悪いコードが生まれてしまいます。

void main()
{
  int x = -1;
  if(x <= 0){
    System.err.println("入力値の範囲が不正です");
  }else{
    try{
      x = exceptionalFunction(x);
      System.out.println("=======結果=======");
      System.out.println(x);
    }catch(SomeException e){
      System.err.println("入力値の範囲が不正です");
    }
  }
}

int exceptionalFunction(int x)
  throws SomeException
{
  if(x <= 0){
    throw new SomeException;
  }else{
    return 2*x*x;
  }
}

強調表示したtry節は明らかに冗長です。しかし、Javaコンパイラは、その冗長性を理解できなおバカさんなので、必ず書くことを要求されます(*1)。しかも、悪いことに、呼出前チェックの部分は、プログラマが呼出先の関数の中を読んで導き出した条件なので、このような記述は、関数のモジュール強度を低下させています。
かくして、スマートな例外処理を意図したコードが、予想以上に不安定なコードを生成してしまうというパラドックスが起こるで、例外機構は今ひとつ好きになれないのです。
*1 だからといって、これを要求されないというのは、誰がその例外に責任を持つのか不明になってしまうのでよくないのです。だから、Javaでゼロ除算がRuntimeExceptionになっているのは、日和っているとしか思えない。