世界線航跡蔵

Mad web programmerのYuguiが技術ネタや日々のあれこれをお送りします。

2009年11月16日

Three implicit contexts in Ruby

Yehuda Katz wrote an article about self and metaclass. In the article he said that Person.instance_eval assigns Person's metaclass to self for a class Person. But this is obviously wrong.

class Person; end
Person.instance_eval{ p self }  #=> Person

As I mentioned in an old article, though I'm sorry about it is written in Japanese, Ruby always has three implicit contexts: self), so called `klass' and the constant definition point. Yehuda is confusing self with `klass'.

self

self is the self which you know. It is the default receiver of method invocation. There is always a self.

p self                         # displays "main"

class Foo
  def bar(a = (p self)) end
end
foo = Foo.new
foo.bar                        #  displays "#<Foo:0x471004>"

class Foo
  class Baz < (p self; self)   # displays "Foo"
  end
end

On the top level, a special instance of Object named "main" is the self. Whenever you are, you can retrieve the self from the pseudovariable self.

If you invoke a method without giving an explicit receiver, self will receive the invocation.

so called `klass'

I called the conception `klass' in the old article but it might not be a good name. It is the default class onto which method is defined. Now I'd like to call it "default definee".

Ruby always holds the reference to a class as well as self. But there is no way to retrieve it directly. It is more implicit than self. If you define a method without giving an explicit receiver, in other words, if you define a method with the normal method definition syntax, the default definee will have the method as an instance method.

Examples

On the top level, Object is the class. So global functions are actually instance methods in Object class as you know.

def hoge; end
Kernel.instance_method(:hoge)  #=> #<UnboundMethod: Object#hoge>

BTW, "hoge", "fuga", "piyo" is Japanese "foo", "bar", "baz".

class syntax switches both self and the default definee to the class which is now being defined.

class T
  def hoge; end
end
T.instance_method(:hoge)      #=> #<UnboundMethod: T#hoge>

In a normal method body, self is the receiver of the method invocation and the default definee is the syntactically outer class, now it is T.

class T
  def hoge
    def fuga; end
  end
end
t = T.new
t.hoge
t.method(:fuga)               #=> #<Method: T#fuga>
T.instance_method(:fuga)      #=> #<UnboundMethod: T#fuga>

Don't confuse it with def self.fuga, an singleton method definition. When you give a receiver to a method definition, the method will be adde into the eigenclass of the receiver.

class U
  def hoge
    def self.fuga; end
  end
end
u = U.new
u.hoge
u.method(:fuga)               #=> #<Method: #<U:0x46dbf4>.fuga>
U.instance_method(:fuga)      # raises a NameError "undefined method `fuga' for class `U'"

U does not have an instance method named fuga because fuga is a singleton method of u.

Wherever you are, there is a default definee. On evaluating a default value, the default definee is the outer class as well as in a method body.

class Bar; end
$bar = Bar.new
class Baz
  def $bar.hoge(a = (def fuga; end))
    def piyo; end
  end
end
$bar.hoge

Baz.instance_method(:fuga)      #=> #<UnboundMethod: Baz#fuga>
Baz.instance_method(:piyo)      #=> #<UnboundMethod: Baz#piyo>

$bar.method(:fuga)              # raises a NameError "undefined method `fuga' for class `Bar'"
$bar.method(:piyo)              # raises a NameError "undefined method `piyo' for class `Bar'"

In other words, class definition changes the default definee but method definition does not.

eval family

What instance_eval does is

  • changing self to instance_eval's receiver
  • changing the default definee to the receiver's eigenclass.
    • If the receiver does not have eigenclass yet, creating it.
  • evaluates the given block.
o = Object.new
o.instance_eval do
  p self                       #=> #<Object:0x454f24>
  def hoge; end
end
o.method(:hoge)                #=> #<Method: #<Object:0x454f24>.hoge>
Object.instance_method(:hoge)  # raises a NameError "undefined method `hoge' for class `Object'"

Let's go.

class T
  $o = Object.new
  $o.instance_eval do
    def hoge(a = (def fuga; end))
      def piyo; end
    end
  end
end
$o.hoge

$o.method(:fuga)           #=> #<Method: #<Object:0x42d144>.fuga>
$o.method(:piyo)           #=> #<Method: #<Object:0x42d144>.piyo>
T.instance_method(:fuga)   # raises a NameError
T.instance_method(:piyo)   # raises a NameError

Because instance_eval changes the default definee to the eigenclass of $o, fuga and piyo will be singleton methods of $o.

Oops, I should mention that

RUBY_VERSION               #=> "1.9.1".

Ruby 1.8 acts more lexically, so you will contrarily get

$o.method(:fuga)           # raises a NameError
$o.method(:piyo)           # raises a NameError
T.instance_method(:fuga)   #=> #<UnboundMethod: T#fuga>
T.instance_method(:piyo)   #=> #<UnboundMethod: T#piyo>

In Ruby 1.8, the default definee in the method body is based on the lexically outer class definition. Anyway, in both Ruby 1.8 and 1.9, instance_eval changes self to the receiver, the default definee to its eigenclass.

Finally, class_eval changes both self and the default definee to the receiver.

class_eval and instance_eval
selfdefault definee
class_evalthe receiverthe receiver
instance_evalthe receivereigenclass of the receiver

In the my old article, I discussed about Kernel#eval and instance_eval/class_eval with String evaluation.

constant definition

When you use an instance variable, it is a instance variable of self. When you use an class variable, it is a class variable of self's class; Or the one of self itself when self is a class.

But constants behave differently. It is another implicit context in Ruby. The ruby core team calls the conception "cref".

I must write a presentation for the comming RubyConf. So I'll discuss about cref later.

トラックバック

http://yugui.jp/articles/846/ping
Meta programming (Pearltrees)
Discover a selection of related articles on Pearltrees

コメント

blog comments powered by Disqus

ご案内

前の記事
次の記事

タグ一覧

過去ログ

  1. 2014年02月
  2. 2013年11月
  3. 2013年08月
  4. 2013年04月
  5. 過去ログ一覧

フィード

フィードとは

その他

Powered by "rhianolethe" the blog system