【问题标题】:Common lisp restart to condition binding常见的 lisp 重启到条件绑定
【发布时间】:2013-03-18 06:03:45
【问题描述】:

我在空闲时间学习通用 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,它可以从一些参数创建消息并调用进程消息。我想绑定另一个重启recreate-command-message,如果'message-error 获取,它将允许用户从参数发送新命令。这种重启可以在process-message 的重启案例中进行,但它并不完全正确,因为process-message 不应该知道像send-command 这样的高级函数并且返回值可能不同。

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

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

谢谢。

【问题讨论】:

  • 你应该学习the PCL chapter on restarts - 它会给你所有必要的答案。
  • 本章有一个错误类型和多次重启的示例。这是关于多种错误类型和一次重启。可以稍微修改该章中的示例来解决这个问题。假设 log-analyzer 可以解析多种日志格式。而不是 handler-bind 将有另一个允许指定日志格式的重新启动。现在使用 log-analyzer 的代码将处理来自 with-open-file 的解析错误和文件错误。因此,当我们处理文件错误时,允许我们更改日志文件类型的重新启动将可用,但完全无用,在最坏的情况下有害。
  • 如果我正确理解您想要的内容:restart-case 不会处理错误 - 它会建立重新启动点。错误由handler-bind 处理,您可以在其中指定要处理的错误类型并选择要对其应用的重新启动。所以你的工作就是在你的代码中正确地放置这两种形式。如果您的意思是重新启动将以交互方式可用,如果您不处理错误,那么您是对的。但这真的是个问题吗?
  • 问题(如果可以这样称呼的话)是:我建立了可以从某些特定的不良情况中恢复的重启点。如果发生这种糟糕的情况,我可以解决在处理程序中调用此重新启动的问题(这就是我写重新启动的原因)。如果发生了完全不同的糟糕情况并且通过了另一种错误类型并抛出了重启怎么办?我在另一个处理程序中处理了完全不同的错误,并在列表中获得了重启列表。但是重新启动并不能做任何有用的事情。在最坏的情况下,如果我调用它,这个重启可能会破坏一些进展..

标签: error-handling common-lisp


【解决方案1】:

也许这会有所帮助:

(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)))

尝试使用不同的值(tnil)调用 high-level 的参数,并在调试器中检查相应的可用重启是否符合您的需要。只有在发出高级别错误信号时才会看到高级别重启,并且由于高级别的重启保持在堆栈中,因此低级别函数不必知道高级别的恢复方式。

对于您的特定用例,如果我理解正确,这意味着:建立您的 recreate-command-message 重启以在 send-command 中重新调用 process-message,并使其仅适用于高级错误。

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

【讨论】:

  • 谢谢。重启中的 :test 关键参数就是我想要的。在您的帖子之后,我再次查看了 CLHS 并找到了它,但我之前没有注意到它。谢谢!
猜你喜欢
  • 2016-01-30
  • 1970-01-01
  • 1970-01-01
  • 2012-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-13
相关资源
最近更新 更多