Hacker News new | ask | show | jobs
by zeveb 2584 days ago
Here's a stupidly-simple example in Python. So very not ready for production use:

    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))
2 comments

Ah, like the old Visual Basic "ON ERROR" system? (Of course, that was nearly always used for ON ERROR RESUME NEXT)
To have this used by libraries and libraries users won't you need some standardization though in the language? Otherwise you get various implementation.
Yup, that's another reason I love Lisp — it's been standardised since 1994, and everyone supports it (and since it's standardised and provided by the implementation, there are efficiency hacks possible which you wouldn't want in normal library code).

Lisp has been doing things newer languages still don't do since before a lot of modern programmers were even born. And there are relatively few warts for that age (upcasing and the pathname functions are the warts which leap to mind)!