views:

95

answers:

1

Ruby has continuations... does it have a dynamic-wind construct like Scheme?

+5  A: 

[This answer is written with Scheme programmers in mind (the OP has asked other Scheme questions here before, so that's a safe bet). If you're here because you're a Ruby programmer with no Scheme background, read the footnote for some context. :-)]

MRI doesn't (see below); and if MRI doesn't, that means there is no portable way to use any such functionality even if another implementation provides it.

I actually inspected the MRI 1.9.1 source code, just to be sure. In any case, here is some code to demonstrate that even the normal unwind protection (ensure) doesn't work correctly with continuations on MRI (tested with both 1.8.7 and 1.9.1). (It does work correctly with JRuby (I tested with 1.5), so it goes to show it's an implementation-specific thing. But note that JRuby only provides escape continuations, not general-purpose ones.)

callcc do |cc|
  begin
    puts 'Body'
    cc.call
  ensure
    puts 'Ensure'
  end
end

(To test with MRI 1.9+, you need to either run with the -rcontinuation option, or put require 'continuation' at the top of the file.)


For readers who don't know what a dynamic-wind is, it's a way to specify code to be run when the code being covered is exited (much like ensure), as well as code to to be run when the covered code is re-entered. (This can happen when you use call/cc inside the covered code, and invoke the continuation object after the covered code has been exited.)

Totally contrived example:

def dynamic_wind pre, post, &block
  raise 'Replace this with a real implementation, kthx'
end

def redirect_stdout port, &block
  saved = $stdout
  set_port = lambda {$stdout = port}
  reset_port = lambda {$stdout = saved}
  dynamic_wind set_port, reset_port, &block
end

cc = nil
# cheap way to nuke all the output ;-)
File.open '/dev/null' do |null|
  redirect_stdout null do
    callcc {|cc|}
    puts 'This should not be shown'
  end
  puts 'This should be shown'
  cc.call
end

So, a properly-functioning dynamic_wind implementation would ensure that $stdout would be set back to the /dev/null stream when the continuation is invoked, so that at all instances where puts 'This should not be shown' is run, that text is indeed not shown.

Chris Jester-Young