RubyのTopLevelは不思議だ。Rubyはオブジェクト指向言語だから普通まずクラスでオブジェクトを定義し、これをインスタンス化し、この生まれたオブジェクトにメッセージを送る、という手続きを経てプログラムが組成される。

class Person
    def initialize(name)
      @name = name
    end
    def name
      @name
    end
  end
  me = Person.new("Charlie")
  me.name # => "Charlie"

でもTopLevelではそんな手続きを吹っ飛ばして、いきなりメソッドが実行できたり書けたりする。

rand(10) # => 4
  def hello(name)
    puts "hello, #{name}"
  end
  hello("Charlie")
        # >> hello, Charlie

なぜ?メソッドのレシーバは誰?此処は一体どこ?

それを知るにはselfが使える。

self  #  => main

ここはどうやらmainらしい。Rubyの操作対象はすべてオブジェクトだから、mainもきっとオブジェクトに違いない。とすればIDを持っているはずだ。

self.object_id # => 107690

やはりオブジェクトだった。そうなると当然にその基となるクラスが存在するはずだ。

self.class  # => Object

クラスはObjectクラスだった。mainはObjectクラスのインスタンスなんだ。だからObjectクラスに定義されたinstanceメソッドが使えるんだな。他にも使えるメソッドを調べてみよう。

self.methods.sort # => ["==", "===", "=~", "__id__", "__send__", "class", "clone", "display", "dup", "enum_for", "eql?", "equal?", "extend", "freeze", "frozen?", "hash", "id", "include", "inspect", "instance_eval", "instance_exec", "instance_of?", "instance_variable_defined?", "instance_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_of?", "method", "methods", "nil?", "object_id", "private", "private_methods", "protected_methods", "public", "public_methods", "respond_to?", "send", "singleton_methods", "taint", "tainted?", "tap", "to_a", "to_enum", "to_s", "type", "untaint"]

ずいぶんあるけどちょっと変だな。さっき使ったrandも定義したhelloもこのリストにはないぞ。レシーバがはっきりしたんだから、もう一度レシーバを明示してメソッドを呼んでみよう。

main.rand(10) # => 
      # ~> -:10: undefined local variable or method `main' for main:Object (NameError)
  main.hello("Charlie") # => 
      # ~> -:10: undefined local variable or method `main' for main:Object (NameError)

だめだ。selfでどうかな。

self.hello("Charlie") # => 
      # ~> -:10: private method `hello' called for main:Object (NoMethodError)
  self.rand(10) # => 
      # ~> -:10: private method `rand' called for main:Object (NoMethodError)

あれ?randもhelloもprivateメソッドて書いてあるぞ!

privateメソッドっていうのは確か、レシーバを明示しては呼び出せないメソッドだったよね。じゃあmainのprivateメソッドのリストを見てみようか。

self.private_methods.sort # => ["Array", "Float", "Integer", "String", "__method__", "`", "abort", "at_exit", "autoload", "autoload?", "binding", "block_given?", "callcc", "caller", "catch", "chomp", "chomp!", "chop", "chop!", "eval", "exec", "exit", "exit!", "fail", "fork", "format", "getc", "gets", "global_variables", "gsub", "gsub!", "hello", "initialize", "initialize_copy", "iterator?", "lambda", "load", "local_variables", "loop", "method_missing", "open", "p", "print", "printf", "proc", "putc", "puts", "raise", "rand", "readline", "readlines", "remove_instance_variable", "require", "scan", "select", "set_trace_func", "singleton_method_added", "singleton_method_removed", "singleton_method_undefined", "sleep", "split", "sprintf", "srand", "sub", "sub!", "syscall", "system", "test", "throw", "trace_var", "trap", "untrace_var", "warn"]

リストが長くて見つけられないなあ。じゃあスーパークラスのものを除外して表示してみよう。

self.private_methods(false) # => ["initialize", "hello"]

helloがあったぞ!でも、ずいぶんときれいさっぱり他のメソッドが無くなっちゃったね。randも見つからないし。スーパークラスにあるのかな?

あれ?ちょっと待った。mainのクラスはObjectだったよね。それにスーパークラスなんてあるの?

Object.superclass # => nil

やっぱり無い1。じゃあさっきの長いメソッドリストはどこから来たの?

そうか!きっとモジュールだよ。Objectクラスには他のモジュールがMix-inされていて、そのモジュールにさっきのメソッドたちが定義されてるんだ。調べてみよう。

Object.included_modules # => [Kernel]

KernelというモジュールがMix-inされている。じゃあKernelに定義されているprivateなInstanceメソッドをリストしてみよう。

Kernel.private_instance_methods.sort # => ["Array", "Float", "Integer", "String", "__method__", "`", "abort", "at_exit", "autoload", "autoload?", "binding", "block_given?", "callcc", "caller", "catch", "chomp", "chomp!", "chop", "chop!", "eval", "exec", "exit", "exit!", "fail", "fork", "format", "getc", "gets", "global_variables", "gsub", "gsub!", "initialize_copy", "iterator?", "lambda", "load", "local_variables", "loop", "method_missing", "open", "p", "print", "printf", "proc", "putc", "puts", "raise", "rand", "readline", "readlines", "remove_instance_variable", "require", "scan", "select", "set_trace_func", "singleton_method_added", "singleton_method_removed", "singleton_method_undefined", "sleep", "split", "sprintf", "srand", "sub", "sub!", "syscall", "system", "test", "throw", "trace_var", "trap", "untrace_var", "warn"]

今度は目を凝らしてみつけるぞ。randがあったぞ!randはKernelモジュールに定義されたprivateメソッドだったんだ。するとここに並んでいるメソッドはTopLevelで使えるんだね。じゃあKernelにメソッドを定義して、TopLevelで呼べるか試してみよう。

module Kernel
    private
    def kernel_private_hello
      "hello of Kernel Private called from #{self}"
    end
  end
  kernel_private_hello # => "hello of Kernel Private called from main"

うまくいった。

でも不思議だなあ。mainはオブジェクトなのにKernelのprivateメソッドがなぜ使えるんだろう。ここがObjectクラス内だったら分かるんだけど…

それからなぜオブジェクトにhelloメソッドが定義できたんだろう。メソッド定義はクラスにしかできないと思ってたのに…

もしかして…

そうかSingletonメソッドがあるじゃないか。helloはきっとmainのSingletonメソッドになってるんだよ。

self.singleton_methods # => ["public", "to_s", "include", "private"]

違った…

なんか違うの出てきちゃったな。

試しにSingletonメソッドをTopLevelで定義して、ここにリストアップされるか見てみよう。

def self.toplevel_singleton_hello
    "hello of TopLevl Singleton from #{self}}"
  end
  class << self
    def toplevel_singleton_class_hello
      "hello of TopLevel Singleton Class from #{self}"
    end
  end
  toplevel_singleton_hello # => "hello of TopLevl Singleton from main}"
  toplevel_singleton_class_hello # => "hello of TopLevel Singleton Class from main"
  self.singleton_methods # => ["public", "toplevel_singleton_class_hello", "to_s", "include", "toplevel_singleton_hello", "private"]

ちゃんとリストアップされるなあ。やっぱりここはmainオブジェクトだよ。

まさか…

ここはObjectクラス?試しにObjectクラスが持っているprivateなinstanceメソッドを調べてみよう。

Object.private_instance_methods(false) # => ["initialize", "hello"]

あっ!TopLevelで定義したhelloがある!

つまりここは…

Objectクラスの中なんだ!これでprivateメソッドが呼べるのにも筋が通る。

よしもう一つ確かめてみよう。ここがObjectクラスの中なら、そこに定義されるクラスはネストされたクラスになるはずだ。

class Nested
  
  end
  Object::Nested # => Nested
  ::Nested # => Nested
  Object::Nested.new # => #<Nested:0x1bf58>
  Object.constants.detect{ |c| c =~ /^Ne/ } # => "Nested"

確かにObjectにネストされているぞ。

これで答えが出た。

RubyのTopLevelはObjectクラスであり、かつ、そのインスタンスであるmainオブジェクトだったんだ!だからそこでクラスとしてinstanceメソッドを定義でき、しかもそのメソッドをそのインスタンスとして呼び出せる。

つまり自身で自身を作るという自己増殖の機能を備えている!

そうRubyのTopLevelは…

生物だったんだ!

不思議の国Rubyでは、ClassクラスがClassクラスを生成するように、TopLevelがTopLevelを生成していたんだ!

  1. Ruby1.9ではBasicObjectがある


blog comments powered by Disqus
ruby_pack8

100円〜で好評発売中!
M'ELBORNE BOOKS