構造化例外処理
D版にてbreakを再実装。
http://p22.aaacafe.ne.jp/~qwerty/private/queen/page/0047.html
http://p22.aaacafe.ne.jp/~qwerty/private/queen/page/0049.html
これで例外処理も済ませてしまう事にしました。
現行の例外処理の問題点として、どの関数がどの例外を投げるのか、そのtryブロックを読んだだけではわからない、というのがあります。Javaなんかではthrowsをシグネチャとして明記しますが、それでも、関数を呼んでいるところにはその情報は記載されず、IDEの機能を使わないといけません。ちょっとずれてます。
関数の返値も、変数に受け取るなり更に引数として使われるなり、定義なんか見なくても使用箇所を見るだけで、何かに使われている事が確認できます。返値の扱いを明記しない時即ち返値を無視している時は返値がフローに関らない時なのです。
返値も例外も関数のOutputであることに違いは無いのに、例外は使用側が意識していなくてもフローに関ってきます。これが、例外処理が精神的に気が重い原因じゃないかなあ、と。
Adaの呼びだし側での仮引数明示のように、例外も、呼びだし側で明記できたらもうちょっと気軽に使える読めるようになるのではないかと思うのです。
で…例えば例外オブジェクト(または例外のメタクラス)を引数として渡して、それを投げてもらうことにすれば、関数の呼びだし側にも、その関数が投げる例外の名前が書かれる事になるため、改善になるのではないか、と考えたり。
C++であれば関数内でクラス定義できますので、対応関係を取るのも楽じゃないかと。
int read() { class file_not_found: public exception{}; class can_not_convert: public exception{}; int const default_value = 100; try{ file f("a.txt", file_not_found()); return str_to_int(f.read_line, can_not_convert()); }catch(file_not_found){ return default_value; }catch(can_not_convert){ message("invalid data in a.txt"); return default_value; } }
このコードは動きません。
で…もう一歩進めて考えると、受けとり側が投げるものを指示できるなら、継承階層なんて要らないんです。複数の例外を同じハンドラで処理させるためにクラスという分類機構を使わずとも、単に同じハンドラで処理させたいエラーには同じ例外を発生させるように指示すればいいだけです。無理やりOOPを絡めなくても、ずっとシンプルになりそうです。
デメリットとして例外が追加情報を持てなくなりますが、たいがいの言語の例外オブジェクトはメッセージ文字列を持っていますけど、エラー表示の際にそれをそのまま使ったりするのはアプリ側の怠慢だよなあ…とか。まあ、それがデバッグの助けになっているのは確かですので、確実にデメリットなんですけど…ま、まあいいか、みたいな。
…という思考を経て、Queenではラベルを変数に入れられるようになりました。breakは内部的に例外を投げます。こういう無謀な思いつきを実装してしまえるのは、趣味コンパイラの特権です。逆に使い辛いようでしたらいつ反故にしてしまっても痛くも無いのも趣味コンパイラの特権です。(現状if文すら未実装ですのでbreakだけあってもしょーがないんですけどね…)
あとaaacafeがzipを制限してるみたいですので今度から7z。(CyberXに指摘貰いました)