3

我在空闲时间学习 common lisp,并且对条件系统有疑问。

当我们在 common lisp 中处理错误时,我们在处理程序中指定错误类型以确定要处理的错误。在引发和处理错误之间,我可以进行一些重新启动(例如使用重新启动案例),但我无法在重新启动时指定错误类型。

例如,假设我有一个函数,它接受一个字符串和一个流,将字符串发送到流并从流中读取响应并返回它。假设如果我的消息是错误的,我会从流中读取错误响应。我想提出一个错误并绑定一个重新启动,要求这样的新消息:

(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。我想绑定另一个重新启动,以允许用户在获取recreate-command-message时从参数发送新命令。'message-error这种重新启动可以放在 restart-case at 中process-message,但它并不完全正确,因为process-message不应该知道这样的高级函数send-command,并且返回值可能不同。

但是现在将抛出流错误(例如 EOF 等)recreate-command-message,如果套接字失败,则recreate-command-message重新启动将在某些超高级socket-error处理程序中可用,并且这种重新启动将是无用的并且是惯用的错误。

这是一个程序设计问题,应该设计一个程序来避免此类问题,或者我只是找不到如何将重新启动绑定到错误类型,或者我不正确理解条件系统?

谢谢。

4

1 回答 1

8

也许这有帮助:

(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-messagein send-command,并使其仅可用于高级错误。

正如您在阅读上面链接的 PCL 章节 Vsevolod 后可能知道的那样,实际处理这些错误,即决定调用哪个重新启动,是使用handler-bindand完成的handler-case

于 2013-03-18T09:55:16.363 回答