From: szergling
Subject: Macros for restarts
Date: 
Message-ID: <4219151a-fc0b-4f98-bcce-7672f07ec34f@d38g2000prn.googlegroups.com>
Seeing as restarts were mentioned as 'infrequently used CL features',
I thought I might have two macros to show, tell and share today. One
I've had for a while, but very very seldom use. Another, I just made
yesterday, because with-simple-restart was too simple :) (always
returns (values nil t) only? What use is that?)

For these examples, provide your own with-unique-names.

Here's a macro to programmatically, or automatically invoke some
restart. Usually useful around the very outermost loop of your program
(usually the REPL for me). Also very useful for pesky recurring
errors (signalled inside some tight loop for example).

(defmacro auto-invoke-restart ((error name &optional hook) &body body)
  "Executes the body, and if an error of type 'error' is signalled,
always/automatically invoke the restart 'name'. Optionally, when/if
invoking the restart, run hook too."
  (with-unique-names (e)
    `(handler-bind ((,error
                     (lambda (,e)
                       (declare (ignorable ,e))
                       ,(when hook
                              `(funcall ,hook ,e))
                       (invoke-restart ,name))))
      ,@body)))

This, I just wrote yesterday, as a wrapper around
with-simple-restart. Place this at strategic places to offer more
useful restarts.

(defmacro with-action-restart ((restart-name format-string &rest
format-args)
                               body-form &body actions)
  "Offers a restart that (when invoked) performs actions, returning
the value of the last form in actions (implicit progn)."
  (with-unique-names (return-values errorp)
    `(multiple-value-bind (,return-values ,errorp)
      (with-simple-restart (,restart-name ,format-string ,@format-
args)
        (multiple-value-list ,body-form))
      (if (and (not ,return-values) ,errorp)
          (progn ,@actions)
          (values-list ,return-values)))))

Some usage examples:

On error, provides the two restarts that print some stuff and returns
some value.

(defun nested-eg ()
  ;; This would usually be nested deeper, several functions deep.
  (with-action-restart (outer "This is the outer restart. ~a ~a" 'blah
'blah)
      (with-action-restart (inner "This is the inner restart.")
          (error "Test-error")
        (print 'inner)
        :inner)
    (print 'outer)
    :outer))

And to automatically invoke some restart.


(auto-invoke-restart (error 'inner
                      (lambda (err)
                        (format t "Got error ~a, invoking a restart."
err)))
  (nested-eg))
Prints  ==> Got error Test-error, invoking a restart.
Returns ==> INNER :INNER

My questions: Are they general enough? Have I missed anything (can you
break them, hack them, abuse them?). I would appreciate comments from
your experience with using the condition system, restarts and other
magic etc.

Cheers.