久しぶりにリファレンスマニュアル。
ああっ! そういえば動的なモジュールの include は考えたことなかった! どうしよう。
むー。真面目にやるなら、 include にもメタデータを付けて動的に追跡しないといけないよな。 検索は広めにひっかければいいとして、 検索後に情報を絞らないといけないな。
そうか。いまは追加元のライブラリ (origin-library) だけ表示してるけど、 この他にモジュールをインクルードしたライブラリ (include-origin-library) が必要だな。 ユーザからすれば、どのライブラリを require すると使えるかが問題なのだから、 include しているライブラリのほうが重要だ。 なぜなら include するということはそのライブラリは include すべきモジュールをすでに得ているはずである。 つまり origin-library を require しているはずである。 したがってユーザが include-origin-library を require したら 自動的に origin-library も使えるようになるはずだし、 またそのように期待してよいだろう。
真面目にやらない場合は、 (reopen/redefine) + include でドキュメントをごっそり複製してつっこめばいいわけだ。 このときにライブラリだけ書き換えれば include-origin も表示できるな。 うーんなんかこれでいいような気もしてきた。楽だし。
(17:15)
用語がいまいちなので再定義
ん? でもあれか、origin-library と includer-library が 違う場合ってどれくらいあるんだろ。ていうか、存在するか? 今回問題になった base64 ライブラリは origin = includer だしな。
なんか Rails 方面とかでバリバリ活用されてそうな嫌な予感がする。 出てきたときに悔しいから実装しとくかあ。
やっぱ、単純にコピーで実装したほうが楽かもなあ。 どのような手段によって (継承か、include か、直定義か) 提供されたかよりも、 どのライブラリを require すると使えるのか、がユーザにとっては知りたいわけだろうし。
このさい、 違うメソッド (のドキュメント) を参照できるようにしてしまおうかなあ。 メタデータとして「このメソッドはアレと同じ」という情報を付けてしまえばいい。 Signal.trap と Kernel.#trap とか、共有したい場合はけっこうあるし。
んで、include だけするライブラリについては、 そのドキュメント共有の仕組みを使ってメソッド単位でリンクしまくればいい。
こういうのを入れるとリンク切れが問題になったりするわけだが、 まあそのへんはあとで考えよう。 最悪、エントリを全部なめてチェックすりゃいいわけだし。
う、いや待てよ、やっぱまずいか。 include を単にメソッドのリストにコピーした場合、 オーバーライドが表現できなくなるな。 チェックしつつコピーすればいいか? いやでもその時点では元のクラスがロードされていない可能性があるな。 ということは検索時にオーバーライドチェックが必要? それもダルい。 パースを終えた時点でオーバーライドされてるかチェックして消せば多少はマシか? 検索側をこれいじょう複雑にするときついし、こっちのほうがまだいいな。
Ruby の実装と同じに、iclass を導入するのはどうだろう。 「Base64」モジュールに対して「@1@Base64」モジュールのように別名を付けて、 メソッドごっそりコピー。
あっ! いやいやいや待てそれはまずいな。っていうかコピー案はだめだ。 include したモジュールがまた別のモジュールを include してる可能性を考えてなかった。 やっぱり実体を分ける方針は筋が悪い。
うーん、いや、そうか。 そもそも includer-library をどこに記録したらいいんだろう。 include 先クラスに記録したとすると、 メソッド検索後に ancestors を全部見て、 定義元モジュールが include されてるところを探して、 なんかえらく面倒だな。速度の点は継承メソッドキャッシュと同じ方法で解決するとしても、 もうちょっとスマートな方法はないのかな。
この問題の厄介なところは、reopen/redefine 属性で調べられないとこか。 defined メソッドを別のライブラリが勝手に include しても reopen と同等になってしまう。 激しくウザい。
むー、やっぱ iclass のようなものが必要だな。 それも検索時のみ動的に作ったほうがよさげ。 元のモジュールの仮身となる iclass を作成し、 iclass は元のモジュールのラッパーにする。 IClassEntry#entries はエントリの kind を reopen 属性に変えて返すと。
……あれ、常に reopen とは限らないかな。 いやいやそんなことはない、いまは reopen な include の話だった。 パース時に reopen include であることは区別できるから、 別のライブラリが include しているという情報をクラスに記録しておけばいいんだな。 reopen include が普通の include よりも早くなることはありえないだろうから、 ancestors の順番が常に最後になることはさほど問題ない。
よし、なんとか解決できそうだ。
(20:29)
Copyright (c) 2002-2007 青木峰郎 / Minero Aoki. All rights reserved.