The timeless repository

Exception#continue

Written by Magnus Holm.

Imagine a world with Exception#continue:

10.times do |i|
  begin
    raise "OH NO!"
    puts "OH YES! #{i}"
    i += 1
  rescue => err
    if i < 5
      # Nah, let's ignore this exception and just continue
      # from where it was raised.
      err.continue
    else
      raise err
    end
  end
end

Oh, that’s right. This is Ruby. Forty lines later:

class Exception
  class NoContinuation < StandardError
  end
  
  attr_accessor :continuation
  
  def continue
    raise NoContinuation unless continuation.respond_to?(:call)
    continuation.call
  end
end

module NeverGonnaLetYouDown
  def raise(exception = RuntimeError, string = nil, array = caller)
    # With a single String argument, raises a
    # RuntimeError with the string as a message. 
    if exception.is_a?(String)
      string = exception
      exception = RuntimeError
    end
    
    callcc do |cc|
      obj = exception.exception(string)
      obj.set_backtrace(array)
      obj.continuation = cc
      super obj
    end
  end
  
  def fail(exception = RuntimeError, string = nil, array = caller)
    raise(exception, string, array)
  end
end

class Object
  include NeverGonnaLetYouDown
end