Journal InTime


2014-02-20 (Thu) [長年日記]

_ A memory of Jim Weirich

Today, I heard that Jim Weirich had passed away, but I can't believe it yet.

I met him at the first time at RubyConf 2005. It was also the first time I have attended RubyConf. He did a workshop titled "Continuations Demystified" with Chad Fowler there. In the workshop he explained continuations by comparing them to The Legend of Zelda, which is a Nintendo 64 video game. I had such a fun time.

You can download presentation materials from the following site:

<URL:http://www.zenspider.com/Languages/Ruby/RubyConf2005.html>

It's too late, but I've solved an exercise from the workshop, which exercise I didn't solve at that time.

The exercise is to implement throw/catch using continuations.

EXAMPLE:

result = cc_catch(:tag) {
  do_something()
  cc_throw(:tag, 1)
  fail "You never get here"
}
assert_equal 1, result

My answer is:

require "continuation"

def cc_tag_stack
  Thread.current[:cc_tag_stack] ||= []
end

def cc_catch(tag)
  callcc { |c|
    cc_tag_stack.push([tag, c])
    begin
      yield
    ensure
      cc_tag_stack.pop
    end
  }
end

def cc_throw(tag, obj = nil)
  stack = cc_tag_stack.dup
  while x = stack.pop
    t, c = x
    if t == tag
      # The ensure clause in cc_catch is not called when c.call(obj) is
      # called, so cc_tag_stack should be rewound here.
      Thread.current[:cc_tag_stack] = stack
      c.call(obj)
    end
  end
  raise NameError, "uncaught throw #{tag}"
end

I don't like this code very much because cc_throw is ugly. Jim's code is simpler:

$continuations = {}

def cc_catch(sym)
  callcc { |cc|
    $continuations[sym] ||= []
    $continuations[sym] << cc
    yield
  }
ensure
  $continuations[sym].pop
end

def cc_throw(sym, value=nil)
  cc = $continuations[sym]
  fail NameError, "uncaught throw `#{sym}'" if cc.nil? || cc.empty?
  cc.last.call(value)
end

I thank Jim for all his efforts for Ruby and the community. May his spirit rest in peace.

Tags: Ruby