Continuations


#1

Please help. I’m totally confused by following behaviour with regard to
continuations (Ruby 1.8.7) My goal was to simply jump between two loops,
which worked fine. But I do not really understand this behaviour:

def loller(id, num, that)
loop do
puts id.to_s + " " + num.to_s
num += 1
sleep(0.5)

    # why do i need to update 'that' here, but not 'well' in the

while loop??
# callcc { |here| that.call(here) }, w/o assignment doesn’t work
here
that = callcc { |here| that.call(here) }

    # any subsequent calls to 'well' (well.call) should return to

THIS POINT
# with num == 35
# since I never reassign the continuation referenced by ‘well’

end

end

i = 0
well = callcc { |c| loller(1,34,c) }

while true

# why don't I need: well = callcc { |here| well.call(here) }
# callcc { |here| well.call(here) }, w/o assignment works here!!!
callcc { |here| well.call(here) }
puts "back to root " + i.to_s
i += 1

end

This program increases a simple counter in each loop, and prints it.
However, I do not understand why this works, since I am not reassigning
the continuation returned by the loop in function “loller”. The call
made from the while-loop was looking like this to begin with:

well = callcc { |here| well.call(here) }

Which gives the same result, even when i remove the assignment. That
means the ‘well’ variable still references the original continuation
i.e. value of ‘num’ inside loller should always be the same as in
original continuation? Should I remove the analogous assignment from the
loop in “loller” then as expected i never get “back to root” printout,
since then “loller” always returns to the point after the original
assignment, and thst is just before the while loop.

Anybody who understand this, please explain. Big thanks in advance.

/Paul W Florczykowski


#2

Responding to Paul w Florczykowski:

[Disclaimer: I’m certainly not a pro on continuations, but I’ll just
give it a try. Please bear with me.]

    # any subsequent calls to 'well' (well.call) should return to THIS POINT
# callcc { |here| well.call(here) }, w/o assignment works here!!!
callcc { |here| well.call(here) } #B
puts "back to root " + i.to_s
i += 1

end

Continuations don’t capture a frozen state but rather a live environment

  • you can return to the same continuation and encounter different local
    state each time.

i = 0
callcc{|cc| puts “assign once”; $cc = cc}
i += 1
puts(i)
$cc.call unless i == 3

So you don’t need to ‘update’ your continuation references in order to
keep track of changed state (such as the num value inside loller). The
reason you require the assignment to ‘that’ is because on the initial
method invocation it will have the ‘wrong’ value - it will transfer back
to #A, and you have to fix that after the first control transfer back
into loller via ‘well’ to point to #B instead. (Which is also why you
get two successive messages from loller before the root messages kick
in.)

A slightly modified version of your code:

def loller(id, num, that)
loop do
puts id.to_s + " " + num.to_s
num += 1
sleep(0.5)
callcc { |here| that.call(here) }
end
end

i = 0
well = nil
while true
if(well.nil?)
well = callcc { |there| loller(1,34,there) }
else
callcc { |there| well.call(there) }
end
puts "back to root " + i.to_s
i += 1
end

Best regards,
Patrick


#3

Paul w Florczykowski wrote:

Anybody who understand this, please explain. Big thanks in advance.

The way I think of it: firstly

  • A closure is an environment in which variables are evaluated and
    stored

Once you ‘get’ closures, then:

  • A continuation is a closure plus an execution pointer

Some closure examples I read a while back were not helpful. One I think
talked about going across a railway crossing, getting run over by a
train, and then rewinding to before you went across.

Using a continuation, you can rewind to before you went across, but
you’ll still be dead :slight_smile:

Regards,

Brian.


#4

Patrick R. wrote:


So you don’t need to ‘update’ your continuation references in order to
keep track of changed state (such as the num value inside loller). The
reason you require the assignment to ‘that’ is because on the initial
method invocation it will have the ‘wrong’ value - it will transfer back
to #A, and you have to fix that after the first control transfer back
into loller via ‘well’ to point to #B instead. (Which is also why you
get two successive messages from loller before the root messages kick
in.)

Best regards,
Patrick

Appreciate the reply! it actually helped me understand this! Thanks
again!