Irb is a Text Adventure

December 16th, 2009

Ruby's flavor of classed OO offers a thoughtful balance of rigidity and flexibility along with plenty of introspection and reflection. Over time I have come to experience this model less as a framework for thought and more as a living thing with which to converse. Dave Thomas brought up the prospect of a Ruby object browser over nine years ago. That thread raised questions that still haven't been answered.

Unfortunately for a browser the rigidity offered by Ruby is optional. I personally feel that HTML with links is the only graphical form that comes close to capturing the needed range. I just described a blank slate for a digraph so feel free to think it's lame. The object browser is possible and desirable but after all these years Irb is still the Ruby browser of choice.

My take is that every object in Irb is like a noun in a text adventure, and likewise every object is different. There are common verbs and uncommon verbs, and not every noun supports every verb. The components of trial, error, failure, and reward are the same. Nothing ruins my suspension of disbelief like having to consult documentation, and I like to believe that I am programming for as long as possible. The notable missing link to the underground world of Zork is "look". Here is "look". It is nothing more than a mashup of introspection methods but I've found it indispensable.


# Examples:
#   foo.look
#   foo.class.look
#   foo.method(:meth).look
#   foo.method(:meth).source_context
#   foo.instance_variable_hash

Object.class_eval do
  def instance_variable_hash
    ret = {}
    instance_variables.each { |v|
      ret[v] = instance_variable_get(v)
    }
    ret
  end

  def look
    {
      :class => self.class,
      :interesting_methods => (methods - Object.new.methods).sort,
      :instance_variables => instance_variables.sort,
    }
  end
end

Class.class_eval do
  def look
    {
      :class => self.class,
      :ancestors => ancestors,
      :included_modules => included_modules,
      :interesting_methods => methods - Class.new.methods,
      :instance_methods => instance_methods(false),
      :class_variables => class_variables,
      :instance_variables => instance_variables,
      :constants => constants,
      :source_files => methods(false).collect { |m| method(m).source_location.try(:first) }.compact.sort.uniq,
    }
  end
end

Module.class_eval do
  def look
    {
      :class => self.class,
      :ancestors => ancestors,
      :included_modules => included_modules,
      :class_variables => class_variables,
      :interesting_methods => methods - Class.new.methods,
      :instance_methods => instance_methods(false),
      :instance_variables => instance_variables,
      :constants => constants,
    }
  end
end

Method.class_eval do
  if RUBY_VERSION < "1.9"
    def source_location
    end
  end

  def look
    {
      :class => self.class,
      :arity => arity,
      :name => name,
      :owner => owner,
      :receiver => receiver,
      :source_location => source_location,
    }
  end

  def source_context
    return nil unless source_location
    line_number = source_location.second
    file = File.open(source_location.first, "r")
    lines = file.lines.drop(line_number - 1)

    indent = lines.first.scan(/^ */).first.length
    end_marker = Regexp.new("^" << " " * indent << "end")

    interesting_lines = []
    begin
      line = lines.shift
      interesting_lines << line
    end until line =~ end_marker or interesting_lines.count > 20 or line.nil?

    file.close

    source_location << interesting_lines.join
  end
end

Plays well with 1. Wirble, 2. pretty printing, 3. Ruby 1.8, 4. Ruby 1.9, and OF COURSE your .irbrc.

More experimenting with closures and iteration led to some interesting results. Due to some quirks with Ruby's scope rules there are a few gotcha's and some of my initial assumptions about how variables are bound were wrong. Hopefully this entry will serve to enlighten those who, like me, wondered about the mystical Ruby closure.

What are closures?

Closures are an stateful object created on the fly when a block of code with its own scope becomes bound to variables in its environment. Closures consist of two elements. The first is a reference to some executable code such a block, proc, or method. The second is the set of variables that are bound to the scope of the executable block. This code creates a closure bound to c: n = 2; c = lambda { n * n }; A closure is created any time a lambda references variables not local in scope. To be perfectly clear a closure is loosely:


struct Closure {
    void *function;
    void *function_arguments;
};

What problem do closures solve?

Read the rest of this entry

When a proc or lambda is created the variables in local scope are bound and not copied. This means that if an outside method updates a variable the closure will see it, and if the closure updates a variable the method will see it. For example:


closures = []
for n in 0..7
  closures << lambda { n }
end
closures.map { |c| c.call } => [7, 7, 7, 7, 7, 7, 7, 7]

The variable n was updated outside the { n } closure. All eight closures will print 7 in this example since that was the last value of n. What if I need to keep a unique value of n for each closure? There’s a trick, make a function object. Here’s the solution:


def make_functor(n)
  lambda { n }
end

closures = []
for n in 0..7
  closures << make_functor(n)
end
closures.map { |c| c.call } => [0, 1, 2, 3, 4, 5, 6, 7]

Because make_functor’s argument (n) goes out of scope when make_functor returns it (n) can no longer be updated. This effect isolates the variable in the closure from the rest of the program and the variable n is not garbage collected because it is still referenced by the closure.

Now lets apply this to a real world problem. With the ruby-gnome2 library I am writing a class that displays a collection of ActiveRecord objects as an editable grid. Because ruby-gnome2 is a close match to the C bindings there is quite a bit of record keeping to do. Here is a loop that will add an “edited” signal handler to each column in a Gtk::TreeView. This code is buggy because n is shared between all closures created when signal_connect internally calls to_proc on the provided block. An edit to any cell will result in that change getting applied to only the cell in the last column since the loop exits with n on the last column.


for n in 0...NUM_COLUMNS
  renderer.signal_connect "edited" do |renderer, path, data|
    iter = @tree_store.get_iter path
    iter[n] = data
  end
end

Functors to the rescue!!!


def connect_edit_functor(renderer, n)
  renderer.signal_connect "edited" do |renderer, path, data|
      iter = @tree_store.get_iter path
      iter[n] = data
  end
end

for n in 0...NUM_COLUMNS
  connect_edit_functor(renderer, n)
end

Here we pass the relevant variables to a method that will bind the signal handler and then gracefully let those variables go out of scope before the loop updates n. The result is that each instance of the signal handling closure now holds its own copy of n at the correct value. The correct column will therefore be referenced when the “edited” signal invokes the callback.

Proc.new vs Lambda in Ruby

March 19th, 2007

I found the following lines of code on Wikipedia today. It’s a very succinct description of one important difference between a lambda and a Proc. Try printing the return value of f.call for more insight.


def foo
  f = Proc.new { return "return from foo from inside proc" }
  f.call # control leaves foo here
  return "return from foo" 
end

def bar
  f = lambda { return "return from lambda" }
  f.call # control does not leave bar here
  return "return from bar" 
end

puts foo # prints "return from foo from inside proc" 
puts bar # prints "return from bar" 

Whole article on Wikipedia about closures. . . And a bunch more on procs and blocks

Gametrees in Ruby

December 28th, 2006

Here is a Ruby implimentation of Tic Tac Toe, or Noughts and Crosses if you prefer. Just paste the following code into irb and type either me_first or you_first.

Read the rest of this entry