ruby blocks and evaluation contexts

If you’ve ever played around with JavaScript and jQuery then chances are you’ve stumbled on call and apply. These are methods on function objects that allow you to change what this points to. If you haven’t stumbled on those methods yet then you have more than likely used them indirectly via bind. John Resig has an excellent set of interactive lessons that demonstrate everything you’d ever wish to know about JavaScript’s scoping rules and the cool tricks you can do by manipulating the scope with call and apply. The lessons can be found at http://ejohn.org/apps/learn/.

Ruby has similar context manipulation facilities and they are even more useful. In fact one of the reasons that Ruby programmers are so fond of writing DSLs is because of methods like instance_eval, instance_exec, class_eval, class_exec, instance_variable_set, etc. that allow one to mess with the context. Combined with the syntax sugar that Ruby offers for passing around blocks of code a good Ruby programmer can pretty much bend the language into whatever form desired for solving a problem. Unfortunately I can’t offer a cool set of interactive lessons like John Resig but I can provide some templates that you can mess with to get a feel for some of Ruby’s context management tricks.

We’ll start of with a few simple methods that demonstrate some simple scoping rules for self. Fire up irb and type in the following code

# simple class to demonstrate some scoping rules
class ScopeDemo
  def instance_method
    puts self
  end

  def block_caller(&block)
    block.call
  end
end

scope_demo = ScopeDemo.new
# think about what the output should be of 
# the following method calls
# before typing them in and seeing the answer
scope_demo.instance_method
scope_demo.block_caller { puts self }

I hope you read the comments and thought about what the output of each method should have been. The first call is pretty simple and you shouldn’t be surprised by what it outputs. The second call requires a bit more thought. I have to do this experiment every time to figure out what’s going on. Since the block is created in the topmost context, self points to main and when it is evaluated by block_caller the block remembers that self points to main and prints out main. Now lets try something a little different. Add the following method and then think about what the output of the new method should be before typing it in and seeing the result.

class ScopeDemo
  def context_switched_caller(&block)
    instance_eval(&block)
  end
end

scope_demo.context_switched_caller { puts self }

The output of the above code should give you hint about what instance_eval does. instance_eval changes what self points to so that when you pass in a block of code the block gets evaluated with self pointing to the receiver of instance_eval. This is an extremely useful trick and when designing a DSL one of the things people do is create an object that has the required methods and instance variables and then use instance_eval to evaluate a piece of code in the context of that object. Here’s a non-trivial example

  def ret_apply(name_context, &block)
    unless block.nil? || @status == :failure
      # there is a block so we need to create a context for the block to execute in
      block_context, input = Object.new, @rest
      block_context.singleton_class.instance_eval do
        # define the names that the block can refer to
        name_context.each do |k, v|
          # transform all ranges to actual characters from the input and leave everything else alone
          actual_consumed_input = v.map {|c| c.class == Range ? input.get_slice_range(c) : c}
          define_method(k) { actual_consumed_input }
        end
      end
      # transform the current parsed input and define a method for it so that the block can access it
      ret_copy = @ret.map {|c| c.class == Range ? input.get_slice_range(c) : c}
      block_context.singleton_class.instance_eval { define_method(:ret) { ret_copy } }
      # evaluate the block and re-assign @ret to what the block returns wrapped in an array
      @ret = [*block_context.instance_exec(&block)]
    end
    # return self for fluent interfacing
    self
  end
The above code is part of pure Ruby PEG interpreter DSL that I wrote to learn about PEGs. I took inspiration from peg.js and the above method uses the trick I just mentioned to evaluate blocks of code during the parsing process. It’s hard to tell what’s going on but if you look at lines 4-12 you can see that I’m creating an object and then defining a whole bunch of methods on that object. Then I use that object as the context in which to evaluate the block.

The above presentation was probably a little terse but it should have given you a taste of the context manipulation facilities that are available in Ruby. There are tons of resources available on the web about Ruby’s metaprogramming facilities of which context manipulation is a small part of. If you just google “ruby block context” then you should get a nice set of articles that explore blocks and their uses in DSL design in a bit more detail.