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.