Excerpts from William Morgan's mail of 11 Feb 2005 (EST):
> Here's something I whipped up that lets you suspend and resume an
> arbitrary function. For simplicity, it doesn't pass return values from
> resume/suspend, but it wouldn't be too hard to modify it to do that.

What the hey, here's a version that lets you pass values with suspend
and resume as well. It's not too much more complex.

class Resumable
  def initialize(&proc)
    @inside = nil
    @outside = nil
    @proc = proc
  end

  def callable?; @inside.nil?; end
  def resumable?; !@inside.nil?; end
  alias :done? :callable?

  def call(*a)
    raise "not in callable state" unless callable?
    callcc do |c|
      @outside = c
      ret = @proc.call(self, *a)
      @inside = nil
      @outside.call(ret)
    end
  end

  def resume(*a)
    raise "not in a resumable state" unless resumable?
    callcc do |c|
      @outside = c
      @inside.call(*a)
    end
  end

  def suspend(*a)
    raise "not in a suspendable state" unless @outside
    callcc do |c|
      @inside = c
      @outside.call(*a)
    end
  end
end

## example

f = Resumable.new do |r, *args|
  puts "a: started (args #{args.join(', ')})"
  y = r.suspend "hello"
  puts "a: said hello, got #{y}"
  y = r.suspend "how\'s it going?"
  puts "a: said how's it going, got #{y}"
  y = r.suspend "goodbye!"
  puts "a: done! said goodbye, got #{y}"
  "see ya!"
end

puts "calling"
x = f.call "howdy"
puts "initial call: got #{x}"

i = 0
until f.done?
  m = "message #{i}"
  puts "b: resuming with #{m}"
  x = f.resume m
  puts "b: got #{x}"
  i += 1
end

puts "done!"

## end

Output is:

calling
a: started (args howdy)
initial call: got hello
b: resuming with message 0
a: said hello, got message 0
b: got how's it going?
b: resuming with message 1
a: said how's it going, got message 1
b: got goodbye!
b: resuming with message 2
a: done! said goodbye, got message 2
b: got see ya!
done!

-- 
William <wmorgan-ruby-talk / masanjin.net>