0

When passing a block to instance_eval, it is meant to be executed within the context of that instance. self, when referenced either explicitly or implicitly within that block, should refer to the instance that instance_eval has been called on. This seems to work fine in all cases, except when passing a method object that has been converted to a proc. In this case, self refers to the instance that the method is defined on, rather than the instance where the block is evaluated. Here's a code example to demonstrate what I mean:

class A
  def test(&b)
    instance_eval(&b)
  end
end

class B
  def test_a(a)
    a.test { puts self }
  end

  def test_b_helper(*args)
    puts self
  end

  def test_b(a)
    m = method(:test_b_helper).to_proc
    a.test(&m)
  end
end

a = A.new
b = B.new

b.test_a(a) #<A:0x007ff66b086c68>
b.test_b(a) #<B:0x007fa3e1886bc0>

The expected behaviour is for both tests to return the same output. In this case, self should refer to an instance of A, not B.

I have looked in the docs and done some searches, but I have not been able to find information on this peculiarity. I am hoping that there are some experienced Rubyists who can help clear up this difference in behaviour.

Just to clarify, I am using Ruby 1.9.2.

1 Answer 1

3

The difference is, that Blocks and Procs are closures, Method objects are not.

Excerpt from D. Flanagan, Y. Matsumoto. The Ruby Programming Language, O'Reilly 2008, p. 204:

One important difference between Method objects and Proc objects is that Method objects are not closures. Ruby’s methods are intended to be completely self-contained, and they never have access to local variables outside of their own scope. The only binding retained by a Method object, therefore, is the value of self — the object on which the method is to be invoked.

When you now cast the Method object to_proc, you bind the value of self to the calling instance of B, hence you get the result you described above. Actually, self is already fixed when you create the Method object.

This gets particularly clear when you consider the following code:

class A
  def foo
    puts 'bar'
  end
end

class B; end

class C < A; end

foo = A.instance_method(:foo)
# => #<UnboundMethod: A#foo>

a = A.new
foo.bind(a).call
# bar

b = B.new
foo.bind(b).call
# TypeError: bind argument must be an instance of A

c = C.new
foo.bind(c).call
# bar

Simply put: self is always fixed on Method and UnboundMethod objects.

Sign up to request clarification or add additional context in comments.

1 Comment

@tabdulla: added a clarifying example to my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.