Skip to content

Instantly share code, notes, and snippets.

@armstrjare
Created July 16, 2011 11:42
Show Gist options
  • Save armstrjare/1086285 to your computer and use it in GitHub Desktop.
Save armstrjare/1086285 to your computer and use it in GitHub Desktop.
A proof of concept that Ruby's native catch/throw can be [almost*] re-implemented using Ruby continuations.
# A proof of concept that Ruby's native catch/throw can be [almost*] re-implemented in Ruby.
#
# * begin/ensure semantics don't work when calling a Continuation
# -- so, unfortunately this implementation is not semantically equivalent to Ruby's native version :( But still cool nonetheless
module ContinuationCatchThrow
require 'continuation' unless defined?(Continuation)
# Emulate ruby Kernel#catch
def my_catch(tag = Object.new, &blk)
# This is a global stack that keeps track of the current "catch" blocks we're in.
# It's an array of arrays, where the first value is the tag we're catching (or nil if 'any tag').
# The second value is a continuation to bring the program state back to the point it was at when the catch block was entered.
Thread.current[:__catch_tags] ||= []
# Count the number of catches on the stack.
# We use this to reset the state after leaving a catch block
catches = Thread.current[:__catch_tags].length
# Return caught thrown value (unless returned from catch block)
return callcc { |cc|
Thread.current[:__catch_tags].push([tag, cc])
# Yield the catch block and return it (i.e. no throw)
return yield(tag)
}
ensure
# Reset the stack to what it was when we entered this catch call.
Thread.current[:__catch_tags].slice!(catches, Thread.current[:__catch_tags].length)
end
# Emulate Kernel#throw
def my_throw(tag, value = nil)
Thread.current[:__catch_tags] ||= []
# Detect the catcher
catcher_index = Thread.current[:__catch_tags].rindex { |item| item[0].nil? || item[0] == tag }
# We have to find a catcher
raise ArgumentError, "uncaught throw #{tag.inspect}" unless catcher_index
# Call the continuation set for this catcher
catcher = Thread.current[:__catch_tags][catcher_index]
catcher[1].call(value)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment