As I understand it, continuations are, in a way, a
superset of closures. A continuation creates a
closure that also has information on where it was
executing at the time, so when you execute the
continuation, it goes back to that place, in addition
to having the appropriate context. Closures don’t
simply make a copy of the stack and restore it, it’s
more like they make a reference to a persistant
lexical storage area (read about closures on Dan’s
blog about parrot for a better explanation of this).
So for example:
def foo
x = 5
q = proc { x }
x = 3
end
fun = foo
puts fun
Would, I think print 3, rather than 5, since q
would store a reference to the lexical context of
that call to foo, rather than just making a copy of
the stack up to when proc was called. Likewise,
callcc would make a reference to that lexical
scratch-pad, and values wouldn’t be replaced with
their previous values by resuming the continuation.
I think this makes callcc a bit different than
setjmp and longjmp from C (although I haven’t used
them, only read about them), as I believe they make
a copy of, and restore the stack.
Note, though, that I could be way off base, since
I’m not able to test the closure example right now.
It’s based on my understanding of them, though.
You should be able to implement closures with
continuations, though, and if continuations changed
the values back to what were when the continuation
was taken, then you couldn’t implement something
like:
def foo
x = 1
return proc { x += 1; x }
end
With continuations without some major headaches, I think.
Hope this all makes sense.
- Dan
···
----- Original Message -----
From: Brian Candler B.Candler@pobox.com
Date: Friday, August 8, 2003 5:40 am
Subject: Re: Why does Ruby have callcc?
Here’s my entry for the ‘writing BASIC in Ruby’ competition:
$lines = {}
$running = false
$tron = false
def line(n,&blk)
callcc { |$lines[n]| }
puts “Line #{n}” if $tron and $running
yield if $running
end
def goto n
$lines[n].call
end
def run
unless $running
$running = true
$lines.sort[0][1].call
$end
endline(100) { puts “Hello World” }
line(110) { $i = 0 }
line(120) { if $i > 10 then goto 160 end }
line(130) { puts “Current $i: %d” % [$i] }
line(140) { $i = $i + 1 }
line(150) { goto 120 }
line(160) { puts “Done!” }
line(170) { puts “Stopping now” }$tron = true
run
One thing worth mentioning about continuations is that as far as I
can tell,
Continuation#call NEVER RETURNS. You are not pushing the current
executionstate onto a stack like Method#call or Proc#call; you are
simply replacing
the current execution context, like a longjmp in C. The current
executionthread is lot. In that case perhaps ‘call’ is not the
ideal name for this
method.The thing I am not sure of is what values of variables, if any,
are bound up
in the continuation. Take the following example:i = 0
here = nil
callcc {|here|}
puts i
i = i + 1
here.call unless i == 10 # here.call is just “goto here”
puts “Done!”This is not recursive - as mentioned before, here.call just
rewinds to the
point after the callcc.Now, clearly the value of variable ‘i’ at callcc time is not
included in
the continuation object ‘here’, because when I do ‘here.call’, the
subsequent ‘puts i’ shows ‘i’ with its new value.I think what confuses me is the discussion on the Wiki about
coming to a
crossroads, taking the left path, finding that you get bitten by a
dog, and
then deciding that was a bad idea so you ‘rewind back in time’ to the
crossroads. But if you did this using a Ruby continuation, surely
your leg
would still be bleeding?preferred_path = “LEFT”
bleeding = falsecrossroads = nil
callcc{ |crossroads| }puts “At the crossroads”
puts bleeding ? “I am bleeding!” : “I am OK”puts “I am going to go #{preferred_path}”
if preferred_path == “LEFT”
sleep 2
puts “Bitten by dog!”
bleeding = true
preferred_path = “RIGHT”
crossroads.call
else
sleep 2
puts “Hit by train!”
exit
end