Rubyのメタクラス階層について再び

承前

3ヶ月ばかり時間が空いてしまったけれども、 sumimさんの記事 に答えたいと思います。

yugui さんの図は、たしかにクラスと特異クラス(メタクラス)が揃って並んでいて見た目にはきれいなのですが、これだとクラスが整然と並んでこそいるものの、肝心のメタ階層がどうなっているかという情報のほうは、正直なところ、いささか得にくいものになってしまっています。

いいえ、これで良いのです。なぜって?

これが私の図(下記再掲)で一番言いたかったことで、ただ、一般のメタクラスと#<Class:Class>を並べているのはいただけないかな。これはsumimさんのSmalltalk版の図を意識しすぎて、まずかったかなと思います。

図1:

20080914-metaclass-hierarchy1.png

うん、やっぱり

メタ階層がどうなっているかという情報のほうは、正直なところ、いささか得にくいものになってしまっています。

これは当たってるかもしれません。

図の修正

ただ、私としてはとにかくsumimさんの図でClassが#<Class:Class>よりも一般のメタクラスに近いのは納得がいかなくててですな。

反省を活かして一般のメタクラスから#<Class:Class>を浮かせるとこうなりました。

図2:

20081214-metahierarchy-pre01.png

さて、こうしてみると#<Class:BasicObject>と#<Class:Object>が不自然に頑張ってClassのインスタンスになっていることが分かります。#<Class:Yapoo>は#<Class:Object>のサブクラスなのに、どうして両者にこんなに大きな違いがあるんでしょう?

こうしてみたらどうでしょうか? (図3)

20081214-metahierarchy-pre02.png

たぶん、これがRubyにとっての正解です。図3のようになっていなかったのは何故かというと、単に実装の都合だったりします。図2ほうがクラス階層をブートする部分のコードが何行か短くて済むから。あとから図3のように直すには何行かコードを足さなければならなかったから。そして、誰も文句を言わなかったから。

と言うわけで、実はRuby 1.9メタクラス階層は私が図3のように 直してしまった のでした。

クラスはメタクラスインスタンスメタクラスは#<Class:Class>のインスタンスメタクラスと#<Class:Class>は等しくClassを通じてObjectを継承する。こうしてすべてがObjectになる。どうでしょうか?

そして、クラス、メタクラス、メタメタクラスと辿る上でSmalltalkのMetaclassに近いのは、たぶんだけれども、Classではなくて#<Class:Class>のほう *1 。 sumimさんが、SmalltalkRubyにおける対応関係を

  • Metaclass → Class
  • Metaclass class → #<Class:Class>

と仮定したのは、恐らくは私が直す以前の階層初期化のサボりに加えて、ClassはClassという名前で呼ばれるユーザーに身近なものであるのに対して#<Class:Class>はClassのクラスとしてのみ定義される名前のない何かであるからではないかと推察します。これを、Metaclass classがMetaclassのメタクラスであるがゆえにMetaclass classという名前なのと同じように見立てたのでは?

メタクラスはクラスか

オブジェクトがクラスに属するように、クラスが属する何か。それをメタクラスと呼ぶとすればメタクラスはクラスでしょうか?

Smalltalkではそうではないです。(GNU Smalltalk)

Object subclass: #Foo

Foo isKindOf: Class             "true"

Foo class isKindOf: Class       "false"

一方、メタクラスのクラスであるMetaclassはクラスです。

Foo class class                     "Metaclass"

Foo class class isKindOf: Class     "true"

MetaclassのメタクラスであるMetaclass classは再びクラスではありません。クラスではなくてメタクラスですから。

Foo class class class                 "Metaclass class"

Foo class class class isKindOf Class  "false"

すると、

  • Metaclass → Class
  • Metaclass class → #<Class:Class>

というSmalltalkRuby対応関係は自然に見えてきますね。Classは日の当たるところにいるクラスであるのに対して、#<Class:Class>は特異クラスという特殊な存在ですから。

でも、これはRuby流のメタクラス狂の感覚からすると転倒している、というのが私の言いたかったことなのでした。にもかかわらず、sumimさんの図に合わせることにこだわって本題からずれてしまったのは、私こそ「図が悪い」なのだったのだと思いますです。

何故Rubyは転倒しているか

逆に見ればRubyが転倒しているのです。クラスのクラスのクラスであってクラスである#<Class:Class>をClassと呼ぶのではなく、なぜ#<Class:Class>の祖先であるというに過ぎないClassが表舞台にいるのでしょうか。そして、#<Class:Class>が、マニア以外は気づかないような場所に追いやられているでしょうか。

これはRubyの無限退行モデルと呼ばれるメタクラス階層編成によるものです。無限退行モデルのためにすべてのクラスのクラスが#<Class:Class>であるかというとそうではなくて、クラスや、メタクラスや、それらを束ねる究極の何かはClassになります。

図4

20081214-metahierarchy-ideal.png

このように、Rubyでは、クラスがあって、クラスのクラス(メタクラス)があって、メタクラスのクラス(メタメタクラス)があって、と果てしなく続いていきます。

次の記事で、無限退行モデルについてもう少し詳しく見てみたいと思います。

概要

簡単に概要だけ先に述べておくと、無限退行モデルを取っている理由はクラスを普通のオブジェクトであるようにするためです。

Ruby式にクラスに属する何かをオブジェクトと呼びましょう。そうすると、Smalltalkにおけるクラスはオブジェクトではありません。なぜならば、クラスはメタクラスに属するのであって、メタクラスはクラス(isKindOf: Class)ではないからです。クラスはクラスには属しません。

メタクラスもまたクラスに属するようにして、かつ整合性を取るためためにRubyは無限退行モデルをとっています。

ちなみに、この言い方には不公正があります。むしろ源流であるSmalltalkからいえば、オブジェクトよりも先にクラスが立つRubyが邪道なのです。isKindOf: Objectであるという意味で言えば一般のオブジェクトもクラスもメタクラスも等しくオブジェクトです。

*1:

初めは「SmalltalkのMetaclass classに近いのは、たぶんだけれども、#<Class:Class>ではなくてClassのほう」と書いていた。指摘により修正。