"错误/异常"和"抛出/捕获"之间的区别是什么?"

4

我对Rebol中的错误处理有些困惑。它有THROW和CATCH结构:

>> repeat x 10 [if x = 9 [throw "Nine!"]]
** Throw error: no catch for throw: make error! 2

>> catch [repeat x 10 [if x = 9 [throw "Nine!"]]]
== "Nine!"

但是这种抛出和捕获与传递给TRY中/EXCEPT修饰符的处理程序无关:
>> try/except [throw "Nine!"] [print "Exception handled!"]
** Throw error: no catch for throw: make error! 2

这与Rebol的错误类型非常相关:

>> try/except [print 1 / 0] [print "Error handled!"]
Error handled!

如果你想触发自己的错误,但不要像其他语言中那样使用THROW。抛出错误只会导致未被捕获的投诉,就像任何其他值类型一样:

>> try/except [throw make error! "Error string"] [print "Error handled!"]
** Throw error: no catch for throw: make error! 2

为了让评估器尝试执行类型错误的内容,您必须执行它们!以引起其认为的"异常"

>> try/except [do make error! "Error string"] [print "Error handled!"]
Error handled!

(注意:您可以使用预制错误,例如cause-error 'Script 'invalid-type function! - 有关更多信息,请参见system/catalog/errors。)
省略/EXCEPT细化将使您接收任何错误作为值。 但是,这似乎使得无法区分错误是否被调用:
>> probe try [do make error! "Some error"]
make error! [
    code: 800
    type: 'User
    id: 'message
    arg1: "some error"
    arg2: none
    arg3: none
    near: none
    where: none
]
** User error: "Some error"

>> probe try [make error! "Some error"]
make error! [
    code: 800
    type: 'User
    id: 'message
    arg1: "Some error"
    arg2: none
    arg3: none
    near: none
    where: none
]
** User error: "Some error"

似乎CATCH也没有区分返回值和抛出异常的差别。但有一个工具可以通过“命名异常”来解决这个问题。
>> code: [repeat x 10 [if x = 9 [throw/name "Nine!" 'number]]]

>> catch/name [do code] 'number
== "Nine!"

>> catch/name [do code] 'somethingelse
** Throw error: no catch for throw: make error! 2

现在是问题时间:

  • 这种区分有实际价值吗?如何决定使用THROW和CATCH还是使用错误的DO并用TRY/EXCEPT处理它?

  • 这种区分在其他语言中是否有先例,/EXCEPT是否更好地命名为/ON-ERROR或其他名称?

  • 为什么它说“没有捕获throw:make error!2”而不是更多信息?“make error! 2”是什么意思?

3个回答

5
“这种分离有实际价值吗?如何决定使用THROW和CATCH还是使用错误的DO并使用TRY / EXCEPT处理它?”正如其他答案所指出的那样,THROW和CATCH形成了一个展开结构(非本地退出),它本身关注控制流,并不一定与错误处理有任何关系。 THROW和CATCH首先是影响控制流的简单方法,是其他自定义控制流结构的基本构建块。因此,当然也可以使用THROW和CATCH来构建类似于许多现代主流语言中看到的“异常处理”式错误系统,因为这些“异常处理”系统是非本地控制流的一个实例。另一方面,Rebol的error!是信号和传播评估错误的主要方法。因此,通常决策很容易做出:如果您想引发错误,请使用error!。如果要影响控制流以可控方式展开,则使用THROW / CATCH。关于术语的两个更多的备注:
  • 讨论Rebol错误时,变得更加小心使用“cause an error”作为短语而不是“throw an error”,以避免混淆。
  • 类似地,调用THROW / CATCH的参考文献“Rebol的异常处理”(如@HostileFork在一条评论中所暗示的)需要重新制定。

“这种区别在其他语言中有先例吗?”

是的,评估错误和非本地退出之间的区别在其他语言中有先例,特别是Lisp系列。一些快速参考:

“为什么它显示“没有捕获可抛出的异常:make error!2”,而不是更多有信息的内容?什么是“make error!2”?”
这是一个bug。 (很好发现!)我会说,错误消息的核心(“没有捕获可抛出的异常”)已经相当具有信息性了,但是“make error!2”是一个bug(应该在此处显示抛出的值)。
“/EXCEPT”是否更名为“/ON-ERROR”或其他名称会更好?”
更改“/EXCEPT”的名称存在争议。因此,我认为这是一个不太适合SO Q&A的讨论,最好留给其他论坛。

2
我认为常规抛出并不是用于错误处理,而是用于快捷方式或跳转。它充当循环中的“break”等。您也可以使用它模拟“continue”。
参见 是否有一个与continue相当的东西

我认为一个合理的异常处理定义是:“当成员未能按其名称指示完成其应执行的任务时,就会出现异常。”根据这个定义,我会说需要另一种类别来处理THROW/CATCH;也许可以称之为“Rebol的解除操作”与“Rebol的异常处理”… - HostileFork says dont trust SE

2
我对Rebol中的错误处理不是很熟悉,但关于异常和抛出和捕获的观点是它们经常被认为是“错误处理”结构,这是一个相当狭隘的概念。相反,最好将try/catch视为流程控制机制。但是,退出循环只会退出一个直接包围块,从抛出异常中获得的好处是它可以跨越堆栈中的多个帧。因此,您可以更轻松地将单个代码块隔离为逻辑单元,如果其中任何一部分失败,则可以将其视为逻辑单元的失败,而不管异常在堆栈中的深度如何。
抱歉,这可能没有直接回答您的问题,但我想我可以回答您的第二个问题:是否应该称之为ON-ERROR。我会说不,因为异常并不一定意味着错误;作为流程控制的手段,它只是一种更一般的指令,表示发生了不应允许沿特定路径进一步执行的条件。

我在另一条评论中提到我喜欢“异常是指成员未能按其名称指示执行任务”的定义。无论如何,我觉得这里需要澄清术语...因为我一直在做很多throw make error! "...",因为我以为这就是你应该做的,然后自己解析错误对象进行打印和退出...没有意识到我可以处理错误并获得相同的行为。这与其他语言不同,应该更清楚地解释。 - HostileFork says dont trust SE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接