Common Lisp the Language, 2nd Edition

Next: Finding and Manipulating Up: Program Interface to Previous: Creating Conditions

## 29.4.7. Establishing Restarts

The lowest-level form that creates restart points is called `restart-bind`. The `restart-case` macro is an abstraction that addresses many common needs for `restart-bind` while offering a more palatable syntax. See also `with-simple-restart`. The function that transfers control to a restart point established by one of these macros is called `invoke-restart`.

All restarts have dynamic extent; a restart does not survive execution of the form that establishes it.

[Macro]

    with-simple-restart (name format-string {format-argument}*)
               {form}*

This is shorthand for one of the most common uses of `restart-case`.

If the restart designated by name is not invoked while executing the forms, all values returned by the last form are returned. If that restart is invoked, control is transferred to the `with-simple-restart` form, which immediately returns the two values `nil` and `t`.

The name may be `nil`, in which case an anonymous restart is established.

`with-simple-restart` could be defined by

    (defmacro with-simple-restart ((restart-name format-string
                                    &rest format-arguments)
                                   &body forms)
      `(restart-case (progn ,@forms)
         (,restart-name ()
           :report
             (lambda (stream)
               (format stream ,format-string ,@format-arguments))
           (values nil t))))

Here is an example of the use of `with-simple-restart`.

    Lisp> (defun read-eval-print-loop (level)
            (with-simple-restart
                (abort "Exit command level ~D." level)
              (loop
                (with-simple-restart
                    (abort "Return to command level ~D." level)
                  (let ((form (prog2 (fresh-line)
                                     (read)
                                     (fresh-line))))
                    (prin1 (eval form)))))))
     => READ-EVAL-PRINT-LOOP
    Lisp> (read-eval-print-loop 1)
    (+ 'a 3)
    Error: The argument, A, to the function + was of the wrong type.
           The function expected a number.
    To continue, type :CONTINUE followed by an option number:
     1: Specify a value to use this time.
     2: Return to command level 1.
     3: Exit command level 1.
     4: Return to Lisp Toplevel.
    Debug>

Compatibility note: In contrast to the way that Zetalisp has traditionally defined `abort` as a kind of condition to be handled, the Common Lisp Condition System defines `abort` as a way to restart (``proceed'' in Zetalisp terms).

Remark: Some readers may wonder what ought to be done by the ``abort'' key (or whatever the implementation's interrupt key is-Control-C or Control-G, for example). Such interrupts, whether synchronous or asynchronous in nature, are beyond the scope of this chapter and indeed are not currently addressed by Common Lisp at all. This may be a topic worth standardizing under separate cover. Here is some speculation about some possible things that might happen.

An implementation might simply call `abort` or `break` directly without signaling any condition.

Another implementation might signal some condition related to the fact that a key had been pressed rather than to the action that should be taken. This is one way to allow user customization. Perhaps there would be an implementation-dependent `keyboard-interrupt` condition type with a slot containing the key that was pressed-or perhaps there would be such a condition type, but rather than its having slots, different subtypes of that type with names like `keyboard-abort`, `keyboard-break`, and so on might be signaled. That implementation would then document the action it would take if user programs failed to handle the condition, and perhaps ways for user programs to usefully dismiss the interrupt.

Implementation note: Implementors are encouraged to make sure that there is always a restart named `abort` around any user code so that user code can call `abort` at any time and expect something reasonable to happen; exactly what the reasonable thing is may vary somewhat. Typically, in an interactive program, invoking `abort` should return the user to top level, though in some batch or multi-processing situations killing the running process might be more appropriate.

[Macro]

    restart-case expression {(case-name arglist
                               {keyword value}*
                               {form}*)}*

The expression is evaluated in a dynamic context where the clauses have special meanings as points to which control may be transferred. If the expression finishes executing and returns any values, all such values are simply returned by the `restart-case` form. While the expression is running, any code may transfer control to one of the clauses (see `invoke-restart`). If a transfer occurs, the forms in the body of that clause will be evaluated and any values returned by the last such form will be returned by the `restart-case` form.

As a special case, if the expression is a list whose car is `signal`, `error`, `cerror`, or `warn`, then `with-condition-restarts` is implicitly used to associate the restarts with the condition to be signaled. For example,

    (restart-case (signal weird-error)
      (become-confused ...)
      (rewind-line-printer ...)
      (halt-and-catch-fire ...))

is equivalent to

    (restart-case (with-condition-restarts
                    weird-error
                    (list (find-restart 'become-confused)
                          (find-restart 'rewind-line-printer)
                          (find-restart 'halt-and-catch-fire))
                    (signal weird-error))
      (become-confused ...)
      (rewind-line-printer ...)
      (halt-and-catch-fire ...))

If there are no forms in a selected clause, `restart-case` returns `nil`.

The case-name may be `nil` or a symbol naming this restart.

It is possible to have more than one clause use the same case-name. In this case, the first clause with that name will be found by `find-restart`. The other clauses are accessible using `compute-restarts`. [In this respect, `restart-case` is rather different from `case`!-GLS]

Each arglist is a normal lambda-list containing parameters to be bound during the execution of its corresponding forms. These parameters are used to pass any necessary data from a call to `invoke-restart` to the `restart-case` clause.

By default, `invoke-restart-interactively` will pass no arguments and all parameters must be optional in order to accommodate interactive restarting. However, the parameters need not be optional if the `:interactive` keyword has been used to inform `invoke-restart-interactively` about how to compute a proper argument list.

The valid keyword value pairs are the following:

`:test fn`

  The fn must be a suitable argument for the `function` special form. The expression `(function fn)` will be evaluated in the current lexical environment. It should produce a function of one argument, a condition. If this function returns `nil` when given some condition, functions such as `find-restart`, `compute-restart`, and `invoke-restart` will not consider this restart when searching for restarts associated with that condition. If this pair is not supplied, it is as if

      (lambda (c) (declare (ignore c)) t)

  were used for the fn.

`:interactive fn`

  The fn must be a suitable argument for the `function` special form. The expression `(function fn)` will be evaluated in the current lexical environment. It should produce a function of no arguments that returns arguments to be used by `invoke-restart-interactively` when invoking this function. This function will be called in the dynamic environment available prior to any restart attempt. It may interact with the user on the stream in `*query-io*`.

  If a restart is invoked interactively but no `:interactive` option was supplied, the argument list used in the invocation is the empty list.

`:report exp`

  If exp is not a literal string, it must be a suitable argument to the `function` special form. The expression `(function exp)` will be evaluated in the current lexical environment. It should produce a function of one argument, a stream, that prints on the stream a description of the restart. This function is called whenever the restart is printed while `*print-escape*` is `nil`.

  If exp is a literal string, it is shorthand for

      (lambda (s) (write-string exp s))

  [That is, a function is provided that will simply write the given string literally to the stream.-GLS]

  If a named restart is asked to report but no report information has been supplied, the name of the restart is used in generating default report text.

  When `*print-escape*` is `nil`, the printer will use the report information for a restart. For example, a debugger might announce the action of typing ```:continue`'' by executing the equivalent of

      (format *debug-io* "~&~S - ~A~%" ':continue some-restart)

  which might then display as something like

      :CONTINUE - Return to command level.

  It is an error if an unnamed restart is used and no report information is provided.

Rationale: Unnamed restarts are required to have report information on the grounds that they are generally only useful interactively, and an interactive option that has no description is of little value.

Implementation note: Implementations are encouraged to warn about this error at compilation time.

At run time, this error might be noticed when entering the debugger. Since signaling an error would probably cause recursive entry into the debugger (causing yet another recursive error, and so on), it is suggested that the debugger print some indication of such problems when they occur, but not actually signal errors.

Note that

    (restart-case expression
      (name1 arglist1 options1 . body1)
      (name2 arglist2 options2 . body2)
      ...)

is essentially equivalent to

    (block #1=#:block-1
      (let ((#2=#:var-2 nil))
        (tagbody
          (restart-bind ((name1 #'(lambda (&rest temp)
    		    (setq #2# temp)
    		    (go #3=#:tag-3))
    		<slightly transformed options1>)
                         (name2 #'(lambda (&rest temp)
    		    (setq #2# temp)
    		    (go #4=#:tag-4))
    		<slightly transformed options2>)
                         ...)
            (return-from #1# expression))
          #3# (return-from #1#
                    (apply #'(lambda arglist1 . body1) #2#))
          #4# (return-from #1#
                    (apply #'(lambda arglist2 . body2) #2#))
          ...)))

[Note the use of ``gensyms'' such as `#:block-1` as block names, variables, and `tagbody` tags in this example, and the use of `#n=` and `#n#` read-macro syntax to indicate that the very same gensym appears in multiple places.-GLS]

Here are some examples of the use of `restart-case`.

    (loop
      (restart-case (return (apply function some-args))
        (new-function (new-function)
            :report "Use a different function."
            :interactive
              (lambda ()
                (list (prompt-for 'function "Function: ")))
          (setq function new-function))))

    (loop
      (restart-case (return (apply function some-args))
        (nil (new-function)
            :report "Use a different function."
            :interactive
              (lambda ()
                (list (prompt-for 'function "Function: ")))
          (setq function new-function))))

    (restart-case (a-command-loop)
      (return-from-command-level ()
          :report
            (lambda (s)     ;Argument s is a stream
              (format s "Return from command level ~D." level))
        nil))

    (loop
      (restart-case (another-random-computation)
        (continue () nil)))

The first and second examples are equivalent from the point of view of someone using the interactive debugger, but they differ in one important aspect for non-interactive handling. If a handler ``knows about'' named restarts, as in, for example,

    (when (find-restart 'new-function)
      (invoke-restart 'new-function the-replacement))

then only the first example, and not the second, will have control transferred to its correction clause, since only the first example uses a restart named `new-function`.

Here is a more complete example:

    (let ((my-food 'milk)
          (my-color 'greenish-blue))
      (do ()
          ((not (bad-food-color-p my-food my-color)))
        (restart-case (error 'bad-food-color
                             :food my-food :color my-color)
          (use-food (new-food)
              :report "Use another food."
            (setq my-food new-food))
          (use-color (new-color)
              :report "Use another color."
            (setq my-color new-color))))
      ;; We won't get to here until MY-FOOD
      ;; and MY-COLOR are compatible.
      (list my-food my-color))

Assuming that `use-food` and `use-color` have been defined as

    (defun use-food (new-food)
      (invoke-restart 'use-food new-food))

    (defun use-color (new-color)
      (invoke-restart 'use-color new-color))

a handler can then restart from the error in either of two ways. It may correct the color or correct the food. For example:

    #'(lambda (c) ... (use-color 'white) ...)   ;Corrects color

    #'(lambda (c) ... (use-food 'cheese) ...)   ;Corrects food

Here is an example using `handler-bind` and `restart-case` that refers to a condition type `foo-error`, presumably defined elsewhere:

    (handler-bind ((foo-error #'(lambda (ignore) (use-value 7))))
      (restart-case (error 'foo-error)
        (use-value (x) (* x x))))
     => 49

[Macro]
`restart-bind ({(name function {keyword value}*)}*) {form}*`

Executes a body of forms in a dynamic context where the given restart bindings are in effect.

Each name may be `nil` to indicate an anonymous restart, or some other symbol to indicate a named restart.

Each function is a form that should evaluate to a function to be used to perform the restart. If invoked, this function may either perform a non-local transfer of control or it may return normally. The function may take whatever arguments the programmer feels are appropriate; it will be invoked only if `invoke-restart` is used from a program, or if a user interactively asks the debugger to invoke it. In the case of interactive invocation, the `:interactive-function` option is used.

The valid keyword value pairs are as follows:

`:test-function form`

  The form will be evaluated in the current lexical environment and should return a function of one argument, a condition. If this function returns `nil` when given some condition, functions such as `find-restart`, `compute-restart`, and `invoke-restart` will not consider this restart when searching for restarts associated with that condition. If this pair is not supplied, it is as if

      #'(lambda (c) (declare (ignore c)) t)

  were used for the form.

`:interactive-function form`

  The form will be evaluated in the current lexical environment and should return a function of no arguments that constructs a list of arguments to be used by `invoke-restart-interactively` when invoking this restart. The function may prompt interactively using `*query-io*` if necessary.

`:report-function form`

  The form will be evaluated in the current lexical environment and should return a function of one argument, a stream, that prints on the stream a summary of the action this restart will take. This function is called whenever the restart is printed while `*print-escape*` is `nil`.

[Macro]

    with-condition-restarts condition-form restarts-form
          {declaration}* {form}*

The value of condition-form should be a condition C and the value of restarts-form should be a list of restarts `(R1 R2 ...)`. The forms of the body are evaluated as an implicit `progn`. While in the dynamic context of the body, an attempt to find a restart associated with a particular condition C' will consider the restarts R1, R2, ... if C' is `eq` to C.

Usually this macro is not used explicitly in code, because `restart-case` handles most of the common uses in a way that is syntactically more concise.

[The X3J13 vote (CONDITION-RESTARTS) left it unclear whether `with-condition-restarts` permits declarations to appear at the heads of its body. I believe that was the intent, but this is only my interpretation.-GLS]

Next: Finding and Manipulating Up: Program Interface to Previous: Creating Conditions