restart-bind
restart-bind Macro
Syntax:
restart-bind ({(name function {↓key-val-pair}*)}) {form}*
→ {result}*
key-val-pair::=:interactive-function interactive-function |
:report-function report-function |
:test-function test-function
Arguments and Values:
name—a symbol; not evaluated.
function—a form; evaluated.
forms—an implicit progn.
interactive-function—a form; evaluated.
report-function—a form; evaluated.
test-function—a form; evaluated.
results—the values returned by the forms.
Description:
restart-bind executes the body of forms in a dynamic environment where restarts with the given names are in effect.
If a name is nil, it indicates an anonymous restart; if a name is a non-nil symbol, it indicates a named restart.
restart-bindThe function, interactive-function, and report-function are unconditionally evaluated in the current lexical and dynamic environment prior to evaluation of the body. Each of these forms must evaluate to a function.
If invoke-restart is done on that restart, the function which resulted from evaluating function is called, in the dynamic environment of the invoke-restart, with the arguments given to invoke-restart. The function may either perform a non-local transfer of control or may return normally.
If the restart is invoked interactively from the debugger (using invoke-restart-interactively), the arguments are defaulted by calling the function which resulted from evaluating interactive-function. That function may optionally prompt interactively on query I/O, and should return a list of arguments to be used by invoke-restart-interactively when invoking the restart.
If a restart is invoked interactively but no interactive-function is used, then an argument list of nil is used. In that case, the function must be compatible with an empty argument list.
If the restart is presented interactively (e.g., by the debugger), the presentation is done by calling the function which resulted from evaluating report-function. This function must be a function of one argument, a stream. It is expected to print a description of the action that the restart takes to that stream. This function is called any time the restart is printed while *print-escape* is nil.
In the case of interactive invocation, the result is dependent on the value of :interactive-function as follows.
:interactive-function
Value is evaluated in the current lexical environment and should return a function of no arguments which constructs a list of arguments to be used by invoke-restart-interactively when invoking this restart. The function may prompt interactively using query I/O if necessary.
:report-function
Value is evaluated in the current lexical environment and should return a function of one argument, a stream, which prints on the stream a summary of the action that this restart takes. This function is called whenever the restart is reported (printed while *print-escape* is nil). If no :report-function option is provided, the manner in which the restart is reported is implementation-dependent.
:test-function
Value is evaluated in the current lexical environment and should return a function of one argument, a condition, which returns true if the restart is to be considered visible.
Affected By:
*query-io*.
See Also:
restart-case, with-simple-restart
Notes:
restart-bind is primarily intended to be used to implement restart-case and might be useful in implementing other macros. Programmers who are uncertain about whether to use restart-case or restart-bind should prefer restart-case for the cases where it is powerful enough, using restart-bind only in cases where its full generality is really needed.
Expanded Reference: restart-bind
Basic Restart Binding
restart-bind establishes restarts in a dynamic environment. Unlike restart-case, the restart function is called in the dynamic environment of the invoke-restart, not after unwinding the stack.
(restart-bind ((my-restart (lambda ()
:restarted)
:report-function (lambda (stream)
(write-string "Invoke my-restart" stream))))
(invoke-restart 'my-restart))
=> :RESTARTED
Restart with Arguments
The restart function receives whatever arguments are passed to invoke-restart.
(restart-bind ((use-value (lambda (v) v)
:report-function (lambda (stream)
(write-string "Use a provided value" stream))))
(invoke-restart 'use-value 42))
=> 42
Restart Without Unwinding
A key difference from restart-case: the restart function in restart-bind executes without unwinding the stack. This means the restart can inspect the dynamic environment at the point of the error.
(let ((log '()))
(restart-bind ((log-and-continue
(lambda ()
(push :restart-invoked log))
:report-function (lambda (s)
(write-string "Log and continue" s))))
(push :before log)
(invoke-restart 'log-and-continue)
(push :after log))
(nreverse log))
=> (:BEFORE :RESTART-INVOKED :AFTER)
Using restart-bind with handler-bind
restart-bind and handler-bind work well together for advanced condition handling without stack unwinding.
(block done
(handler-bind ((error (lambda (c)
(declare (ignore c))
(invoke-restart 'recover))))
(restart-bind ((recover (lambda ()
(return-from done :recovered))
:report-function (lambda (s)
(write-string "Recover from the error" s))))
(error "Something failed"))))
=> :RECOVERED
Comparing restart-bind and restart-case
Most programs should prefer restart-case for its simpler syntax. Use restart-bind when you need the restart function to execute without unwinding, or when you need full control over the restart function as a closure.
;; restart-case unwinds before executing the clause body
(handler-bind ((error (lambda (c)
(declare (ignore c))
(invoke-restart 'fix-it))))
(restart-case (error "problem")
(fix-it () :fixed)))
=> :FIXED