sumimさんの「 Ruby1.9のクラスのメタ階層を整理する 」という記事、Rubyの型階層は雑然としているというのは、なんかsumimさんの図が悪いような気もするなぁ。整理すればもうちょっと情報を引き出せるよ。あと、モジュールのせいもある。

前提知識

Rubyは基本的には単一継承のクラスベースオブジェクト指向言語なのだけれども、幾つか注意すべき処がある。

クラスはClassクラスのインスタンスである。

モジュール 制限付きの実装多重継承をもたらす仕組み。内部的には、モジュールの「化身」となるクラスを継承階層を挟み込むことで実装されている。 以下、モジュール M に対してその化身クラスを I(M) と表記する。 詳しくは 以前の記事 を参照。

特異クラス 特定のオブジェクトに専属するクラスのこと。特定のオブジェクトにだけ存在するメソッド「特異メソッド」を定義すると、内部的には特異クラスを生成してそれをクラス階層に挟み込んで表現する。 以下、オブジェクト obj に対してその特異クラスを (obj) と書く。



なお、sumimさんの記事では特異クラスは #<Class:<obj>> のように表記されている。これは特異クラスの inspect が返す文字列表現に由来する表記だが、長いので私はRHG式表記を使う。

クラスはモジュール クラスはモジュールの一種でもある。どうしてこういう設計になっているかは 別記事 参照。

隠蔽 化身クラスや特異クラスというのはRuby内部の実装の詳細の話だ。モジュールや特異メソッドを実現するためにそういうやり方をしているという話に過ぎない。だから、これらはRubyレベルには出てこないようになっている。 ただし、特異クラスは隠蔽漏れがあって、 (class << obj; self end) というイディオムで取得できる。



詳しいことは 以前の記事 を参照。

で、隠蔽された特異クラスや化身クラスを無理矢理取り出すために、前掲の記事のときに evil-ruby を拡張した。

拡張した分は本家にパッチ送ったんだけど反応がないし、そもそも開発されている気配がない。そこで、今回 githubにフォーク した。

対象ソースと解析

次のソースをRubyに読ませて、evil-rubyで解析してみた。

class Yapoo ; end class MenseMidget < Yapoo ; end rin = Yapoo .new kimiko = Yapoo .new anonymous_midget = MenseMidget .new class << anonymous_midget; end

sumimさんの図よりは、Rubyのクラス階層も幾分ましに見えるんでないか?

見れば分かるけれども、化身クラスは instance_of? Object ではない。それ以前に、Rubyレベルでこのクラスをオブジェクトとして処理する可能性をRubyは考慮していない。だから、化身クラス周りでメソッド呼び出しをするとすぐにRubyが落ちる。どうしようもない部分はgdbで内部を覗いて補いつつなんとかして作ったのが上の図だ。

図の整理

この図をsumimさんの記事のSmalltalkの場合の階層図に合わせて配列してみるとこうなる。

随分とすっきりした。そして、Smalltalkでは4階層になっているところ、Rubyではクラスのクラスのクラスが再び Class のクラスに戻ってくるので2階層になっていることが分かる。言い換えると、これはメタクラスはクラスの一種であるということだ。

モジュール

まあ、先ほどの図には抜けている部分があって、Kernelモジュールがない。Rubyの場合はモジュール機構があるから、この2階層の他にモジュールの階層ができるわけだ。

先ほどの図にモジュールを差し込んでみると次のようになる。化身クラスは書かずに include で表してある。

=begin

複雑になったのは、モジュールをクラス継承と一緒の面に書くから悪いんだと思う。モジュール階層はモジュール階層で別個のレイヤーをなしていて、だから3次元に書くのが正しい。間の2行を上にごそっと持ち上げれば、綺麗になるはずでしょ?

ちなみに、sumimさんのSmalltalk図風にもうちょっと整理したらこうなった。

いや、ModuleクラスやClassクラスを、この下のほうの行に置いてしまうのは妥当か分からないのだけれども。sumimさんの図はSmalltalkにクラスとメタクラスの区別があるのを踏まえて、それで振り分けているように見える。Rubyではメタクラスは特異クラスの一種なのでクラスなのだが、一応、次のように分類した。

インスタンス - クラスやモジュールではないもの

メタクラス - クラスやモジュールのクラス

狭義のクラス/モジュール - メタクラスではないクラス/モジュール

で、上から順にメタクラス、狭義のクラス、インスタンス、と並べてある。

思ったこと

(BasicObject) と (Object) のクラスが (Class) ではなくて Class だというのは謎だ。これが (Class) になっていれば、ちょうどSmalltalkの4階層が、メタクラスをクラスにしたことで2階層に潰れた形になって綺麗なのだが。実際、こうなる。

def Class . foo p :foo end Class .foo metaBasicObject = ( class << BasicObject ; self end ) metaBasicObject.ancestors metaBasicObject.foo

(BasicObject) は Class のサブクラスなのに、 Class のクラスメソッドを継承していない!

どうしてこうなっているかというと、たぶん、最初にクラス階層を初期化するときの順番の都合ではないかと予想される。直そうと思えば直せるけれども、普通メタクラスをそんなにいじくらないので誰も文句を言わなくて、なので実装の都合で放置されているんじゃないかと推測する。

モジュールの階層

ついでにモジュールの階層だけ別個に書いてみるとこうなる。次のソースを読み込んだと思いねぇ。

module Inteligence ; end module Prayable include Inteligence end class Cattle ; end class Pegasus include Inteligence end class Yapoo include Prayable end

Yapoo の定義が最初と違うのは趣味に走ったら例がかぶっただけなので、気にしない方向で。

ちなみに、モジュールが Module クラスの直接のインスタンスであるのは「初期状態」の話だ。

class << Inteligence ; end

と、メタクラスに触ったり、

module Inteligence def self . conscious? (subject = self ) end end

と、クラスメソッドを定義したりすると特異クラス (Inteligence) が湧いてきて、 Kernel , (Kernel) , Module の関係と同じ形になる。

結論

Rubyの階層はSmalltalkよりも雑然として見える。

宣伝

……というような話を、もうちょっと実用的な範囲に限定しつつもうちょっと丁寧に説明したのが『 初めてのRuby 』の8章です。よろしく。