CLHS: Macro RESTART-CASE

#

Macro RESTART-CASE

Syntax:

restart-case restartable-form {clause} => result*

    clause::= (case-name lambda-list
               [[:interactive interactive-expression | :report report-expression | :test test-expression]]
               declaration* form*)

Arguments and Values:

restartable-form---a form.

case-name---a symbol or nil.

lambda-list---an ordinary lambda list.

interactive-expression---a symbol or a lambda expression.

report-expression---a string, a symbol, or a lambda expression.

test-expression---a symbol or a lambda expression.

declaration---a declare expression; not evaluated.

form---a form.

results---the values resulting from the evaluation of restartable-form, or the values returned by the last form executed in a chosen clause, or nil.

Description:

restart-case evaluates restartable-form in a dynamic environment where the clauses have special meanings as points to which control may be transferred. If restartable-form finishes executing and returns any values, all values returned are returned by restart-case and processing has completed. While restartable-form is executing, 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 is evaluated and any values returned by the last such form are returned by restart-case. In this case, the dynamic state is unwound appropriately (so that the restarts established around the restartable-form are no longer active) prior to execution of the clause.

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

If case-name is a symbol, it names 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 is found by find-restart. The other clauses are accessible using compute-restarts.

Each arglist is an ordinary lambda list to be bound during the execution of its corresponding forms. These parameters are used by the restart-case clause to receive any necessary data from a call to invoke-restart.

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

Keyword options have the following meaning.

`:interactive`

  The value supplied by `:interactive` value`` must be a suitable argument to function. `(function` value`)` is evaluated in the current lexical environment. It should return a function of no arguments which returns arguments to be used by invoke-restart-interactively when it is invoked. invoke-restart-interactively is called in the dynamic environment available prior to any restart attempt, and uses query I/O for user interaction.

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

`:report`

  If the value supplied by `:report` value`` is a lambda expression or a symbol, it must be acceptable to function. `(function` value`)` is evaluated in the current lexical environment. It should return a function of one argument, a stream, which prints on the stream a description of the restart. This function is called whenever the restart is printed while *print-escape* is nil.

  If value is a string, it is a shorthand for

       (lambda (stream) (write-string value stream))

  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 uses the report information for a restart. For example, a debugger might announce the action of typing a ``continue'' command by:

       (format t "~&~S -- ~A~%" ':continue some-restart)

  which might then display as something like:

       :CONTINUE -- Return to command level

  The consequences are unspecified if an unnamed restart is specified but no `:report` option is provided.

`:test`

  The value supplied by `:test` value`` must be a suitable argument to function. `(function` value`)` is evaluated in the current lexical environment. It should return a function of one argument, the condition, that returns true if the restart is to be considered visible.

  The default for this option is equivalent to `(lambda (c) (declare (ignore c)) t)`.

If the restartable-form is a list whose car is any of the symbols signal, error, cerror, or warn (or is a macro form which macroexpands into such a list), then with-condition-restarts is used implicitly to associate the indicated restarts with the condition to be signaled.

Examples:

     (restart-case
         (handler-bind ((error #'(lambda (c)
                                 (declare (ignore condition))
                                 (invoke-restart 'my-restart 7))))
           (error "Foo."))
       (my-restart (&optional v) v))
    =>  7

     (define-condition food-error (error) ())
    =>  FOOD-ERROR
     (define-condition bad-tasting-sundae (food-error)
       ((ice-cream :initarg :ice-cream :reader bad-tasting-sundae-ice-cream)
        (sauce :initarg :sauce :reader bad-tasting-sundae-sauce)
        (topping :initarg :topping :reader bad-tasting-sundae-topping))
       (:report (lambda (condition stream)
                  (format stream "Bad tasting sundae with ~S, ~S, and ~S"
                          (bad-tasting-sundae-ice-cream condition)
                          (bad-tasting-sundae-sauce condition)
                          (bad-tasting-sundae-topping condition)))))
    =>  BAD-TASTING-SUNDAE
     (defun all-start-with-same-letter (symbol1 symbol2 symbol3)
       (let ((first-letter (char (symbol-name symbol1) 0)))
         (and (eql first-letter (char (symbol-name symbol2) 0))
              (eql first-letter (char (symbol-name symbol3) 0)))))
    =>  ALL-START-WITH-SAME-LETTER
     (defun read-new-value ()
       (format t "Enter a new value: ")
       (multiple-value-list (eval (read))))
    =>  READ-NEW-VALUE
     (defun verify-or-fix-perfect-sundae (ice-cream sauce topping)
       (do ()
          ((all-start-with-same-letter ice-cream sauce topping))
         (restart-case
           (error 'bad-tasting-sundae
                  :ice-cream ice-cream
                  :sauce sauce
                  :topping topping)
           (use-new-ice-cream (new-ice-cream)
             :report "Use a new ice cream."
             :interactive read-new-value
             (setq ice-cream new-ice-cream))
           (use-new-sauce (new-sauce)
             :report "Use a new sauce."
             :interactive read-new-value
             (setq sauce new-sauce))
           (use-new-topping (new-topping)
             :report "Use a new topping."
             :interactive read-new-value
             (setq topping new-topping))))
       (values ice-cream sauce topping))
    =>  VERIFY-OR-FIX-PERFECT-SUNDAE
     (verify-or-fix-perfect-sundae 'vanilla 'caramel 'cherry)
    >>  Error: Bad tasting sundae with VANILLA, CARAMEL, and CHERRY.
    >>  To continue, type :CONTINUE followed by an option number:
    >>   1: Use a new ice cream.
    >>   2: Use a new sauce.
    >>   3: Use a new topping.
    >>   4: Return to Lisp Toplevel.
    >>  Debug> :continue 1
    >>  Use a new ice cream.
    >>  Enter a new ice cream: 'chocolate
    =>  CHOCOLATE, CARAMEL, CHERRY

Side Effects: None.

Affected By: None.

Exceptional Situations: None.

See Also:

restart-bind, with-simple-restart.

Notes:

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

is essentially equivalent to

     (block #1=#:g0001
       (let ((#2=#:g0002 nil))
            (tagbody
            (restart-bind ((name1 #'(lambda (&rest temp)
                                    (setq #2# temp)
                                    (go #3=#:g0003))
                              ...slightly-transformed-options1...)
                           (name2 #'(lambda (&rest temp)
                                    (setq #2# temp)
                                    (go #4=#:g0004))
                              ...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#)))))

Unnamed restarts are generally only useful interactively and an interactive option which has no description is of little value. Implementations are encouraged to warn if an unnamed restart is used and no report information is provided at compilation time. At runtime, 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, etc.) it is suggested that the debugger print some indication of such problems when they occur but not actually signal errors.

     (restart-case (signal fred)
       (a ...)
       (b ...))
     ==
     (restart-case
         (with-condition-restarts fred
                                  (list (find-restart 'a)
                                        (find-restart 'b))
           (signal fred))
       (a ...)
       (b ...))

The following X3J13 cleanup issues, not part of the specification, apply to this section:

- CONDITION-RESTARTS:PERMIT-ASSOCIATION
- DECLS-AND-DOC

Copyright 1996-2005, LispWorks Ltd. All rights reserved.