1から始めるRuby(その2)

1から始めるRubyの続きです

クラス再び

Rubyの1はFixnumというクラスに属している」
前回私はそう言った
疑い深いあなたは
うさぎ本*1やキリン本*2を紐解いて
その事実を確かめたかもしれない


でもそれは回り道だ
それも1に聞くのが確実で早い

>> 1.class
=> Fixnum


しかもこの方法は確実で早いだけではない
実は1の返答には
Rubyにおける別の重大な真実が隠されているのだ


あなたが1にclassと送ったら
彼はFixnumと返してきた
Rubyでは入口も出口もオブジェクトであった
賢明なあなたはもう気が付いたかもしれない


そう
RubyではFixnumというクラスもまた
オブジェクトなのである!


あなたは誰かに
クラスというのはオブジェクトのひな形
つまり設計図であると教えられただろう
Rubyにおいてもそれは真実だ
Fixnumクラスは1オブジェクトのひな形になっている
でもFixnumは
それと同時にRubyの世界に実体として存在し
1と同じようにオブジェクトとして振る舞うのだ


試しに前回1に覚えさせた挨拶に
Fixnumを投げてみよう

>> class Fixnum
>>   def hello(name)
>>    "Yo! #{name}"
>>   end
>> end
=> nil
>> 1.hello(Fixnum)
=> "Yo! Fixnum"


helloメソッドは
Fixnumをオブジェクトとして受け入れた


では
1のときと同様に
Fixnumがどんなメッセージに反応するか見てみよう

>> Fixnum.methods
=> [:allocate, :superclass, :freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s, :included_modules, :include?, :name, :ancestors, :instance_methods, :public_instance_methods, :protected_instance_methods, :private_instance_methods, :constants, :const_get, :const_set, :const_defined?, :const_missing, :class_variables, :remove_class_variable, :class_variable_get, :class_variable_set, :class_variable_defined?, :module_exec, :class_exec, :module_eval, :class_eval, :method_defined?, :public_method_defined?, :private_method_defined?, :protected_method_defined?, :public_class_method, :private_class_method, :autoload, :autoload?, :instance_method, :public_instance_method, :nil?, :=~, :!~, :eql?, :hash, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]


Fixnumはこれら95個のメッセージに反応する


ちなみに
1のためにFixnumが持っているメソッドを見るには
次のようにすればいい

>> Fixnum.instance_methods
=> [:to_s, :-@, :+, :-, :*, :/, :div, :%, :modulo, :divmod, :fdiv, :**, :abs, :magnitude, :==, :===, :<=>, :>, :>=, :<, :<=, :~, :&, :|, :^, :[], :<<, :>>, :to_f, :size, :zero?, :odd?, :even?, :succ, :integer?, :upto, :downto, :times, :next, :pred, :chr, :ord, :to_i, :to_int, :floor, :ceil, :truncate, :round, :gcd, :lcm, :gcdlcm, :numerator, :denominator, :to_r, :rationalize, :singleton_method_added, :coerce, :i, :+@, :eql?, :quo, :remainder, :real?, :nonzero?, :step, :to_c, :real, :imaginary, :imag, :abs2, :arg, :angle, :phase, :rectangular, :rect, :polar, :conjugate, :conj, :between?, :nil?, :=~, :!~, :hash, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

>> Fixnum.instance_methods == 1.methods
=> true

メタクラス(クラスのクラス)

さてFixnumがオブジェクトだとすると
また一つの疑問が湧いてくる
Fixnumにメッセージを送ったとき
その答えを用意しているのは一体誰なのかと


もうあなたも答えの探し方が分かっただろう
irbを開いて..

>> Fixnum.class
=> Class


FixnumはClassという名のクラスを返してきた
つまりFixnumにメッセージを送ったとき
それに答える実体は
Classクラスであったのだ


そしてあなたの疑問は尽きることはない
先の答えからClassもまたオブジェクトであり
したがってClassもメッセージに反応する
だとしたらその答えを用意しているのは一体誰なのかと


さあもう一度やってみて!

>> Class.class
=> Class


自分の目を疑っているなら
これで目が覚めるだろう

>> Class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class.class
=> Class


Rubyの世界の家族構成が見えてきた
1オブジェクトにはFixnumクラスという母がいて
1に代わって答えを用意し
Fixnumクラス(オブジェクト)にはClassクラスという母がいて
Fixnumに代わって答えを用意する
そして
Classクラス(オブジェクト)は自分自身が母でもあって
自分で答えを用意する



そうつまりClassクラスは
Rubyの世界におけるアダムとイヴだったのだ!
世界(オブジェクト)はここからはじまった


前回私は
「FixnumこそがGoogle同様のスゲー存在なのである」
と言ったが今ここでそれを撤回する
そして言う
「ClassクラスこそがGoogle同様のスンゲー存在なのである」

クラスメソッド定義

さて
ここまでの仕組みが理解できたなら
Fixnumにも挨拶をさせてみよう
やり方はもうわかるよね?

>> class Class
>>   def bonjour
>>     "Bonjour from Fixnum with love!"
>>   end
>> end
=> nil
>> Fixnum.bonjour
=> "Bonjour from Fixnum with love!"


一見うまくいっているこのコードを見て
何も感じないのであれば
あなたはRubyのことを
まだよく知らないのかもしれない
このまま先を読み進めてほしい
違和感を感じたのなら
1から始めるRuby(その326)」に飛んで!
ここはあなたのためのページじゃない


問題を明らかにしよう
下のコードを見てほしい

>> String.bonjour
=> "Bonjour from Fixnum with love!"
>> Array.bonjour
=> "Bonjour from Fixnum with love!"
>> Hash.bonjour
=> "Bonjour from Fixnum with love!"
>> Symbol.bonjour
=> "Bonjour from Fixnum with love!"
>> Regexp.bonjour
=> "Bonjour from Fixnum with love!"
>> Range.bonjour
=> "Bonjour from Fixnum with love!"


RubyにはFixnum以外にも多数のクラスが用意されている
上に並べたものはそれらの代表選手だけれども
これらのクラスもbonjourメソッドに反応してしまった
しかもfrom Fixnumなんて!


実は
ClassクラスはFixnumの母というだけではなく
すべてのクラスの母なのだ

>> String.class
=> Class
>> Array.class
=> Class
>> Hash.class
=> Class
>> Symbol.class
=> Class
>> Regexp.class
=> Class
>> Range.class
=> Class


仮にあなたが独自のメソッドに飽きたらなくなって
後から独自のクラスを作ったときにも
この問題のあるbonjourメソッドに反応してしまう

>> class Loner
>>
>> end
=> nil
>> Loner.bonjour
=> "Bonjour from Fixnum with love!"



解決策は2つある
1つ目はFixnumという文字列の代わりに
selfを渡すのだ

>> class Class
>>   def bonjour
>>     "Bonjour from #{self} with love!"
>>   end
>> end


selfというのはそれを包むオブジェクトを返す
Rubyのキーワードだ
bonjourメソッドが
Fixnumクラスオブジェクトに送られたなら
Fixnumがselfになる
Arrayクラスオブジェクトに送られたなら
Arrayがselfになる
あなたのLonerクラスオブジェクトに送られたなら
Lonerがselfになる

>> Fixnum.bonjour
=> "Bonjour from Fixnum with love!"
>> String.bonjour
=> "Bonjour from String with love!"
>> Array.bonjour
=> "Bonjour from Array with love!"
>> Hash.bonjour
=> "Bonjour from Hash with love!"
>> Symbol.bonjour
=> "Bonjour from Symbol with love!"
>> Regexp.bonjour
=> "Bonjour from Regexp with love!"
>> Range.bonjour
=> "Bonjour from Range with love!"
>> Class.bonjour
=> "Bonjour from Class with love!"
>> Loner.bonjour
=> "Bonjour from Loner with love!"


続き