story
def with_handler(ctx, h):
return {'handler': h, 'next': ctx}
def with_restart(ctx, name, r):
return {'restart': (name, r), 'next': ctx}
def find_handler(ctx, err):
if 'handler' in ctx:
return ctx['handler']
elif 'next' in ctx:
return find_handler(ctx['next'], err)
else:
raise Exception('Unhandled error %s' % err)
def find_restart(ctx, name):
if 'restart' in ctx and ctx['restart'][0] == name:
return ctx['restart'][1]
elif 'next' in ctx:
return find_restart(ctx['next'], name)
else:
return error(ctx, 'No restart named %s' % name)
def error(ctx, err):
find_handler(ctx, err)(ctx, err)
raise Exception('No handler found for %s in %s', err, ctx)
def do_stuff(ctx):
results = []
for i in range(100):
results.append(do_little_stuff(ctx, i))
return results
class alpha(Exception):
def __init__(self, text):
self.text = text
def do_little_stuff(ctx, i):
def restart(ctx):
raise alpha('text: %d' % i)
ctx = with_restart(ctx, 'use-alpha', restart)
try:
if i % 3 == 0:
error(ctx, '%d %% 3 == 0' % i)
return i
except alpha as e:
return e.text
def handler(ctx, err):
find_restart(ctx, 'use-alpha')(ctx)
ctx = with_handler({}, handler)
print do_stuff(ctx)
Note how the top level provides a contextual handler which chooses to use the alpha restart, while the restart itself is provided by the mid-level do_stuff, while the low-level do_little_stuff both provides the restart & signals the condition.Add in some type detection &c. and this could start to be useful. But it'd still be hellaciously verbose compared to the Lisp:
(defun do-stuff ()
(loop for i below 100 collect (do-little-stuff i)))
(define-condition mod-three () ())
(defun do-little-stuff (i)
(restart-case
(progn
(when (zerop (mod i 3)) (error 'mod-three))
i)
(use-alpha () (format nil "text: ~d" i))))
(handler-bind ((mod-three #'(lambda (c) (invoke-restart 'use-alpha))))
(do-stuff))