«前の日記(2007-08-19) 最新 次の日記(2007-08-21)» 編集

Matzにっき


2007-08-20 [長年日記]

_ アンカテ(Uncategorizable Blog) - 日本が世界に誇るまつもとゆきひろ氏の大きな穴

えーと、要するに「いびつなことはいいことだ」ということなのかな?

私が日本を代表してるような表現は正直こっぱずかしいのだが、 「Windowsのことがわからないおっさん」という表現は 実はあんまりはずれてないかも。

ただし、わからないのは主にWindowsプログラミングであって、 日常的な操作や設定くらいは(人より回り道することはあっても)なんとかできる。

でも、ワードやエクセルやパワーポイントは今でも使えない。

_ [言語] Karsten Wagner's Blog: Better exception handling

Javaは明示的に宣言してない例外がハンドルされていないと コンパイル時にエラーになる。これはこれで素晴らしい機能だけど、 結果としてプログラムコードを繁雑にしてしまう(ことがある)。

元々は以下のようにシンプルなコードだったのだが、

String readData(File file) {
  BufferedReader in = new BufferedReader(new FileReader(file));
  String data = in.readLine();
  in.close();
  return data;
}

コンパイルできるためには例外を捕捉しなければならない。

String readData(File file, String default_value) {
  String data;
  BufferedReader in = null;
  try {
    in = new BufferedReader(new FileReader(file));
    data = in.readLine();
    in.close();
  }
  catch(FileNotFoundException e) {
    return default_value;
  }
  return data;
}

しかし、これでもまだダメだ。close()がIOExceptionを出すかもしれない。 で、最終版はこんな感じ。

String readData(File file, String default_value) {
  String data = default_value;
  BufferedReader in = null;
  try {
    in = new BufferedReader(new FileReader(file));
    data = in.readLine();
  }
  catch(IOException e) {}
  finally {
    if (in != null) {
      try {
        in.close();
      }
      catch(IOException e) {}
    }
  }
  return data;
}

例外の捕捉漏れを検出して、正しいコードを書かせようという言語機能が むしろコードを繁雑にして、「元々やりたいこと」を例外処理に埋没させてしまう危険性がある。 また、こんなに繁雑だとそもそも例外なんか使いたくない(Cみたいにエラーコードにしちゃう) という気にさせられてしまうんじゃないかなあ。それだとむしろ退化を促進しちゃう。

まあ、こんなことするくらいなら上にIOExceptionを投げちゃえばいいのかもしれないけど。

一方、Rubyではこうなる。

def read_data(file)
  begin
    file.readline
  ensure
    file.close
  end
end

でも、これは例外をそのままスルーしてるんで、 上記のJavaコード(最終版)と比較するのは反則かもしれない。

_ [言語] A lack of productivity is killing Smalltalk.

Dolphon開発終了にインスパイアされたエントリふたたび。 「Smalltalkが生産性に欠ける」というのは、 またずいぶん大胆なエントリタイトルだと思う。

が、ここでは「言語としてははるかに劣るJavaが、 その言語としてのなじみやすさや クラスライブラリの蓄積によって 『普通のプログラマ』の生産性を向上させることにより、 Smalltalkエキスパートよりも低コストでのソフトウェア開発を可能にした」という ことが重視されている。

数少ない「選ばれた人」の生産性がいくら高くても、 圧倒的に多い「普通の人」の(安い)生産性にはかなわない、 という視点か。

完全に同意はしないものの、「普通の人に優しい」というのは、 Rubyが唯一LispやSmalltalkに勝っている点だけに、 個人的にはアリな視点だと思う。

_ The Most Excellent and Lamentable Tragedy of Richard Stallman − Edward O’Connor

Stallmanの欠点は他人の感情を理解しない点だと思う、という話。

StallmanはEmacsのある機能を修正することを、 EmacsメンテナのStefan Monnierに依頼した。 しかし、Stefanには娘が産まれたばかりなので、時間が取れなかった。

Stallmanの返答は

I am sorry to hear it. Unless someone else can figure these things out, I guess the release has to wait until you have time.

それは残念だ。ほかの誰かが対処できなければリリースは延期しなければならないな。

他の人がStepanに「おめでとう」を言う中、Stallmanは追い打ちをかけた。

It doesn't take special talents to reproduce -- even plants can do it. On the other hand, contributing to a program like Emacs takes real skill. That is really something to be proud of.

It helps more people, too.

子孫を作るってのには大した才能は要らない。植物でさえできることだ。 それに比べてEmacsのようなプログラムに貢献するのは誇るに足る本当の才能だ。

もっとたくさんの人を助けることになるしね。

いや、そりゃそうなんだけど、ねぇ。

本日のツッコミ(全38件) [ツッコミを入れる]
_ kame (2007-08-28 07:39)

作るのは楽だけど、ちゃんと育てるのは大変だと思うなあ。<br>とはいえ、RMSそれくらいの変人であってほしいという願望もあったり。

_ hhh (2007-08-28 09:14)

そこでほら、C++のRAIIの出番というわけですよ。D言語でもできたんでしたっけ?

_ 通りすがり (2007-08-28 09:25)

もし君が彼女を才能ある人間に育てたらその時にはおめでとうを言うよ。<br>それとありがとうもね。<br><br>・・・とでも続けばそれはそれでかっこいいような気もする文章ですな。

_ x (2007-08-28 10:42)

「子孫を作る方がぼくには楽しかったから」

_ みずしま (2007-08-28 13:38)

他の場所でも言及されてますが、<br><br>def read_data(file)<br> begin<br> file.readline<br> ensure<br> file.close<br> end<br>end<br><br>というRubyコードは反則云々以前に例外を補足して無い<br>「正しく無い」コード(元のコードではIOExceptionを<br>ハンドリングしてデフォルト値を返しているのと比較して)<br>なので、逆説的にチェック済み例外の有効性の立証に<br>なってませんか?

_ まつもと (2007-08-28 13:46)

例外をぎりぎりまで捕捉しないのがRuby流だと思うので、そういう意味では有効性の立証になってるとは思いません。<br><br>より正確には、例外処理は極力単一のメソッドで閉じているべきで捕捉の見逃しは徹底的に防ぐべしというのが達成すべき目標であれば(Javaではそうみたいですね)、チェック例外の有効性は否定しがたいです。が、そうでないならば記述の繁雑さを招きかねない、ということですが。

_ ひろのぶ (2007-08-28 16:03)

> It doesn't take special talents to reproduce <br>ここでは「子孫を作るって」ってぼかしているけど、もうこれは「生殖するには特別な資質は要らない」ぐらいキツイ書き方しているよ。うむ。この辺がRMSがRMSたる所以だな。RMSは根はいい人だし、RMSよりひどい奴はその辺にごろごろいるから、別に気にならないけどね。ちなみに交通機関が混んでいてK2のパーティーに遅れて来たときのRMSは、すごく恐縮していた。あんなRMSを見たのは最初だし、きっと最後だと思う。さすがにK2には頭が上がらない。

_ まつもと (2007-08-28 16:12)

私はRMSはあれでいいと思います。いびつな方がいいって書いたばっかりだし。<br>身近にいるとしんどそうですけど。それは別の話。

_ みずしま (2007-08-28 16:28)

> より正確には、例外処理は極力単一のメソッドで閉じているべきで捕捉の見逃しは徹底的に防ぐべしというのが達成すべき目標であれば(Javaではそうみたいですね)<br><br>Javaにおいては、そのメソッドでチェック例外を処理しない<br>ならばそれを明示せよ(throws)が重要なのであって、それは<br>例外処理が単一のメソッドで閉じているべき、という事を<br>意味しません。より正確に言うなら、あるメソッドがどの<br>チェック例外を投げるかが仕様として明示され、かつその<br>整合性がコンパイラによってチェックされるという点が<br>重要なのであって、単に例外の捕捉漏れを防ぐための<br>機構としてとらえるのは正しく無いと考えます。<br><br>また、そもそもRubyにおいても公開されるクラス/モジュール<br>のメソッドがどの例外を投げるか、というのは明示される<br>必要がありますよね(そうでなければ、例外を処理しようが<br>無い)?Javaのチェック例外がうっとおしいのは、内部的に<br>使われるメソッドにまでそのような明示を強制されるところ<br>にあるのであって、リンク先の問題提起は本質をついていない<br>と思います。

_ みずしま (2007-08-28 16:32)

連続投稿すいません。まつもとさんの上記Rubyコードの<br>問題点は、Javaコードの<br>readData(File file, String defaultValue)<br>のがIO例外が起きた場合にdefaultValueを返すように<br>なっているのに、Rubyコードには対応する処理が存在しない<br>ことです。Javaでもthrowsによって上位のメソッドに<br>処理を委譲できるのにreadDataではあえてそうしていない<br>わけですから、Ruby流がどうかという問題ではなく、<br>単純に比較として意味をなしていません。

_ まつもと (2007-08-28 16:38)

使用としての例外がプログラム中に記述され、それがコンパイル時にチェックされるという意味でチェック例外は静的型と同じ性質を持っていると思います。もちろん、それは厳密なチェックが行われるのでよいことなのですが、記述が多くなってうっとうしいこともあるという点も静的型と同じでしょう。<br><br>リンク先は「うっとうしい」というのが本質であると思うので、私は「本質をついていない」とは思いません。Javaにそれがあるべきではない、とか、チェック例外はいつも悪い、という主張であればそれは間違っていると思いますが。

_ まつもと (2007-08-28 16:47)

えーと、では<br><br>def read_data(file, default)<br> begin<br> file.readline<br> ensure<br> file.close<br> end<br>rescue<br> default<br>end<br><br>で、どうでしょう?

_ (2007-08-28 17:27)

だとしたらJavaの方は、<br><br>String readData(File file, String default_value) {<br> try {<br> BufferedReader in = new BufferedReader(new FileReader(file));<br> try {<br> return in.readLine();<br> }<br> finally {<br> in.close();<br> }<br> }<br> catch (IOException e) {<br> return default_value;<br> }<br>}<br><br>じゃないでしょうか。型指定とブラケット以外は同じ構造だと思います。

_ まつもと (2007-08-28 17:34)

それってnewで例外起きたらinが未初期化じゃありませんか?<br>それともそれらでは例外は起きないのかな。私はJavaに疎いんで

_ ひがき (2007-08-28 17:34)

例外って発生したら即座に補足しなきゃならないってものじゃないでしょ。<br>ちゃんとその例外に対処できる所で補足すればいいんだから。<br><br>常にその場で対処できるんなら、わざわざ例外にしなくても、戻り値でいいと思う。

_ (2007-08-28 17:36)

コンストラクタで例外が発生してinが初期化されなくても、内側のtryブロックにはまだ入っていないので、finallyは実行されません。だから問題ないです。

_ まつもと (2007-08-28 17:39)

あ、そう読むんですね。うかつ。<br>どうもありがとうございました。

_ 元職業プログラマ (2007-08-28 20:34)

>例外って発生したら即座に補足しなきゃならないってものじゃないでしょ。 <br>>ちゃんとその例外に対処できる所で補足すればいいんだから。<br>という考え方は、私は怖いですね。特にI/Oエラーやメモリ不足、数値演算等の例外は。

_ hhh (2007-08-28 20:50)

その程度の実力でJavaに関してあーだこーだ言ってるんですか・・・<br>正直、失望しました。

_ 元職業プログラマ (2007-08-28 21:24)

hhhさん<br>どれほどの実力をお持ちかは存じませんが、どなたに対して仰っているのかぐらいは、はっきりさせたほうが宜しいのではないでしようか?

_ かるくらん (2007-08-28 22:44)

Matz氏も既存言語に詳しい人がJavaを勉強する時にはまる落とし穴に見事にはまってますね:-p<br>一般的に言われるExceptionにはJavaではErrorが相当し、<br>ExceptionはErrorの特殊な一形態という形になってます。<br><br>私も過去に同じ勘違いをして恥をかきましたw

_ odz (2007-08-28 23:08)

> かるくらん さん<br>何がどうはまっているのか良く分かりませんし、なにが一般的なのかは知りませんが、<br>Java で Exception が Error の一形態なんてことはありまえsん。<br>Error => Throwable の間違いでしょうか。

_ みずしま (2007-08-28 23:25)

> 記述が多くなってうっとうしいこともあるという点も静的型と同じでしょう。 <br><br>これは本題からはずれるのですが、静的型一般の問題として<br>は間違いですよね。それは陽に型付けすることの問題点<br>であって、静的型付けの問題点では無いです。チェック済み<br>例外に関しても、例外仕様(throws)を推論することは原理的<br>に可能なはずです。<br><br>> リンク先は「うっとうしい」というのが本質であると思うので、私は「本質をついていない」とは思いません。<br><br>リンク先では、少なくとも本文においては、catchを強制<br>されるという観点からうっとうしいという事を主張している<br>ように見えます。私の主張は、Javaのチェック例外では<br>catchを強制されるからうっとおしいというのはおかしい<br>(catchを強制されるわけではないので)、うっとおしい<br>のは、常に例外をcatchするかthrowsするかを明示すること<br>を求められるために、それが必要でない箇所(privateな<br>メソッド等)においてもthrows宣言をしなければならない<br>ことである、ということです。catchに関してはRubyで<br>あろうとJavaであろうと、どのみち必要な箇所ではしなければ<br>ならないので、Javaだから記述量が増えるという事は無い<br>はずなので、チェック例外の問題点とは基本的に無関係<br>です。

_ ku-ma-me (2007-08-29 02:42)

Java のチェック例外の問題は、世のプログラマに安易に catch(Throwable _) { } みたいなコードを書かせることだと思います。そのコードを改良・拡張したとき、新たに発生した例外が握りつぶされてることに気がつかなくて大混乱です。<br>とはいえチェック例外と非チェック例外 (?) は一長一短なので、選択できたらいいのになぁと妄想します。書き捨て時やプロトタイピング時は非チェック例外で、大体できてきて完成度を高めたいならチェック例外で。

_ (ぱ) (2007-08-29 03:11)

意図的に脱線します。<br><br>>特にI/Oエラーやメモリ不足、数値演算等の例外は。 <br><br>I/Oエラーはともかくとして、数値演(ArithmeticException)もまあなんとかなるとして、OutOfMemoryErrorっていちいちcatchしてて現実的にJavaプログラムが書けるんでしょうか。

_ (ぱ) (2007-08-29 03:32)

>書き捨て時やプロトタイピング時は非チェック例外で<br><br>私なら、書き捨て時やプロトタイピング時は<br><br>catch (Exception e) {<br> e.printStackTrace();<br> System.exit(1);<br>}<br>ですね。死んだプログラムは嘘をつかないので、うっかりプロトタイプのコードが本番に適用されても安心です。

_ 元職業プログラマ (2007-08-29 08:49)

(ば)さん<br>>I/Oエラーはともかくとして、数値演(ArithmeticException)もまあなんとかなるとして、OutOfMemoryErrorっていちいちcatchしてて現実的にJavaプログラムが書けるんでしょうか。<br>http://www.javainthebox.net/laboratory/JavaSE6/heap/heap.html<br>は、参考にならないでしょうか?<br>>死んだプログラムは嘘をつかないので、うっかりプロトタイプのコードが本番に適用されても安心です。<br>本当に安心出来るかどうかは分かりませんが、ひがきさんの考え方よりは、安心できますね。

_ HoGe (2007-08-29 10:37)

JavaのExceptionはインタフェース実装の際にちょっとウザイです。<br>実装元がvoid p()だとすると、実装としてvoid p() throws ~~は許されないのです。<br>考え方的には、投げる例外の種類も含めてのインタフェース実装と考えたらわかりやすいかもしれません。<br><br># 一方@Overrideアノテーションではthrowsの差異は許されていたり。

_ みずしま (2007-08-29 13:47)

> 実装元がvoid p()だとすると、実装としてvoid p() throws ~~は許されないのです。 <br><br>throwsがメソッドの*仕様の*一部であることを考えれば、<br>ごく自然なことです。もしそれがコンパイルを通るなら、<br>例えば以下のようなコードで、A#pが実際にはIOException<br>を投げるのに、それをコンパイラ側で検出することができ<br>ません。<br><br>interface A {<br> void p();<br>}<br><br>class B implements A {<br> public void p() throws IOException { ... }<br>}

_ みずしま (2007-08-29 13:56)

補足ですが、これはそもそもインタフェース実装のときに<br>限った話ではなく、クラス継承の場合も全く同じです。<br><br>#@Overrideアノテーションは全く関係無いと思います。<br>#@Overrideアノテーションをつけようがつけまいが、<br>#継承時にthrowsで投げる例外の種類を増やすとコンパイル<br>#エラーになるはずです。

_ (ぱ) (2007-08-30 00:58)

元職業プログラマさん:<br>>http://www.javainthebox.net/laboratory/JavaSE6/heap/heap.html <br>>は、参考にならないでしょうか? <br>なりません。この話はまさに「ひがきさんアプローチ」であり、対処できる場所(呼び出し階層で上の方)でOutOfMemoryを捕まえる話じゃないですか。<br>元職業プログラマさんが推奨する「例外って発生したら即座に補足」というアプローチなら、どこで例外が発生したかは明白なので、スタックトレースが取れるかどうかは問題にならないはずです。

_ 元職業プログラマ (2007-08-30 09:47)

(ば)さん<br>分かりづらかったかもしれませんが、私は先の発言では、以下の二つの関連はあるが独立した内容についてコメントを行わせていただいております。<br>ご返答は、この事をご理解いただいた上でのものでしょうか?<br><br>>I/Oエラーはともかくとして、数値演(ArithmeticException)もまあなんとかなるとして、OutOfMemoryErrorっていちいちcatchしてて現実的にJavaプログラムが書けるんでしょうか。<br><br>>死んだプログラムは嘘をつかないので、うっかりプロトタイプのコードが本番に適用されても安心です。<br><br>話は変わりますが、<br>>元職業プログラマさんが推奨する「例外って発生したら即座に補足」というアプローチなら、どこで例外が発生したかは明白なので、スタックトレースが取れるかどうかは問題にならないはずです。<br>プログラムのデバッグ効率や試験効率、保守性を上げる事を考えれば、問題になると思っています。<br>私は、主にC言語での話しですが、少ないコード追加で、いかにこの事を実現するのかという事について、長年労力を費やして来ました。

_ n (2007-08-30 15:23)

D言語の例置いておきますね。<br><br>import std.stream;<br>string readData(string file, string default_value) {<br> auto data = default_value;<br> try {<br> auto _in = new File(file);<br> scope(exit)_in.close();<br> data = _in.readLine();<br> }<br> catch(StreamException e) {}<br> return data;<br>}

_ 元職業プログラマ (2007-08-30 18:15)

nさん<br>やはりD言語はスッキリしていますね。<br>C++の例もあると嬉しいのですが。<br>ところでHaskellでは、例外の補足はどうやるのでしょうかね。

_ (ぱ) (2007-08-31 08:31)

元職業プログラマさん:<br>>ご返答は、この事をご理解いただいた上でのものでしょうか? <br>そのつもりです。<br>わからなくなってきたのですが、「元職業プログラマ」さんが推奨するアプローチでは、OutOfMemoryErrorをどう扱うのでしょうか。<br>(1)ひとつひとつのnewを全部try catchで囲む?<br>(2)あるいは、せめてメソッドごとに補足する?

_ 元職業プログラマ (2007-08-31 15:24)

>わからなくなってきたのですが、「元職業プログラマ」さんが推奨するアプローチでは、OutOfMemoryErrorをどう扱うのでしょうか。<br>特殊なプログラムで無い限り、(1)という選択は無いと思います。<br>後は状況次第かと思います。

_ ardbeg1958 (2007-09-01 05:22)

RMSの話。その記事を先まで読んでいくと彼はアスペルガー(高機能自閉症)なんだよと書いてあります。その裏付けは?とも思いますが、その話題(病気のこと)に触れているネット上の記事は一つだけではありません。自閉症関連の書籍のページにも現れますし、それは信憑性の高いことなのでしょう。<br>だとすると RMS のエキセントリックな面も理解できるような気がします。同時に本人が抱える困難さも。もちろんこのことはソフトウェアの世界における RMS の価値を減じるものではありませんが。

_ 元職業プログラマ (2007-09-02 21:56)

(ば)さん<br>>わからなくなってきたのですが、「元職業プログラマ」さんが推奨するアプローチでは、OutOfMemoryErrorをどう扱うのでしょうか。<br>私の考えをお分かりいただけましたでしょうか?

お名前:
E-mail:
コメント:
[]

«前の日記(2007-08-19) 最新 次の日記(2007-08-21)» 編集

track feed Matzにっき Creative Commons License This work is licensed under a Creative Commons License.