我在空闲时间学习普通的 lisp,并对条件系统有疑问。
当我们在 common lisp 中处理错误时,我们在处理程序中指定 error-type 以确定要处理的错误。在引发和处理错误之间,我可以进行一些重启(例如使用重启案例),但我无法在重启中指定错误类型。
例如,假设我有一个函数,它接受一个字符串和一个流,将字符串发送到流并从流中读取响应并返回它。假设如果我的消息是错误的,我会从流中读取错误响应。我想引发一个错误并绑定(bind)一个重新启动,要求提供新消息,如下所示:
(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)))))
现在假设消息很复杂,并且我在更高级别获得了另一个函数
send-command
,它可以从一些参数创建消息并调用 process-message。我想绑定(bind)另一个重新启动 recreate-command-message
,如果 'message-error
获取,它将允许用户从参数发送新命令。此重启可能位于 process-message
处的 restart-case 中,但它并不完全正确,因为 process-message
不应该知道像 send-command
这样的高级函数,并且返回值可能不同。但是现在流错误(例如 EOF 等)将被抛出 throw
recreate-command-message
并且如果 socket 失败,recreate-command-message
重启将在某些超高级别的 socket-error
处理程序中可用,并且这种重启将是无用的并且惯用错误。这是一个程序设计问题,应该设计一个程序来避免此类问题,或者我只是找不到如何将重新启动绑定(bind)到错误类型或者我不正确理解条件系统?
谢谢。
最佳答案
也许这有帮助:
(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)))
尝试使用不同的值(
high-level
或 t
)为其参数调用 nil
,并检查调试器中相应的可用重启是否满足您的需要。只有在发出高级别错误信号时才会看到高级别重启,并且由于更高级别的重启保持在堆栈中,低级别函数将不必知道高级别方法来恢复。对于您的特定用例,如果我理解正确,这将意味着:建立您的
recreate-command-message
重新启动以重新调用 process-message
中的 send-command
,并使其仅可用于高级错误。正如您在阅读上面链接的 PCL 章节 Vsevolod 后可能知道的那样,实际处理这些错误,即决定调用哪个重新启动,是通过
handler-bind
和 handler-case
完成的。关于error-handling - 常见的 lisp 重启到条件绑定(bind),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/15470619/