Skip to content

Instantly share code, notes, and snippets.

@everton
Last active April 21, 2024 18:57
Show Gist options
  • Save everton/c1b0b553df2e656c88a01249688e6218 to your computer and use it in GitHub Desktop.
Save everton/c1b0b553df2e656c88a01249688e6218 to your computer and use it in GitHub Desktop.
Inspired by Common LISP conditions system, this code here uses continuations to inject "restart" options from the context where an Exception was raised and allowing to resume from the error line. Obs.: since this code relies on continuations, it is not supposed to work on Ruby implementations without this resource (such as JRuby, Ribinius, etc)
require 'continuation'
class Restart < RuntimeError
attr_accessor :menu
def initialize(**menu)
@menu = menu
@menu.each do |name, proc|
define_singleton_method "#{name}!" do
proc.call
end
end
end
end
begin
x = 10
callcc do |c|
raise Restart.new \
resume: -> { c.call },
inc_x: -> { x += 1; c.call }
end
puts "x: #{x}"
rescue Restart => e
# binding.irb
puts "Restart options:"
e.menu.keys.each_with_index do
puts " - #{_2 + 1}: #{_1}"
end
e.inc_x!
end
@everton
Copy link
Author

everton commented Apr 21, 2024

The output of running this using MRI Ruby is like:

Restart options:
  - 1: resume
  - 2: inc_x
x: 11

@everton
Copy link
Author

everton commented Apr 21, 2024

After I worked on this experiment, I found this blog post (from almost 20 years ago!) with a better approach and also porting the example from the Practical Common LISP book (which explains very well why to use this):

https://leahneukirchen.org/blog/archive/2005/03/restartable-exceptions.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment