Common lisp restart to condition binding
common-lisp, error-handling
Solution
Maybe this helps:
(define-condition low-level-error (simple-error)
()
(:report (lambda (c s)
(format s "low level error."))))
(define-condition high-level-error (simple-error)
()
(:report (lambda (c s)
(format s "high level error."))))
(defun low-level (errorp)
(restart-case
(when errorp (error 'low-level-error))
(go-on ()
:report "go on from low-level"
t)))
(defun high-level (high-level-error-p low-level-error-p)
(restart-case
(progn
(when high-level-error-p (error 'high-level-error))
(low-level low-level-error-p))
(go-on ()
:report "go on from high level"
:test (lambda (c) (typep c 'high-level-error))
t)))
Try invoking `high-level` with different values (`t` or `nil`) for its arguments and check in the debugger if the respective available restarts fit your needs. The high level restart will only be seen if a high level error is signalled, and since the restart for the higher level is kept up the stack, the lower level function won't have to know about high level means to recover.
For your particular use-case, if I understand you correctly, this would mean: Establish your `recreate-command-message` restart to re-invoke `process-message` in `send-command`, and make it only available for high level errors.
As you probably know after reading the PCL chapter Vsevolod linked above, actually handling those errors, i.e. deciding which restarts to invoke, is done with `handler-bind` and `handler-case`.
Problem
I'm learning common lisp in my free time and have a questions about the condition system. When we handle an error in common lisp we specify error-type in a handler to determine which error to handle. Between raising and handling an error I can place some restarts (for example with restart-case) but I cannot specify in restart an error type. For example, assume I have a function that takes a string and a stream, sends string to stream and read the response from stream and returns it. Assume that if my message is wrong I read from stream an error response. And I want to raise an error and bind a restart that asks for new message like this: ``` (defun process-message (stream raw-message) (let ((response (get-response stream raw-message))) (restart-case (when (response-error-p response) (error 'message-error :text response)) (change-raw-message (msg) (process-message stream msg))))) ``` Now assume that the message is complicated and I got another function `send-command` at higher level that can create a message from some arguments and calls the process-message. I want to bind another restart `recreate-command-message` that will allow user to send new command from arguments if `'message-error` acquires. This restart could be places in restart-case at `process-message`, but it is not fully correct because `process-message` should not know about such high-level function like `send-command` and the return values can differ. But now the stream errors (such as EOF etc.) will be thrown throw `recreate-command-message` and if socket will fail the `recreate-command-message` restart will be available in some super-high-level `socket-error` handler and this restart will be useless and idiomatically wrong. Is this a program design problem and a program should be designed to avoid such problems, or I just cannot find how to bind restart to error type or I do not understand the condition system correctly? Thanks.