The Trouble with Checked Exceptions

JavaからC#に移った人は、C#にはなぜ検査例外がないのか? と疑問に思うと思います。それに対するC#作者Anders Hejlsbergのインタビュー記事を訳してみました(いままでにもまして訳に自信がないところが多いんですが)。

原文はこちら。
http://www.artima.com/intv/handcuffs.html

拙著「プログラミング言語を作る」内でも少し言及しています(p.340)。

ところで、JavaC#の例外処理の違いというと「検査例外の有無」が取り上げられることが多いのですが、「スタックトレースが生成されるタイミング」も異なっており、Javaプログラマはたまにはまることがあります。その点も「プログラミング言語を作る」では言及しておりますのでぜひどうぞ(宣伝)。

関連記事:
MSDN内の記事
http://msdn.microsoft.com/en-us/vcsharp/aa336812.aspx
チェック例外がJavaにあってC#にない理由 - じゅんいち☆かとうの技術日誌にて紹介あり。

http://www.ibm.com/developerworks/jp/java/library/j-jtp05254/
Summary

Anders Hejlsberg, the lead C# architect, talks with Bruce Eckel and Bill Venners about versionability and scalability issues with checked exceptions.

C#アーキテクト長であるAnders Hejlsbergが、Bruce Eckel、Bill Vennersと、検査例外のversionabilityとスケーラビリティの問題について会話します*1

Anders Hejlsberg, a distinguished engineer at Microsoft, led the team that designed the C# (pronounced C Sharp) programming language. Hejlsberg first vaulted onto the software world stage in the early eighties by creating a Pascal compiler for MS-DOS and CP/M. A very young company called Borland soon hired Hejlsberg and bought his compiler, which was thereafter marketed as Turbo Pascal. At Borland, Hejlsberg continued to develop Turbo Pascal and eventually led the team that designed Turbo Pascal's replacement: Delphi. In 1996, after 13 years with Borland, Hejlsberg joined Microsoft, where he initially worked as an architect of Visual J++ and the Windows Foundation Classes (WFC). Subsequently, Hejlsberg was chief designer of C# and a key participant in the creation of the .NET framework. Currently, Anders Hejlsberg leads the continued development of the C# programming language.

Anders Hejlsbergは、Microsoftの著名なエンジニアであり、C#(Cシャープと発音されます)の設計チームを率いました。Hejlsbergはまず1980年代前半にMS-DOSCP/M向けのPascalコンパイラを作ることでソフトウエア業界に躍り出ました。Borlandと呼ばれる非常に若い会社はすぐにHeijlsbergを雇って彼のコンパイラを買い取りました。それは後にTurbo Pascalとして販売されることになりました。
Borlandでは、HejlsbergはTurbo Pascalの開発を続け、最終的にはTurbo Pascalを代替するもの:Delphiの設計チームを率いることになりました。1996年、Borlandでの13年の後、HejlsbergはMicrosoftに加わりました。そこでは彼は最初はVisual J++とWindows Foundation Classes(WFC)のアーキテクトとして働いていました。その後、HejlsbergはC#のチーフデザイナと、.NET Frameworkの構築における主要メンバーとなりました。現在、Anders Hejlsbergは、C#プログラミング言語の継続的な開発を率いています。

On July 30, 2003, Bruce Eckel, author of Thinking in C++ and Thinking in Java, and Bill Venners, editor-in-chief of Artima.com, met with Anders Hejlsberg in his office at Microsoft in Redmond, Washington. In this interview, which will be published in multiple installments on Artima.com and on an audio CD-ROM to be released this fall by Bruce Eckel, Anders Hejlsberg discusses many design choices of the C# language and the .NET framework.

2003年7月30日、Thinking in C++とThinking in Javaの著者であるBruce EckelおよびArtima.comの編集主任であるBill Vannersが、ワシントンのレドモンドにあるMicrosoftの彼のオフィスでAnders Hejlsbergに会いました。このインタビューは、今秋にはArtima.comとオーディオCD-ROMでBruce Eckelにより複数回に分けて公開されるでしょうが、Anders HejlsbergはC#言語と.NET frameworkにおける多くのデザイン上の選択について議論しています。

  • In Part I: The C# Design Process, Hejlsberg discusses the process used by the team that designed C#, and the relative merits of usability studies and good taste in language design.
  • In this second installment, Hejlsberg discusses versionability and scalability issues with checked exceptions.
  • Part I: The C# Design Processにおいては、HejlsbergはC#を設計したチームが使用したプロセスについて論じ、さらに言語設計の場において、使い勝手の調査を行うことと、良質なセンスを有していること、この優劣について議論しています。
  • 2回目となる今回は、Hejlsbergは検査例外のversionabilityとスケーラビリティにおける問題について議論します。

検査例外について中立的立場にとどまる(Remaining Neutral on Checked Exceptions)

Bruce Eckel: C# doesn't have checked exceptions. How did you decide whether or not to put checked exceptions into C#?

Bruce Eckel: C#には検査例外がありません。あなたは、C#に検査例外を入れるかどうか、どのように決めましたか?

Anders Hejlsberg: I see two big issues with checked exceptions: scalability and versionability. I know you've written some about checked exceptions too, and you tend to agree with our line of thinking.

Anders Hejlsberg: 私が見るに、検査例外にはふたつの大きな問題があります。スケーラビリティとversionabilityです。私は、あなたが検査例外についていくつか書いているのも知っていますが、あなたは我々の一連の考えにどちらかというと同意しているようです。

Bruce Eckel: I used to think that checked exceptions were really great.

Bruce Eckel: 私はかつては検査例外が本当にすばらしいと思っていました。

Anders Hejlsberg: Exactly. Frankly, they look really great up front, and there's nothing wrong with the idea. I completely agree that checked exceptions are a wonderful feature. It's just that particular implementations can be problematic. By implementing checked exceptions the way it's done in Java, for example, I think you just take one set of problems and trade them for another set of problems. In the end it's not clear to me that you actually make life any easier. You just make it different.

Anders Hejlsberg: まさしく。率直に言って、検査例外は傑出してすばらしく、何の悪いところもないアイディアに見えます。私は検査例外がすばらしい機能であることに完全に同意します。単に特定の実装が問題があるかもしれない、というだけです。たとえば、Javaで行われている検査例外の実装によって、あなたはひとそろいの問題をとりあげて、それを別の問題と交換しているだけだと私は考えます。結局、私にとって、あなたが人生を少しでも簡単にしているかどうかは明白ではありません。あなたは単にそれを異なるようにしているだけです*2

Bruce Eckel: Was there a lot of disagreement in the C# design team about checked excpetions?

Bruce Eckel:C#設計チームにおいて、検査例外について意見の食い違いはありましたか?

Anders Hejlsberg: No, I think there was fairly broad agreement in our design group.

Anders Hejlsberg: いいえ。われわれの設計チームには、かなり幅広い同意があったと思います。

C# is basically silent on the checked exceptions issue. Once a better solution is known――and trust me we continue to think about it――we can go back and actually put something in place. I'm a strong believer that if you don't have anything right to say, or anything that moves the art forward, then you'd better just be completely silent and neutral, as opposed to trying to lay out a framework.

C#は検査例外の問題について基本的に沈黙しています。ひとたびよい解決方法が見つかったなら――我々がそれについて考え続けているということを信じて欲しいのですが――我々は戻ってきてそれを実際にしかるべき場所に配備することができます*3。私は、あなたが何か正しい言い分や、技術を前進させる何かを持っているのでないのなら、あなたは完全に沈黙を保ち中立であるべきだ、ということを強く信じています。フレームワークを拡張しようとするのとは反対に。

If you ask beginning programmers to write a calendar control, they often think to themselves, "Oh, I'm going to write the world's best calendar control! It's going to be polymorphic with respect to the kind of calendar. It will have displayers, and mungers, and this, that, and the other." They need to ship a calendar application in two months. They put all this infrastructure into place in the control, and then spend two days writing a crappy calendar application on top of it. They'll think, "In the next version of the application, I'm going to do so much more."

もしあなたが駆け出しプログラマにカレンダーコントロールを書くように頼んだら、彼らはひそかに思うことでしょう。「よし、世界一のカレンダーコントロールを書いてやろう。それはカレンダーの種類に対して多態となって、displayerやmungerやあれやこれやを持っている」。彼らはそれを2ヵ月後に出荷する必要があります。彼らはこれらのインフラストラクチャをすべてコントロールに突っ込み、そして、その上にくだらないカレンダーコントロールを書くのに2日かけます。彼らは、「アプリケーションの次のバージョンでは、もっとずっとよいものにします」と言うでしょう。

Once they start thinking about how they're actually going to implement all of these other concretizations of their abstract design, however, it turns out that their design is completely wrong. And now they've painted themself into a corner, and they have to throw the whole thing out. I have seen that over and over. I'm a strong believer in being minimalistic. Unless you actually are going to solve the general problem, don't try and put in place a framework for solving a specific one, because you don't know what that framework should look like.

あるとき彼らが彼らの抽象的な設計の他のすべての具体化を実装しようと考え始め、しかし、その設計が完全に間違っていると判明します。そして今、彼らは自分の落ち度で困った状態に陥り、すべてを放り投げなければなりません。私は何度もそういうケースを見ました。私はミニマリズムの熱烈な信奉者です。実際に一般的な問題を解決しないなら、特定の問題を解決するためにフレームワークをいじくろうとしないでください。なぜならあなたはフレームワークがどうあるべきかについてまだわかっていないのですから。

Bruce Eckel: The Extreme Programmers say, "Do the simplest thing that could possibly work."

Bruce Eckel: エクストリームプログラミングプログラマは、「動くであろう最も単純なことをやる」と言います。

Anders Hejlsberg: Yeah, well, Einstein said that, "Do the simplest thing possible, but no simpler." The concern I have about checked exceptions is the handcuffs they put on programmers. You see programmers picking up new APIs that have all these throws clauses, and then you see how convoluted their code gets, and you realize the checked exceptions aren't helping them any. It is sort of these dictatorial API designers telling you how to do your exception handling. They should not be doing that.

Anders Hejlsberg: はい。アインシュタインはそれを「物事は可能な限りシンプルにやれ。手抜きでない限り」と言いました。検査例外について私が思うことは、それはプログラマにかけられた手錠だということです。あなたが、throws節を持つ新しいAPIを手に入れたプログラマを見たとします。その後、あなたはそれらのコードがどれくらい複雑になるかわかります。そして、あなたは、検査例外がそれをまったく助けていないということを認識するのです。いわば、APIの設計者たちのほうが支配的になって、例外処理の方法をあなたに指図しているのです。そんなことをさせるわけにはいきません。

検査例外とversioning(Versioning with Checked Exceptions)

Bill Venners: You mentioned scalability and versioning concerns with respect to checked exceptions. Could you clarify what you mean by those two issues?

Bill Venners: あなたは検査例外に関してスケーラビリティとバージョン関連のことについて言及しました。このふたつの問題が意味することをはっきりさせて頂けますか?

Anders Hejlsberg: Let's start with versioning, because the issues are pretty easy to see there. Let's say I create a method foo that declares it throws exceptions A, B, and C. In version two of foo, I want to add a bunch of features, and now foo might throw exception D. It is a breaking change for me to add D to the throws clause of that method, because existing caller of that method will almost certainly not handle that exception.

Anders Hejlsberg: versioningから始めましょう。この問題はかなり理解しやすいので。私が、例外A, B, Cを投げるかもしれないメソッドfooを作ったとしましょう。fooの第2バージョンでは、私は多くの特徴を加えたいと思います。よってfooは例外Dを投げるかもしれません。メソッドのthrows節に例外Dを付け足すことは、破壊的な変更です。なぜなら、既存の呼び出し元は、ほぼ確実にその例外をハンドルしないからです。

Adding a new exception to a throws clause in a new version breaks client code. It's like adding a method to an interface. After you publish an interface, it is for all practical purposes immutable, because any implementation of it might have the methods that you want to add in the next version. So you've got to create a new interface instead. Similarly with exceptions, you would either have to create a whole new method called foo2 that throws more exceptions, or you would have to catch exception D in the new foo, and transform the D into an A, B, or C.

新しいバージョンで、新しい例外をthrows節に書き足すことは、クライアントのコードを破壊します。それはインタフェースにメソッドを加えるようなものです*4。あなたがインタフェースを公開した後は、それは事実上変更不能になってしまいます。なぜなら、いかなるインターフェースの実装も、将来のバージョンであなたが付け加えたくなるメソッドまで含んでいてくれればよいですが、そういうわけには行かないからです。よって、あなたは代わりに新たなインタフェースを作らなければなりません。同様に、あなたはより多くの例外を投げるメソッドfoo2をまるごと作り直すか、新しいfooの中でDをcatchして、それをA, B, Cのどれかに変換しなければならないでしょう。

Bill Venners: But aren't you breaking their code in that case anyway, even in a language without checked exceptions? If the new version of foo is going to throw a new exception that clients should think about handling, isn't their code broken just by the fact that they didn't expect that exception when they wrote the code?

Bill Venners: しかしあなたは検査例外のない言語においても、いずれにせよそれらのコードを破壊していませんか? fooの新しいバージョンが、クライアントがハンドルすべきである新しい例外を投げる場合、呼び出し元のコードは、彼らがそのコードを書いたときにその例外を予期しなかったという事実によって破壊されるのではないですか?

Anders Hejlsberg: No, because in a lot of cases, people don't care. They're not going to handle any of these exceptions. There's a bottom level exception handler around their message loop. That handler is just going to bring up a dialog that says what went wrong and continue. The programmers protect their code by writing try finally's everywhere, so they'll back out correctly if an exception occurs, but they're not actually interested in handling the exceptions.

Anders Hejlsberg: いいえ。なぜなら、多くの場合、人々はそれを気にかけないからです。どうせ彼らはそれらの例外をどれもハンドルしないでしょう。最低限の例外ハンドラが彼らのメッセージループの外側にいます。そのハンドラは、何がおかしくなったのかということを示すダイアログを表示し、処理を続行することでしょう。プログラマは、彼らのコードを、try finallyをあらゆるところに書くことで保護しようとしており、よって例外が起きたとき彼らは正しくもみ消しますが、例外をちゃんとハンドルすることには興味を持っていないのです。

The throws clause, at least the way it's implemented in Java, doesn't necessarily force you to handle the exceptions, but if you don't handle them, it forces you to acknowledge precisely which exceptions might pass through. It requires you to either catch declared exceptions or put them in your own throws clause. To work around this requirement, people do ridiculous things. For example, they decorate every method with, "throws Exception." That just completely defeats the feature, and you just made the programmer write more gobbledy gunk. That doesn't help anybody.

throws節は、少なくともJavaの実装においては、あなたに必ずしも例外のハンドルを強制しません。しかし、あなたがそれをハンドルしなくても、あなたに正確にどの例外があなたのメソッドをすり抜けていくかを認識することを強制します。それはあなたに、宣言された例外をcatchするか、自分のメソッドのthrows節に記述するかのどちらかを要求しているわけです。この要求の中でどうにかしようとするために、人々はおかしなことをします。たとえば、彼らはすべてのメソッドを「throws Exception」で修飾してしまうのです。これは、(検査例外の)特徴を完全にぶち壊しており、あなたは単にプログラマにわけのわからない汚物を書かせただけなのです。それは誰も助けません。

Bill Venners: So you think the more common case is that callers don't explicitly handle exceptions in deference to a general catch clause further up the call stack?

Bill Venners: それではあなたはより一般的なケースでは、呼び出し元はコールスタックのもっと上に一般的なcatch節があることを想定して、例外を陽にハンドルしないと思いますか?

Anders Hejlsberg: It is funny how people think that the important thing about exceptions is handling them. That is not the important thing about exceptions. In a well-written application there's a ratio of ten to one, in my opinion, of try finally to try catch. Or in C#, using statements, which are like try finally.

Anders Hejlsberg:
どうしたわけか、例外について重要なのはそれをハンドルすることだと考えられていますが、それはおかしな話です*5。それは例外について重要なことではありません。よく記述されたアプリケーションにおいて、私の意見では、try finallyはtry catchの10倍もあるのです。または、C#においては、try finallyに似たusing文となります。

Bill Venners: What's in the finally?

Bill Venners: finallyの中には何があるのでしょうか?

Anders Hejlsberg: In the finally, you protect yourself against the exceptions, but you don't actually handle them. Error handling you put somewhere else. Surely in any kind of event-driven application like any kind of modern UI, you typically put an exception handler around your main message pump, and you just handle exceptions as they fall out that way. But you make sure you protect yourself all the way out by deallocating any resources you've grabbed, and so forth. You clean up after yourself, so you're always in a consistent state. You don't want a program where in 100 different places you handle exceptions and pop up error dialogs. What if you want to change the way you put up that dialog box? That's just terrible. The exception handling should be centralized, and you should just protect yourself as the exceptions propagate out to the handler.

Anders Hejlsberg: finallyの中では、あなたは例外から自分自身を防御することになります。しかし、自分で例外をハンドルするわけではありません。あなたは例外処理をどこか他の場所に置くわけです。近代的なUIのようなどんな種類のイベントドリブンアプリケーションにおいても、あなたは、通常メッセージポンプの外側に例外ハンドラを置きます。そして、あなたは、そこまで落っこちた例外のみをハンドルするのです。しかし、あなたは、あなたが確保したあらゆるリソースを開放等することで、自分自身のコードを確実に防御できます。自分で後始末するので、あなた自身は常に一貫した状態にあります。あなたは100の異なった場所において、自分で例外をハンドルし、エラーダイアログをポップアップするプログラムを欲しくはありません。あなたがそのダイアログボックスを提供する方法を変えたい場合、どうなるでしょうか? それはまさにひどいものとなります。例外処理は集結されるべきです。そして、例外がハンドラの外に伝播するとき、あなたはただ自分自身を防御すべきです。

検査例外のスケーラビリティ(The Scalability of Checked Exceptions)

Bill Venners: What is the scalability issue with checked exceptions?

検査例外に関するスケーラビリティの問題とは何でしょうか?

Anders Hejlsberg: The scalability issue is somewhat related to the versionability issue. In the small, checked exceptions are very enticing. With a little example, you can show that you've actually checked that you caught the FileNotFoundException, and isn't that great? Well, that's fine when you're just calling one API. The trouble begins when you start building big systems where you're talking to four or five different subsystems. Each subsystem throws four to ten exceptions. Now, each time you walk up the ladder of aggregation, you have this exponential hierarchy below you of exceptions you have to deal with. You end up having to declare 40 exceptions that you might throw. And once you aggregate that with another subsystem you've got 80 exceptions in your throws clause. It just balloons out of control.

Anders Hejlsberg: スケーラビリティの問題は、versionabilityの問題とも関連します。小さなプログラムでは、検査例外はとても魅力的です。小さな例で言えば、あなたは、FileNotFoundExceptionをちゃんと捕捉したことを確認した、ということを示すことができます。これってすごくない? さて、あなたがひとつのAPIだけを読んでいる場合、これはすばらしいものです。あなたが4つか5つの異なるサブシステムと対話するような大規模システムを作り始めた時に問題が始まります。各サブシステムは、4〜10の例外を投げます。いまや、あなたはモジュールの階層を上がるたびに、指数関数的に増加する、対処すべき例外を持つことになります。あなたは結局、投げる可能性のある40個の例外を宣言しなければなりません。そして、ひとたびそれを別のサブシステムと組み合わせると、あなたはthrows節に80個の例外を並べることになります。それは制御不能なほど膨張していきます。

In the large, checked exceptions become such an irritation that people completely circumvent the feature. They either say, "throws Exception," everywhere; or―and I can't tell you how many times I've seen this―they say, "try, da da da da da, catch curly curly." They think, "Oh I'll come back and deal with these empty catch clauses later," and then of course they never do. In those situations, checked exceptions have actually degraded the quality of the system in the large.

大規模システムでは、検査例外は、人々が完全にその特徴を回避してしまうほどの苛立ちとなります。彼らはまた、いたるところで「throws Exception」と口にします*6。または――私はこれを何回見たか言うことができません――「try, なんとかかんとか、catch { }」。彼らはこう考えます。「ああ、あとで戻ってきてこの空のcatch節をちゃんと対応しよう」。そして、もちろん彼らはその後決してそれをやりません。こういうシチュエーションでは、検査例外は、実際に大規模システムの品質を下げました。

And so, when you take all of these issues, to me it just seems more thinking is needed before we put some kind of checked exceptions mechanism in place for C#. But that said, there's certainly tremendous value in knowing what exceptions can get thrown, and having some sort of tool that checks. I don't think we can construct hard and fast rules down to, it is either a compiler error or not. But I think we can certainly do a lot with analysis tools that detect suspicious code, including uncaught exceptions, and points out those potential holes to you.

そして、あなたがこれらの問題すべてを受け入れるときでも、我々が何らかの検査例外のメカニズムをC#に組み込む前にもっと検討が必要に見えます。そうは言っても、どんな例外が投げられるかを知ることができ、かつそれをチェックするなんらかのツールがあれば、確実に途方もない価値があります。コンパイルエラーにするか、それ以外のところに落とし込むか、いずれにしろ、私は杓子定規なルールを構築してもよいとは考えていません。しかし、我々は、(catchされない例外を含む)疑わしいコードを検出し、潜在的な穴を指摘してくれる分析ツールを使って多くのことができると思います*7

感想

いやもう全然納得できない。メソッドシグニチャは利用者側との契約なんだから、契約が変わるのなら呼び出し側に対応が必要なのは当たり前だし、対応していないのならエラーを出してくれなければ困る。下位モジュールの例外はラッピングすればいいし。「どうせみんなちゃんと対応しないんだから検査例外不要」と言われてもなあ。

2010/1/10追記:
メールで色々ご指摘をいただいたので反映しました。ありがとうございました。
もともと脚注をつけていたところには、注番号がずれないように、「ご指摘により修正」という注を入れています(修正箇所はそこに限るわけではありません)。

*1:versionabilityって、意味はわかるのですが、いい訳語が思いつかないので英語のままとします。

*2:ここのitってlifeのことで、easierにすることはできてなくてdifferentにしているだけだ、ということでよいんでしょうか。

*3:ご指摘により修正

*4:ここのinterfaceとmethodは、C#の文法要素としてのinterfaceとmethodでいいんでしょうか……インタフェースにメソッドを追加してもクライアントのコードは壊さないので、意味が通らないような。

*5:ご指摘により修正

*6:ご指摘により修正。

*7:points outの主語は何なんでしょう? toolsならpoint"s"にはならないはずですし……