Is 'Yield Self' the Same as Instance_Eval

Is 'yield self' the same as instance_eval?

Your two pieces of code do very different things. By using instance_eval you're evaluating the block in the context of your object. This means that using def will define methods on that object. It also means that calling a method without a receiver inside the block will call it on your object.

When yielding self you're passing self as an argument to the block, but since your block doesn't take any arguments, it is simply ignored. So in this case yielding self does the same thing as yielding nothing. The def here behaves exactly like a def outside the block would, yielding self does not actually change what you define the method on. What you could do is:

class Foo
def initialize
yield self if block_given?
end
end
x = Foo.new {|obj| def obj.foo() 'foo' end}
x.foo

The difference to instance_eval being that you have to specify the receiver explicitly.

Edit to clarify:

In the version with yield, obj in the block will be the object that is yielded, which in this case is is the newly created Foo instance. While self will have the same value it had outside the block. With the instance_eval version self inside the block will be the newly created Foo instance.

Difference between instance_eval and class self?

class << obj sets self to whatever obj is in the given block, which in my case is the instance of Class that is Example (this is where I'm probably wrong);

No, class << obj opens up the singleton class of obj. As you correctly pointed out, inside of a class declaration, self refers to the class itself, so, in this case, the "inner" self (i.e. the one being passed to puts) refers to the singleton class of Example.

`instance_eval` and scopes

Your scope method is basically a no-op. When you pass a block to a method that yields, the block is evaluated in the current scope. Observe:

class A
def self.scope
yield
end
end

A.scope { p self }
# main

Since nothing is yielded to the block, and nothing is done with the return value of yield, any code run in the block will have the same effect run outside the scope block.

This isn't the case with instance_eval, however. When instance_eval runs a block, self in the block is set to the receiver (rather than whatever self is in the block's scope). Like this:

class A
end

A.instance_eval { p self }
# A

But note that this means that self.instance_eval { ... } is also a fancy no-op, because you're changing the block's self to the same self outside the block.

So your code is equivalent to this:

class A
def self.method_added method
# do something involving the added method
end
end

class B < A
def foo
end
end

class_eval vs instance_eval

Long story short:

  • Object.instance_eval &block sets:

    • self to Object
    • The "current class" to Object.singleton_class
  • Object.class_eval &block sets:

    • self to Object
    • The "current class" to Object

The "current class" is used for def, undef and alias, as well as constant and class variable lookups.


Now, let's have a look at the implementation details.

Here's how module_eval and instance_eval are implemented in C:

VALUE rb_mod_module_eval(int argc, VALUE *argv, VALUE mod) {
return specific_eval(argc, argv, mod, mod);
}

VALUE rb_obj_instance_eval(int argc, VALUE *argv, VALUE self) {
VALUE klass;
if (SPECIAL_CONST_P(self)) { klass = Qnil; }
else { klass = rb_singleton_class(self); }
return specific_eval(argc, argv, klass, self);
}

Both call specific_eval, which takes the following arguments: int argc, VALUE *argv, VALUE klass and VALUE self.

Note that:

  • module_eval passes the Module or Class instance as both klass and self
  • instance_eval passes the object's singleton class as klass

If given a block, specific_eval will call yield_under, which takes the following arguments: VALUE under, VALUE self and VALUE values.

if (rb_block_given_p()) {
rb_check_arity(argc, 0, 0);
return yield_under(klass, self, Qundef);
}

There are two important lines in yield_under:

  1. block.self = self;

    This sets the self of the block to the receiver.

  2. cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);

    The cref is a linked list
    which specifies the "current class", which is used for def, undef and alias, as well
    as constant and class variable lookups.

    That line basically sets the cref to under.

    Finally:

    • When called from module_eval, under will be the Class or Module
      instance.

    • When called from instance_eval, under will be the singleton class of
      self.

Instance Eval Within Block

class Builder
def initialize
@lines = []
end

def lines(&block)
block_given? ? instance_eval(&block) : @lines
end

def add_line( text )
@lines << text
end
end

my_builder = Builder.new
my_builder.lines {
add_line "foo"
add_line "bar"
}
p my_builder.lines # => ["foo", "bar"]

Ruby Block Scope with instance_eval

When you define @value in the lexical scope (your main source file), you're defining an instance variable in the global interpreter. For example:

self #=> main
# "self" here refers to the main interpreter, which is of class Object
self.instance_variable_get(:@value) #=> 1
# "example" is your instance above
example.instance_variable_get(:@value) #=> "a"
# "self" has been changed to refer to "example" using instance_eval
example.instance_eval { self.instance_variable_get(:@value) } #=> "a"
# this syntax is just a shortcut for the line above
example.instance_eval { @value } #=> "a"

With instance_eval, all you're doing is replacing the main interpreter self with the object you've called instance_eval on.

How to change self in a block like instance_eval method do?

You can write a method that accepts a proc argument, and then pass that as a proc argument to instance_eval.

class Foo
def bar(&b)
# Do something here first.
instance_eval &b
# Do something else here afterward, call it again, etc.
end
end

Foo.new.bar { puts self }

Yields

#<Foo:0x100329f00>

Why might you call instance_eval (as opposed to class_eval) inside 'initialize'?

First of all, you can't do something like this:

class Observer
def initialize(&block)
class_eval(&block) if block_given?
end
end

Because class_eval isn't defined for an instance of Observer. It is defined in Module (which Class descends from). We'll come back to class_eval later.

The reason to use the above idiom is often to allow block initialization:

x = Observer.new do
add_event(foo)
some_other_instance_method_on_observer
self.some_attribute = something
end

Plus, you can add methods to a given instance of the class:

foo = Observer.new do
def foo
'foo'
end
end

foo.foo # => "foo"

You can accomplish roughly the same thing without instance_eval:

class Foo
def initialize
yield self if block_given?
end
end

foo = Foo.new do |x|
x.add_event(foo)
x.some_other_instance_method_on_observer
x.self.some_attribute = something
end

But that doesn't give you the ability to add methods. If you were to do this:

foo = Foo.new do
def foo
'foo'
end
end

foo.foo # => "foo"

It seems to work, right? But what you've actually done is to add the foo method to everything, because self is set to the "main" object. It's equivalent to simply defining the method outside of the block. They get added as instance methods to Object, so they work on everything.

Now, as promised, a brief return to class_eval. You could do something like this:

class Observer
def initialize(&block)
class.class_eval(&block) if block_given?
end
end

But then you open up the entire class:

x = Observer.new { def foo; 'foo'; end }
x.foo # => "foo"
y = Observer.new
y.foo # => "foo"

This isn't typically what we want to do. Plus, self will be the class, not the instance. This makes it useless for the block initialization as demonstrated above.

How to understand the difference between class_eval() and instance_eval()?

As the documentation says, class_eval evaluates the string or block in the context of the Module or Class. So the following pieces of code are equivalent:

class String
def lowercase
self.downcase
end
end

String.class_eval do
def lowercase
self.downcase
end
end

In each case, the String class has been reopened and a new method defined. That method is available across all instances of the class, so:

"This Is Confusing".lowercase 
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
=> "the smiths on charlie's bus"

class_eval has a number of advantages over simply reopening the class. Firstly, you can easily call it on a variable, and it's clear what your intent is. Another advantage is that it will fail if the class doesn't exist. So the example below will fail as Array is spelt incorrectly. If the class was simply reopened, it would succeed (and a new incorrect Aray class would be defined):

Aray.class_eval do
include MyAmazingArrayExtensions
end

Finally class_eval can take a string, which can be useful if you're doing something a little more nefarious...

instance_eval on the other hand evaluates code against a single object instance:

confusing = "This Is Confusing"
confusing.instance_eval do
def lowercase
self.downcase
end
end

confusing.lowercase
=> "this is confusing"
"The Smiths on Charlie's Bus".lowercase
NoMethodError: undefined method ‘lowercase’ for "The Smiths on Charlie's Bus":String

So with instance_eval, the method is only defined for that single instance of a string.

So why does instance_eval on a Class define class methods?

Just as "This Is Confusing" and "The Smiths on Charlie's Bus" are both String instances, Array, String, Hash and all other classes are themselves instances of Class. You can check this by calling #class on them:

"This Is Confusing".class
=> String

String.class
=> Class

So when we call instance_eval it does the same on a class as it would on any other object. If we use instance_eval to define a method on a class, it will define a method for just that instance of class, not all classes. We might call that method a class method, but it is just an instance method for that particular class.



Related Topics



Leave a reply



Submit