クラス変数というのはやめにして、クラス共有変数とでも呼べばいいのか?


[ruby-talk:245207] class methods and instance variables in ActiveRecord::Base と一連のやりとりを見ていて思ったコト。クラスごとに管理される共有変数…といった意味合いで。


SmalltalkRuby で、「クラスメソッド」や「クラスインスタンス変数」といえば、それぞれ、オブジェクトとしてのとある“クラス”、つまり、何か別のクラス(メタクラスや特異クラス)のインスタンスとしてのその“クラス”が、コールできたり持つことができる、たんなる(self が“クラス”というだけの…)“インスタンスメソッド”や“インスタンス変数”を指します。あくまで便宜的な呼称です。


それに対して、「クラス変数」は、クラスやそのクラスに属するインスンタスたち(場合によってはサブクラスやそのインスタンスたち)によって共有可能な、ある種の大域変数のことで、上の「クラスなんちゃら」とは区別されるべき、まったくもって異質な存在であるわけです。そこで“共有”を入れたほうが、まだ分かりやすいのではないかと…。


まあ、名前の付け方やそれが喚起するイメージの一貫性(「クラスなんたら」ってったら必ず“こういうもの”であるべき論…みたいな)が保たれていないという問題もさることながら、冒頭のようなある種の“FAQ”が文字通り繰り返される背景には、やはり、まずは「クラスもオブジェクト」という状況に慣れないと…ということもあるのでしょうね。きっと。



ところで、Java では、“クラスメソッド”というと、SmalltalkRuby の「クラスメソッド」(変数の例に倣えば「クラスインスタンスメソッド」と呼ぶべきか?)ではなく、ここでいう「クラス共有メソッド」と呼ぶべきもの…を意味します。もっとも、Java の場合、“クラスメソッド”というメソッドが実際にあるわけではなく、たんに static なメソッドのことを便宜的にそう称しているだけなのですが…。「クラス変数」についても同様のことが言えるでしょう。


AppleObjective-C 関連の開発者向けドキュメントで「クラスインスタンス変数」を意味しているはずの機能のことを「クラス変数」と呼称してしまっていることともあわせて、「クラスもオブジェクト」であることを謳いながらも、実際には「クラスもオブジェクト」であるような感覚が希薄でいてもあまり不都合が生ずることはない、かような OOPL でのローカルルールにも注意したいところです。



あと、関係ありそでないような話ですが、この「クラスもオブジェクト」感覚というのは、ちょっと前に Python 界隈で話題になった クラスメソッドを列挙する にはどうしたらいいのか?という局面での(言語処理系別の)考え方にも反映されるんではないかな…と、上を書いていてふと思いました。


Python では、くだんの問題を解決するために、それなりの知識とテクニックを駆使する必要があるようですが、クラスにそれが属するクラス(メタクラス)が必ず存在する SmalltalkRuby の場合は、「通常のオブジェクトがコールできるメソッドを問い合わせる際に、そのオブジェクトが属するクラスに問い合わせるが、これと同じだ…」ということで済ませられそうです。たとえば、Smalltalk でなら、実際にその考え方でOK。

String class selectors

念のためこの式は、オブジェクトであるクラス「String」に「class」というメッセージを送って、それが属するクラス(String クラスのクラス。メタクラス)を取得し、あらためてそのクラス(String クラスのクラス)に定義されたメソッド名一覧の取得を要請する「selectors」というメッセージを送ることを表わしています。


Ruby も同様に…と書きたいところなのですが、残念(?)ながら Ruby の場合はちょっと事情が複雑…というか、Smalltalkメタクラスに相当する「クラスの特異クラス」が理解を超えた振る舞いをするので、同じ考え方ではダメなようです。

class Object; def uniclass; class << self; self end end end
class Foo; def self.my_class_method; end end

Foo.uniclass.instance_methods(false)
#=> ["new", "superclass", "my_class_method", "allocate"]

…と、このように、かろうじて "my_class_method" は含まれるものの、他にも定義した覚えのないメソッド名まで返してきてしまいます。


その代わりというわけではないでしょうが(モチのロンw。 あるいは、そもそも、特異クラスにアクセスする手段が標準で用意されていないことからも当然の帰結として…)、Ruby には、インスタンス自ら自身がコールできるメソッドを(それが属するクラスを介さずとも…)答えることが可能な #methods というメソッドが用意されていますから、すくなくともクラスメソッド名の列挙においては(クラスに問い合わせる #instance_methods ではなしに)こちらのほうを使うべき…ということになりそうです。

Foo.methods(false)   #=> ["my_class_method"]


このように、特異クラスというアクセスしにくい奇妙なモノを介在させてしまっているぶん、結果的に Ruby では、Smalltalk より「クラスもオブジェクト」感覚がちょっと薄らいだものになってしまってはいないかな…、ひいてはくだんの「クラスなんたら」の混同というような“FAQ”が生じてしまっているのではないか…という勝手な印象を持つことは、少々うがちすぎ(あるいは余計なお世話!)というものでしょうか?