我在空闲时间学习常见的口齿不清,并对条件系统有疑问。
当我们处理常见的lisp中的错误时,我们在处理程序中指定错误类型以确定要处理的错误。在引发和处理错误之间,我可以进行一些重启(例如使用restart-case),但我无法在重启时指定错误类型。
例如,假设我有一个带字符串和流的函数,将字符串发送到流并从流中读取响应并返回它。假设如果我的消息错误,我会从流中读取错误响应。我想提出一个错误并绑定一个重新启动,请求这样的新消息:
(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
,可以从某些参数创建消息并调用进程消息。我想绑定另一个重启recreate-command-message
,允许用户在'message-error
获取时从参数发送新命令。此重新启动可能会在process-message
处重新启动,但它不完全正确,因为process-message
不应该知道像send-command
这样的高级函数,并且返回值可能不同。
但是现在流错误(例如EOF等)将抛出recreate-command-message
并且如果套接字失败,recreate-command-message
重启将在某些超级高级{{1}中可用处理程序和这个重启将是无用的和惯用的错误。
这是一个程序设计问题,应该设计一个程序来避免这样的问题,或者我只是找不到如何将重启绑定到错误类型或者我不能正确理解条件系统?
感谢。
答案 0 :(得分:7)
也许这会有所帮助:
(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
作为参数,并检查调试器是否符合您的需要。只有在发出高电平错误信号时才会看到高电平重启,并且由于更高级别的重启保持在堆栈中,所以较低级别的功能不必知道要恢复的高级别方法。 / p>
对于您的特定用例,如果我理解正确,这意味着:建立recreate-command-message
重新启动以重新调用process-message
中的send-command
,并使其仅适用于handler-bind
高级错误。
正如您在阅读上面链接的PCL章节Vsevolod后可能知道的那样,实际处理这些错误,即决定要调用哪些重新启动,是使用handler-case
和{{1}}完成的。