java-jaで例外処理の話をしてきました

ブログを書くまでがjava-jaですが、もう眠いのでとりあえず1行だけ書いて、あとは徐々に書き足す。

会場を無料提供してくれたグリーさん、ありがとうございます!

誰かが検査例外の話をするだろうと思って書かなかったら結局誰も言及しなかった、Javaのコミュニティなのに。

っていうか聴衆が100人もいると、もしかしてそもそも「検査例外ってなに?」って人もいたんじゃないか?「検査例外がOCPを壊す」とか「Liskovの置換原則のLiskov」とか通じてるんだろうか?とりあえず直和型が通じてないことだけはひしひしと感じた。

Twitterの自分の発言を転載しておく。

ちなみにZen of Pythonでも「エラーを握りつぶすな」と書いてあります

禅 of Python: 20の格言

「例外はそもそも何のため」ってところ、ざっくり省いたんだけどもそういうところのほうがニーズあったかね??

C#1.0のusingにやっと追いついた」

達人プログラマー「すべての例外ハンドラを除去してもこのプログラムは動作できるか?」

過剰防衛はDRYでないだけで、防衛が足りないことに比べれば害は少ないのでは。常に真な条件を除去できないコンパイラがタコなだけじゃないか(待て

「例外は呼び出す側が契約条件を満たしたが呼び出された側が契約を履行できなかった時に投げるもの」 by t_wada なるほどたしかにその視点はすっきりしているな

「チェック例外の代償はOCPに反すること」はClean codeの中で言われてるのか。

バウンダリに関する問題。UI、ネットワークIO、粒度の大きいシステム間通信・モジュール間結合、こういう部分が単体テストでは拾いにくいバグの温床なのでそれにセンシティブなログ管理があってしかるべきでは」

→具体的には、ログレベル・ネームスペースの他にもう一軸足す。よむべき:ログは、もっと立体的であるべきか。 - 設計と実装の狭間で。

tanakh「関数がnullを返すかどうかわからないし」太一「Javadoc読めばよい」tanakh「人間が書いたドキュメントとか信じられない」

「ドキュメント読めよ」は正論だが、現実の問題を見るとドキュメント読んでないことに起因しているバグが実在するわけで「ドキュメントを読まないエンジニアがいる」という現象は不可避。

Eitherを超簡単に説明すると、整数型を返す関数がエラーを投げうるときに、それを「『整数またはエラー』型を返す関数だ」って考えるみたいなー→Eitherの説明を書いているうちにunwindの説明を求められるなどwww→HaskellからJavaへの同時通訳とか無理だからww→直和型って言われて分からない人にMaybeが説明なしでわかるとも思えないので補足すると「『Just整数またはNothing』という型を……とか書いてたら同時通訳のためのマイクを渡されたでござる

Javaで、あるクラスの値(仮にIntとする)を返す関数があったら、返り値がもしかしたらnullかもしれない。「当然その関数がnullを返しうるかどうかJavadocで確認して、適切にnullチェックをするif文をかいてから使うべき」が太一派で「ドキュメントは信頼出来ない。Intって型にIntとして使えないnullが入ってるのがそもそもおかしい。『Just IntまたはNothing(nullみたいなもの)』っていう新たな型を作って関数の返り値をその型にすれば、Nothingを間違えてIntとして使うミスは発生し得ない」が田中派、この『Just IntまたはNothing』って型が『Maybe Int』だ。

ちなみに「間違う方法があれば必ず誰かが間違う。nullチェックを忘れる人は必ず出現する。エラーは忘れることのできない方法で通知する必要がある。現状大部分の言語はMaybeを持ってなくて例外を持ってるから例外を使って通知するのがよい」が西尾派です。


さらに言えば「ハッシュから値を取り出す時に、キーが存在するかチェックする方法もデフォルト値を指定して取得する方法も用意してあるにもかかわらず、それらを使わずにアクセスしてくるのはプログラマが『そのキーが存在する』という信念を持っている状態だ。そのキーが存在しないならそれは誤った信念なので速やかに例外を投げて過ちに気づかせるべきである。nullとかnilとかundefinedを返してんじゃねえよ」も西尾派です。

太一の「例外は使わせない」って話に僕は「例外を積極的に使うべき」って反論したんだけども、結局のところ彼の主張は「多人数でコードを書く際に、全員が例外について深く知って正しい設計で例外を使うことを要求するのはコストが高いし必要でもない」だったので、まあその「多人数で」って条件をつけるなら賛成できる。僕の主張は「一人で難易度の高いコードを書く際には、自分がなにか過ちを起こしたらすぐ気づくことができるように想定外の状態を発見したらすぐに例外を投げるべき」だったので、ようは応用分野の違いだな。

ちなみにLinuxカーネルのコーディング規約ではgotoを使ってリソース処理をすることを薦めている

http://linuxjf.sourceforge.jp/JFdocs/kernel-docs-2.6/CodingStyle.html

ノリツッコミで「GCあるからいいじゃん」って言ったけども、もちろん「対にならなきゃいけない処理」はメモリ確保と解放だけではない。ロックとアンロックなども間で例外が飛んだ時にどうなるべきか考える必要がある。

例外を投げるかもしれない関数を使うということは、対にならないといけない処理の片方をやって、もう片方をやる前に例外が投げられて、違うコンテキストにジャンプしてしまった時に何が起こるかを考えなければならないということだ。その対になる処理の例の一つが「メモリを確保、その後解放」であって、その問題に関してはGCが解決してくれる。しかし「ロック獲得、その後解放」に関してはGCは何もしてくれない。だからGCがあれば例外安全性に関して何も考えないでいいっていうのは間違い。RAIIを使ってリソース管理の問題にすり替えるとか、STMが解決してくれるかも、とか。

Googleのコーディング規約で例外が禁止されている」ってのはFUDhttp://www.textdrop.net/google-styleguide-ja/cppguide.xml#%E4%BE%8B%E5%A4%96 に和訳されているのでちゃんと理由を読むこと。

表面的には、例外を使うメリットはそのコストを上回ります。新規プロジェクトの場合にはなおさらです。しかし既存のコードに例外を導入しようとすると、依存関係のあるコードすべてに影響が及びます。...

クライアント側で捕まえる話は、あとで発表資料を再読する必要性を感じた。

「2回やってダメだった処理はだいたいもうダメ」何度も何度もリトライしてるんじゃねぇ、問題を切り分けるための処理をしろ、との主張。

ryopeko: そういえば家庭と例外処理っていうタイトルで @t_wada に基調講演するの忘れてた #java_ja

Fail-fastが重要ですね。まず呼び出し元が意図的に自分を不快にすることがないという至極もっともな仮定をする。すると、もし自分が不快になったならばそれは、僕の実装に関する呼び出し元の理解に誤りがあるわけであり、これを改善するには例外を投げて呼び出し元に問題を通知する必要がある。

まあもちろん例外を投げた時に適切に対処できる設計になっているかどうかは例外を投げる前に確認しておかないと、実家に大域脱出したり死んだりするかもしれない点には注意が必要だ。

#java_ja ところでhaskellで「呼び出し側が得たい形でエラーが通知される」ってのは「返り値だけが異なる関数のオーバーロードができる」という性質に依っているので、javaだと全部入りなクラスを作ってas_eitherとかas_maybeみたいなメソッド作るしかないんじゃ?

エラー処理を書いてはいけない

型宣言がめんどくさくて、型宣言がない言語をつくってみたけどやっぱ型はあったほうがよくて、それで型推論できるところは書かなくてよくしようよ、ってなったのと同じように、Javaの検査例外もめんどくさくて、大部分の言語が検査例外を捨ててしまったが、将来的には例外推論とかが入ってプログラマを幸せにするべきではないか。

できる!並列・並行プログラミング

なぜC++は整数などを例外として投げられるのか。そうした方がよかった積極的な理由があるのか、それとも投げられるものの集合を限定することに価値を見出さなかったという消極的な理由なのか。

t_wadaの「例外は仕様で記述された範囲の補集合」って話も印象的だった、思い出したのでメモメモ。