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.