Search code examples
rubylanguage-designfinallycallcc

The semantic of call/cc and "ensure" in Ruby


As I know so far, Ruby is the only mainstream language that supports both call/cc and try/catch/finally (written as begin/rescue/ensure/end block).

I am not familiar with Ruby, but my intuitive tell me that there are potential conflicts of that two, since call/cc allows arbitrarily control flow and ensure require some guaranteed control flow (some code path MUST be executed in a pre-defined situation, namely leaving the containing block).

So, are there any conflicts exists in the language? if so what is the defined behavior of the language in such a case? Especially, what happen if call/cc being used in a begin/ensure block or in the ensure/end block? What if calling a captured call/cc after the block contains ensure clause?


Solution

  • In Ruby you have callcc, throw / catch and raise / rescue / ensure.

    throw terminates the block with ensure begin executed:

    catch(:done) {
      begin
        puts "before"
        throw :done
        puts "after"  # will not be called
      ensure
        puts "ensure"
      end
    }
    

    produces:

    before
    ensure
    

    callcc terminates the block with ensure begin skipped:

    callcc { |cc|
      begin
        puts "before"
        cc.call
        puts "after"   # will not be called
      ensure
        puts "ensure"  # will not be called
      end
    }
    

    produces:

    before
    

    Another example storing the Continuation object inside a global variable:

    begin
      puts "before"
      callcc { |cc| $cc = cc }
      puts "after"
    ensure
      puts "ensure"
    end
    
    $cc.call
    

    produces:

    before
    after
    ensure
    after
    ensure
    after
    ...