Create a gist now

Instantly share code, notes, and snippets.

def reset(&block)
RESET_STACK.push block
def shift(&block)
if SHIFT_MAP.key? block.source_location
ary = SHIFT_MAP[block.source_location]
value = ary.pop
SHIFT_MAP.delete(block.source_location) if ary.empty?
shift_proc = proc do |arg|
SHIFT_MAP[block.source_location] ||= []
SHIFT_MAP[block.source_location].push arg
p reset { 2 * 2 } # 4
p reset { 2 * shift {|k| 2 } } # 4
p reset { 2 * shift {|k| k[2] } } # 8
p reset { 2 * shift {|k| k[k[2]] } } # 16
p reset { 2 * shift {|k| k[shift {|j| j[3]}] } } # 24
p reset { 2 * shift {|k| k[3] + k[4]} } # 28


p reset { 2 * 2 } => 4

interesting reference:


@stephenb: Duh, thanks for the correction.

Yeah, of course this impl only works downstream from reset; you can't actually save off the continuation and call it from outside reset. That would only be possible with a library or JVM support for real continuations/coroutines.

My version obviously also deepens the stack for the nested continuation invokes, where a Lisp or Scheme VM would probably TCO some of those away.


I'm interested in understanding more about shift-reset and was looking for more interesting examples where it would be useful and came across Christian's article.

Then I was trying to work out how the two implementations differed and why his example didn't work with your implementation. Stepping through the code in the different implementations was a bit of a mind-bender. Haven't used continuations in Ruby before.

Does the way you've implemented shift-reset prevent creating some kind of lazy forkable iterator similar to what Christian made with each_to_stream(collection)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment