Sam Ruby has a much praised blog entry entitled “Continuations for Curmudgeons”.
I didn’t like the tone of the article which seem to treat the C programmers as some kind of dinosaur with no interest in other languages (or language features).
There was no mention of Scheme. When learning about continuations I think it’s best to learn from the source. That source appears to be the Scheme programming language. R5RS includes call-with-current-continuation function usually shortened to call/cc. A really good introduction to Scheme with coverage of continuations for non-trivial applications is contained in Dorai Sitaram’s Teach Yourself Scheme in Fixnum Days (TYSiFD).
Sam seems to have some confusion about continuations and there relationship to closures, iterators, generators and coroutines. Just because a language has a “yield” keyword doesn’t mean it has first class continuations. Yield is usually used for generators. One of the highlights of TYSiFD is implementing coroutines with continuations.
Ruby supports first class continuations. The callcc method is available from the Kernel class. I played around with the code from the Ruby documentation of the Continuation class. You really don’t need call/cc for the contrived example there:
original_guys = ["Freddie", "Herbie", "Ron", "Max", "Ringo"]
puts "method 1: (first-class) continuation"
guys = original_guys.clone
callcc{|$k|}
puts(message = guys.shift)
$k.call unless message =~ /^R/;
puts
puts "method 2: continuation with different way of setting global"
guys = original_guys.clone
callcc{|k| $k = k}
puts (message = guys.shift)
$k.call unless message =~ /^R/;
puts
puts "method 3: just a loop"
guys = original_guys.clone
loop {
message = guys.shift
puts message
if (message =~ /^R/)
break
end
}
puts
puts "method 4: (tail) recursion using methods/functions"
puts " Ugly - needs guys to be global variable."
$guys = original_guys.clone
def myloop
message = $guys.shift
puts message
unless message =~ /^R/
myloop
end
end
myloop
puts
puts "method 5: (tail) recursion using blocks"
guys = original_guys.clone
myloop = Proc.new{
message = guys.shift
puts message
unless message =~ /^R/
myloop.call
end
}
myloop.call
puts
puts "method 6: using each and catch/raise"
guys = original_guys.clone
catch (:early_exit) {
guys.each {|item|
puts item
if item =~ /^R/
throw :early_exit
end
}
}