hp12c このページをアンテナに追加 RSSフィード Twitter

2008-09-05

Rubyのオブジェクトは生物なんかじゃない、トップレベルこそが生物なんだ!

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を生成していたんだ!

*1Ruby1.9ではBasicObjectがある

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

Connection: close