Nontinuations and throw / catch

Hi folks,
I've been playing around with continuations a bit. What I'd like to be
abale to do is execute arbitary code sequences, suspend them, and then
resume them. All very well, and all what I can do using Continuation.

The problem is, that I want to be able to modify certain aspects of the
context while the code is suspended, and then have these picked up when
I resume. Consider the following code:

#callcc test

class Func
  attr_reader :cont
  def initialize
  end

  def do
    @cont ? cont.call : do1
    puts "after cont call"
  end

  def do1
    puts 'A'
    callcc { |cc| @cont=cc; throw :wait}
    puts 'B'
    @cont=nil
  end

end

f=Func.new
(1..2).each do |i|
  puts "Before do : i= #{i}"
  catch :wait do
    f.do
  end
  puts "After do : i= #{i}"

end

Now consider the output:

Before do : i= 1
A
After do : i= 1
Before do : i= 2
B
after cont call
After do : i= 1
Before do : i= 2
A
After do : i= 2

Basically, what is happening is that the continuation is the entire
programme context, and hence the loop variable i is overwritten.

This isn't quite what I am trying to achieve, which is to be able to
use a throw / catch mechanism in conjunction with the continuation to
be able to jump out of a function at arbitary points, and then return
to it later. What I really want is to be able to keep the contxt from
the catch downwards, so that I can keep the higher layers of the
context. In this case the desired output would be:

Before do : i= 1
A
After do : i= 1
Before do : i= 2
B
after cont call
After do : i= 2

Any ideas?

Best regards

Steve

PS (before anyone asks, I'm trying to put together a library for
discrete event simulation, and I'd like to be able to easily specify
arbitary breakpoints in functions, and then resume at them later, in
the knowledge that some (higher level) data may have changed.

Hi Steve,

you can find examples for continuations and coroutines in the archives of ruby-talk. You can also look at the Generator class in the standard library. I think for jumping back and forth you need two continuations instead of just one continuation and a throw/catch. I can dig out some code samples if you need them.

Regards,
Pit

Excerpts from Pit Capitain's mail of 10 Feb 2005 (EST):

you can find examples for continuations and coroutines in the archives
of ruby-talk. You can also look at the Generator class in the standard
library. I think for jumping back and forth you need two continuations
instead of just one continuation and a throw/catch. I can dig out some
code samples if you need them.

Pit is absolutely right: you don't need throw/catch, just two
continuations that you jump back and forth between.

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.

(I also wrote about how generator.rb works at
http://www.all-thing.net/Ruby/iterators_generators_and_continuations_in_ruby.html, which describes some of the idioms in using continuations.)

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

  def call(*a)
    raise "not in callable state" if @inside
    if @outside = callcc { |c| c }
      @proc.call self, *a
      @outside.call if @outside
    end
  end

  def resume
    raise "not in a resumable state" unless @inside
    @inside.call if @outside = callcc { |c| c }
  end

  def suspend
    raise "not in a suspendable state" unless @outside
    @outside.call if @inside = callcc { |c| c }
  end
end

## example usage:

f = Resumable.new do |r|
  puts "started!"
  r.suspend
  puts "in the middle"
  r.suspend
  puts "done!"
end

i = 0
puts "calling, i = #{i}"
f.call
puts "suspended, i = #{i}"

i += 1
puts "resuming, i = #{i}"
f.resume
puts "suspended, i = #{i}"

i += 1
puts "resuming, i = #{i}"
f.resume
puts "done, i = #{i}"

## end

HTH,

···

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

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
initial call: got hello
b: resuming with message 0
b: got how's it going?
b: resuming with message 1
b: got goodbye!
b: resuming with message 2
b: got see ya!
done!

···

a: started (args howdy)
a: said hello, got message 0
a: said how's it going, got message 1
a: done! said goodbye, got message 2

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

William Morgan ha scritto:

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.

I think you just reinvented coroutines :slight_smile:

Excerpts from gabriele renzi's mail of 11 Feb 2005 (EST):

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

I think you just reinvented coroutines :slight_smile:

Hey, just giving the people what they want. Not my fault it was invented
40 years ago. :slight_smile:

···

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