«前の日記(2003-05-12) 最新 次の日記(2003-05-14)» 編集

Matzにっき


2003-05-13 [長年日記]

_ [原稿] 原稿提出

Linux Magazine 7月号の原稿はさきほどしあがりました。

予告通り、タイトルは『XMLとYAML(その2)』。 この日記での議論もある程度反映したものになっています。 みなさん、ありがとうございました。

_ [Ruby]stdin, stdout, stderr, $defout, $deferr

[ruby-talk:71112]などでSimon Strandgaardくんが要求していることが ずっとわからなかったのだが(お互い英語は得意じゃないし)、 しばらく彼のメールを読むうちにやっと分かってきた。

要するに彼はRubyインタプリタをC++プログラムに組み込むことを行っているわけだが、 その時に、$stdin, $stdoutなどに代入した効果をRubyの中だけで閉じたい、と考えているようだ。 これは

  • Rubyプログラムの中からstdinなどが繋ぎ換えられたくない
  • Rubyプログラムからsystemなどで呼んだプログラムの出力がstdoutなどに出て欲しくない。

ということから来ているもののようだ。しかし、いくら組み込みだとはいえ、 stdinなどはプロレスプロセス全体でひとつずつしか持てない資源なわけで、 それをRubyの世界の中だけで繋ぎ換えるのは無理な話である。 彼にはあきらめてもらうしか。

しかし、それはそれとして、この議論は私には有意義であった。 つまり、Rubyに足りないと思われる点がいくつか見つかったからだ。

ひとつは、エラー出力のリダイレクトである。Rubyの通常出力(printやputsのデフォルトの出力先)は、 すべて、変数$defoutという変数に格納されたオブジェクトに対して行われる。 このオブジェクトは必ずしもIOである必要はなく、writeメソッドを備えていれば良い。 しかし、エラー出力に対しては問答無用でstderrに出力していた。 しかし、一貫性の観点や便利さから言えば、同様の仮想化が行われるべきだ。

ということで、新たに$deferrという変数を用意し、エラー出力はすべて、ここを通すようにした。 さらにwarnメソッドも新設した。その動作は

$deferr.write(s)
$deferr.write("\n")

と同じである。

もうひとつは標準入出力を切り替えることのできるsystem関数である。 すでにOpen3というライブラリはあるのだが、これのあり方も含めて再考したい。

最後に変数$stdinなどである。現在はこれらの変数に代入すると内部的にreopenを行っている。 しかし、この動作がSimonの誤解を招いたと言う事実は否定できない。 むしろ、これらの変数はread-onlyにして、警告などを使って、 最終的にはSTDINなどを使うように誘導した方が良いような気がしてきた。

間違いを招くような機能はむしろない方が良いし、それを放置しないことが良い言語であり続ける秘訣だと思うのだ。 これを「broken window theory」と呼ぶ。

_ [Ruby]メソッド結合

そういえば、Rubyを直したい点というのがもうひとつある。 正確にはひとつだけではないが、今日になって改めて思い出したものがひとつある。

それはメソッド結合である。モジュールをincludeしたとき、そのモジュール独自の初期化を行いたい場合がある。 そのような場合、現在のRubyならinitializeメソッドを呼ぶことになるだろう。 しかし、どのようなクラスにincludeされるか事前にはっきりとはわからないモジュールでは、 includeするクラスがinitializeでsuperを呼んでいないかもしれない。 そうするとそのモジュールのinitializeは呼ばれず、 オブジェクトは初期化されていない半端な状態で取り残されることになる。

superを呼ばなかったのがいけないんだ、と責任をユーザに押しつけるのは簡単だが、 多重継承に近いモジュールで呼び出し関係を維持するためsuperが必要か必要でないのか悩むのは、 プログラマの時間がもったいない。

他の言語ではこれらの問題をどう解決しているかというと、

  • C++ではコンストラクタが順に呼ばれる(superは無用)。
  • CLOSではメソッド結合があり、initializeの前に呼ばれる手続きなどを定義できる。

こっちの方がスマートだ。

Rubyでもなんらかの方法で解決策を提供したいが、どのような機能、記法が良いのかは、 まだだいぶん思案しないといけないだろう。メソッド結合が良さそうなんだけど。

_ [Ruby]メソッドフック

あるいは1.7から導入された define_method と同じような形で、 たとえば define_hook (あるいは define_advice) というメソッドを用意するのはどうだろう。

module Foo
  define_hook(:initialize, :before) { p 44}
end
class Bar
  include Foo
  def initialize()
    p 55
  end
end
bar = Bar.new
# => 44
#    55

が、before hookで44を出力し、initialize本体で55を出力するわけだ。もちろん、after hookも定義可能とする。 が、around hookはいらないだろう、きっと。

でも、define_method同様、いや、それ以上に醜いと言うのは欠点だ。

_ [Ruby]正規表現の$と\Z

先日、正規表現の「$」が「改行と文字列終端の間」にもマッチするように変更したのに合わせて、 「\Z」も同様に「改行と文字列終端の間」にもマッチするようにしました。

本日のツッコミ(全4件) [ツッコミを入れる]
_ akr (2003-05-13 23:46)

before と after 以外使わないんなら<br><br>define_before_hook(:initialize) { ... }<br>define_after_hook(:initialize) { ... }<br><br>にすればいいんじゃないですかね。<br>メソッドを増やしたくなければ<br><br>define_method(:before, :initialize) { ... }<br>define_method(:after, :initialize) { ... }<br><br>とか。

_ はら (2003-05-14 00:07)

プロレスって

_ なかだ (2003-05-14 10:39)

[ruby-talk:71333] "inherit rb_stdin/rb_stdout/rb_stderr from the parent to the child process"だと逆かと思ったんですが。<br>でもその後の疑似コードはわけ分からんし。rb_stdoutからdup()して子プロセスだけ元に戻す?

_ まつもと (2003-05-14 17:33)

Simonはstdioについて誤解していると思います。<br>正直なところ、これ以上はフォローできんです。

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

«前の日記(2003-05-12) 最新 次の日記(2003-05-14)» 編集

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