Skip to content

Instantly share code, notes, and snippets.

@headius
Created October 26, 2011 08:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save headius/1315794 to your computer and use it in GitHub Desktop.
Save headius/1315794 to your computer and use it in GitHub Desktop.
RESET_STACK = []
SHIFT_MAP = {}
def reset(&block)
RESET_STACK.push block
block.call()
ensure
RESET_STACK.pop
end
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?
value
else
shift_proc = proc do |arg|
SHIFT_MAP[block.source_location] ||= []
SHIFT_MAP[block.source_location].push arg
RESET_STACK[-1].call
end
block.call(shift_proc)
end
end
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
@stepheneb
Copy link

correction:

p reset { 2 * 2 } => 4

interesting reference: http://chneukirchen.org/blog/archive/2005/04/shift-reset-and-streams.html

@headius
Copy link
Author

headius commented Oct 26, 2011

@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.

@stepheneb
Copy link

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