Hatena::ブログ(Diary)

星一の日記 このページをアンテナに追加 RSSフィード

2008-04-06

[] 第 3 回 RHG Strikes Back に参加して Ruby の不思議な実装を見た

第 3 回 RHG Strikes Back に参加しました。ちょっとだけ発表もさせていただきました。

今回は第 4 章のメタクラスあたりについて。複雑すぎです。

以下、得られた知見。

クラスの特異クラスの特異クラスが 1.8 と 1.9 で変わった

RHG には「特異クラスの特異クラスは自分自身である」なんて書いてありますが、そうではないようです。

詳しくは後述しますが、 Ruby レベルでオブジェクトの初期状態における特異クラスを取り出すことは不可能です。以下の実験にはまやかしがあります。

1.8
irb(main):001:0> A = Class.new
=> A
irb(main):002:0> class << A; self; end
=> #<Class:A>
irb(main):003:0> class << (class << A; self; end); self; end
=> #<Class:#<Class:A>>
irb(main):004:0> class << (class << (class << A; self; end); self; end); self; end
=> #<Class:#<Class:#<Class:A>>>

特異クラスが無限後退します。

1.9
irb(main):001:0> A = Class.new
=> A
irb(main):002:0> class << A; self; end
=> #<Class:A>
irb(main):003:0> class << (class << A; self; end); self; end
=> Class
irb(main):004:0> class << (class << (class << A; self; end); self; end); self; end
=> #<Class:Class>
irb(main):004:0> class << (class << (class << (class << A; self; end); self; end); self; end); self; end
=> Class

特異クラスの特異クラスは、なんと Class クラスになっています。謎です。

結局何が正しいか

「無限後退に戻すべき」「特異クラスの特異クラスの作成はエラーにすべき」などいろいろな意見がでましたが、まとまってはいません。

追記 (2008-04-13)

Yugui さんの記事にご指摘がありましたが、特異メソッド定義式 (class << obj; end) は、正確には、単純に特異クラスを取り出すという操作だけ行われるのではありません。「obj 特異クラスがなかった場合、特異クラスを作成する」という操作 (副作用?) も同時に行われます。そのため、本来の初期状態における obj の特異クラスは Ruby レベルで取り出すことはできません。実際に Ruby の実装をみると、特異クラスがもつ、自分の特異クラスへのポインタは、初期状態では自分自身をさしています。 特異メソッド定義式を評価した時点で、このポインタが変化してしまい、上記実験結果が得られた訳です。 RHG にある「特異クラスの特異クラスは自分自身である」というのは、初期状態ではそうなっているという意味で正しいです。

Class クラスの子クラスは本来作れないはずだが、実は作れる

Known Bug らしいです。

irb(main):001:0> class A < Class; end
TypeError: can't make subclass of Class
        from (irb):1
        from :0
irb(main):002:0> A = Class.new(Class)
=> A
irb(main):003:0> A.ancestors
=> [A, Class, Module, Object, Kernel]
irb(main):004:0> A.new
TypeError: wrong instance allocation
        from (irb):4:in `new'
        from (irb):4
        from :0

Module の子クラスは普通に作れる

Yugui さんは「無意味だから禁止すべき」的なことをおっしゃってましたが。

irb(main):001:0> class A < Module; end
=> nil
irb(main):002:0> class B; include A.new; end
=> B
irb(main):003:0> B.ancestors
=> [B, #<A:0x7fc24>, Object, Kernel]

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証