Playing with Proc#bind

When I write internal DSLs in Ruby, I tend to do so by leveraging Object#instance_eval. #instance_eval is powerful because it’s yet another way that you can repoint self in Ruby. Object#instance_eval executes its supplied Proc in the context of the calling Object Below is a simple example.

  def car(&block)
    # self is "main" (an Object) here
    c = Car.new
    # self refers to Car instance when Proc is evaluated on the line below
    c.instanceeval(&block) if blockgiven?
    c
  end

class Car
def make(v); @make = v; end def model(v); @model = v; end def engine(v); @engine = v; end end

car do make "Ford" model "Fusion" engine :piece_of_crap end

You could implement the same code by replacing Object#instance_eval with calls to Proc#bind as follows:

  def car(&block)
    # self is "main" (an Object) here
    c = Car.new
    # self refers to Car instance when Proc is evaluated on the line below
    block.bind(c).call if block_given?
    c
  end
  

Remember, Procs are also closures. That is, they take the context within which they were created along with them. For example:

  def make_incrementer
    x = 0
    Proc.new { puts self; x += 1}
  end

inc = make_incrementer # Will print 1 to 5 interleaved with "main" 5.times { puts inc.call }

… gives us …

  # >> main
  # >> 1
  # >> main
  # >> 2
  # >> main
  # >> 3
  # >> main
  # >> 4
  # >> main
  # >> 5
  

Wherever you pass that Proc, when you call it, puts self would always say main (which is just the “root” Object of the Ruby interpreter).

So now we add the following code:

  class Foo
  end    

foo = Foo.new rebound_inc = inc.bind(foo)

5.times { puts rebound_inc.call }

We didn’t change the Proc. We just created a Method, rebound_inc, where self now points to our instance of Foo_. So our code, with the above additions, when executed, now returns:

    # >> main
    # >> 1
    # >> main
    # >> 2
    # >> main
    # >> 3
    # >> main
    # >> 4
    # >> main
    # >> 5
    # >> #<Foo:0x10019092>
    # >> 6
# >> #<Foo:0x10019092> # >> 7
# >> #<Foo:0x10019092> # >> 8
# >> #<Foo:0x10019092> # >> 9
# >> #<Foo:0x10019092> # >> 10

In the example above, reboundinc_ is still a closure over x, defined in make_incrementer, but it’s self now points to whatever object we like.

Posted by evan on Thursday, November 05, 2009

blog comments powered by Disqus