Continuation - where does it continue

Why does the following code print the line "doing other stuff" twice?

def cc
    puts "do A"
    puts "----> 1. #{caller}"
    callcc { |cont| return cont}
    puts "----> 2. #{caller}"
    puts "do B"
end

puts "calling cc"
cont = cc()
puts "doing other stuff"
cont.call() if cont
puts "end of work"
puts

OUTPUT:
calling cc
do A
----> 1. remove2.rb:59
doing other stuff
----> 2. remove2.rb:59
do B
doing other stuff
end of work

Responding to michelemendel@gmail.com:

Why does the following code print the line "doing other stuff" twice?

def cc
    puts "do A"
    puts "----> 1. #{caller}"
    callcc { |cont| return cont}
    puts "----> 2. #{caller}"
    puts "do B"
end

puts "calling cc"
cont = cc()
puts "doing other stuff"
cont.call() if cont
puts "end of work"
puts

The continuation 'continues' from exactly the state where it left off: It starts after the callcc call, after returning from the cc method it continues one 'stack frame'[1] deeper after the cc() call, just as in normal execution - it won't 'jump back' to the code position after the continuation call.

Best regards,
Patrick

[1] Java lingo, don't know what it's called in Ruby

michelemendel@gmail.com schrieb:

Why does the following code print the line "doing other stuff" twice?

def cc
    puts "do A"
    puts "----> 1. #{caller}"
    callcc { |cont| return cont}

The continuation continues here, after the call to callcc.

    puts "----> 2. #{caller}" puts "do B"
end

puts "calling cc"
cont = cc() # <------ (X)
puts "doing other stuff"
cont.call() if cont
puts "end of work"
puts

OUTPUT:
calling cc
do A
----> 1. remove2.rb:59
doing other stuff
----> 2. remove2.rb:59
do B
doing other stuff
end of work

Above I've shown the place where the continuation continues. So when you call the continuation, you're back in the cc method after the callcc. The program prints the remaining messages of the cc method and returns nil (the result of puts). After the cc method returns, you're back at the place where the method has been called, which the line marked with (X). cont is set to nil, and the program prints "doing other stuff" for the second time.

If you dont' want this, you have to change the code to something like

   puts "calling cc"
   cont = cc()
   if cont
     puts "doing other stuff"
     cont.call()
   end
   puts "end of work"
   puts

Florian Groß has written something to make Continuation handling easier. You should be able to find it in the mailing list archives.

Regards,
Pit

michelemendel@gmail.com said:

Why does the following code print the line "doing other stuff" twice?

01 def cc
02 puts "do A"
03 puts "----> 1. #{caller}"
04 callcc { |cont| return cont}
05 puts "----> 2. #{caller}"
06 puts "do B"
07 end

08 puts "calling cc"
09 cont = cc()
10 puts "doing other stuff"
11 cont.call() if cont
12 puts "end of work"
13 puts

This is the seqence of events (by line number). Comments on what's
happening are in parenthesis.

08: calling cc
09 (calling cc)
02: do A
03: ----> 1. remove2.rb:59
04: (returning the continuation)
10: doing other stuff
11: (calling the continuation, so picking up in the middle of cc)
05: ----> 2. remove2.rb:59
06: do B
07: (returning from cc to the original location cc call site.
    Now we return a nil value)
10: doing other stuff
11: (since cc returned a nil the second time, we don't call the
       continuation this time)
12: end of work

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

AHA - EUREKA!!!

Thanks for your very quick answers! After "studying" continuations for
a couple of days, reading everthing I could find about, I think I
finally understand it. Your answers made it all clear.

Michele

Thanks, the line numbers help a lot!

regards,
Randy Kramer

···

On Wednesday 21 September 2005 10:36 am, Jim Weirich wrote:

michelemendel@gmail.com said:
> Why does the following code print the line "doing other stuff" twice?

This is the seqence of events (by line number). Comments on what's
happening are in parenthesis.

What I don't understand is why the line:

    04 callcc { |cont| return cont}

Doesn't give an 'undefined local method or variable "callcc"' error.
To my noob eyes that looks like a method call w/an accompanying block.
Have you got a clue for me?

Thanks!

-Roy

Hi,

this finally cleared up Ruby's continuations for me too.

Would this be the effect you're after?
# code
def cc
   puts "do A"
   puts "----> 1. #{caller}"
   cont = callcc { |cont| return cont}
   puts "----> 2. #{caller}"
   puts "do B"
end

puts "calling cc"
if cont = cc()
  puts "doing other stuff"
  cont.call() if cont
  puts "end of work"
  puts
end

#output
__END__
calling cc
do A
----> 1. continuations.rb:10
doing other stuff
----> 2. continuations.rb:10
do B
#end

I'm curious - what problem are you trying to solve?

Regards,
Sean

···

On 9/21/05, michele <michelemendel@gmail.com> wrote:

AHA - EUREKA!!!

Thanks for your very quick answers! After "studying" continuations for
a couple of days, reading everthing I could find about, I think I
finally understand it. Your answers made it all clear.

Michele

You've got good eyes. That's exactly what it is ... a call to the "callcc"
method passing a block.

···

On Wednesday 21 September 2005 10:51 pm, rpardee@gmail.com wrote:

What I don't understand is why the line:

    04 callcc { |cont| return cont}

Doesn't give an 'undefined local method or variable "callcc"' error.
To my noob eyes that looks like a method call w/an accompanying block.

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

I justed wanted to understand how continuations work and where it
happens, so I made the simplest example possible I could think of. I
wanted to start work one place, leave it in the middle, do some other
work, and then continue from where I left.

Your answer as well as Pit Capitain's above solved the problem.