世界線航跡蔵

Mad web programmerのYuguiが技術ネタや日々のあれこれをお送りします。

2008年09月13日

Ruby1.9 のクラスのメタ階層とかevil-rubyとか

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

で、隠蔽された特異クラスや化身クラスを無理矢理取り出すために、前掲の記事のときに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

20080914-metaclass-hierarchy0.png

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

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

図の整理

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

20080914-metaclass-hierarchy1.png

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

モジュール

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

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

20080914-metaclass-hierarchy2.png

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

20080914-metaclass-hierarchy3.png

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

  • インスタンス - クラスやモジュールではないもの
  • メタクラス - クラスやモジュールのクラス
  • 狭義のクラス/モジュール - メタクラスではないクラス/モジュール

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

思ったこと

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

def Class.foo
  p :foo
end
Class.foo                   #=> :foo
metaBasicObject = (class << BasicObject; self end)
metaBasicObject.ancestors   #=> [Class, Module, Object, Kernel, BasicObject]
metaBasicObject.foo         #=> NoMethodError

(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の定義が最初と違うのは趣味に走ったら例がかぶっただけなので、気にしない方向で。

20080914-metaclass-hierarchy4.png

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

class << Inteligence; end

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

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

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

結論

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

  • sumimさんの図が悪い。でも、それだけが原因じゃない。
  • メタクラスがクラスでもあるせいで、早めに再帰的な矢印が戻ってきてごちゃごちゃする。
  • モジュールという別のレイヤーのものを書き込まなければならないのでごちゃごちゃする。
  • 化身クラスまわりを真面目に書くと、化身クラスはあちこちに発生するのでごちゃごちゃする。
  • 三次元で、継承階層とmix-in階層を別の方向に分けて描くとたぶんすっきりする。
  • Ruby 1.9が、メタクラスの特異クラスをClassにしてしまったのは、クラスとメタクラスを区別すると言うことだ。クラスはオブジェクトとして振る舞って結構だがメタクラスをクラス扱いするな、ということだ。これはSmalltalkへの先祖返りか?

宣伝

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

トラックバック

http://yugui.jp/articles/798/ping
[OOPL] Ruby1.9 のクラスのメタ階層を整理する 2 (sumim’s smalltalking-tos)
sumimさんの「Ruby1.9のクラスのメタ階層を整理する」という記事、Rubyの型階層は雑然としているというのは、なんかsumimさんの図が悪いような気もするなぁ。整理すればもうちょっと情報を引き出せるよ。 Ruby1.9 のクラスのメタ階層とかevil-rubyとか @ 2008年09月 @ ratio

コメント

blog comments powered by Disqus

ご案内

前の記事
次の記事

タグ一覧

過去ログ

  1. 2016年07月
  2. 2016年01月
  3. 2015年09月
  4. 2015年08月
  5. 過去ログ一覧

フィード

フィードとは

その他

Powered by "rhianolethe" the blog system