id:sumim:20080913:p2 の続きで、最後に本題(?)の特異クラスの特異クラスが作られた場合について。
これまでの Ruby1.8 では、特異クラスに特異クラスが作られると、文字通り特異クラス特異的クラスが作られていました。こうして新しく設けられた特異クラスの特異クラスの真のクラスは特異クラスの特異クラス自身になります。
Ruby1.8 で #<Class:Foo> および #<Class:#<foo2>>、それぞれの特異クラスを作った場合
善し悪しはともかく、これは特異クラスに対する(少なくとも私個人的には)メンタルモデル通りの動きであるため、逆にそのまんま過ぎて「こんなんでいいの?(インスタンス特異クラスのときの苦心はなんだったのか?)」などとなんだか肩すかしをくったような感じがしますね。汗
他方で Ruby1.9 では、同じことをすると、もっとびっくりすることが起こります。
Ruby1.9 で #<Class:Foo> および #<Class:#<foo2>>、それぞれの特異クラスを作った場合
それまで #<Class:Class> のインスタンスだった #<Class:Foo>、その #<Class:Foo> のインスタンスだった #<Class:#<foo2>>、それぞれが、なんと同じ Class のインスタンスに鞍替えするのです。
ん?これって Ruby の特異クラスを Smalltalk のメタクラスに、Ruby の Class を Smalltalk の Metaclass に見立てたときの状況がほぼ再現されているのでは…と気づいたので、ここでちょっと悪のり。
Ruby1.9 で(Foo インスタンスの特異クラスの特異クラスは作らずにそのままで…)、Foo、Kernel、Class の特異クラスの特異クラスを作ります。
[Foo, Kernel, Class].each{ |m| class << m; class << self; end end }
すると、メタ階層は先のルール(つまり特異クラスが Class のインスタンスに鞍替え)に従って再編され、結果、目論見通り Smalltalk そっくりにメタ階層を整然とさせることができます(この図の範囲限定ですが)。
Ruby1.9 で Foo、Kernel、Class の特異クラスの特異クラスを作って Smalltalk のメタ階層を真似てみた結果
id:sumim:20080913:p1 で示した見た目のみのメタ構造の再編前とは違って、実際の振る舞いとの食い違いがないのも安心ですね。
Class と #<Class:Module> に同じ #m を定義する
class Class; def m; "Class#m" end end def Module.m; "Module.m" end
メタ構造の再編前は、見かけ上 Class#m がコールされるはずが Module.m がコールされてしまう
Kernel.actual_class.m #=> "Module.m"
メタ構造の再編後は、想定されるとおり Class#m がコールされる
Kernel.actual_class.m #=> "Class#m"