有异常层次结构的理论吗?


18

我熟悉许多编程语言,它们以某种方式具有异常,但是我亲眼目睹了两种“病理性”趋势。

  1. 似乎没有常见的异常模式或层次结构。每种语言基本上都会发布自己的版本,如果例外将其纳入标准,则人们在标准中发现的例外种类将相当随意(大多数是在创建语言工具时实现的,例如从中读取源代码)字符串或调用调试器的异常,或找不到文件时发生的异常等)

  2. 用这种语言定义的异常很少被用户程序重用。通常会有一个或两个流行的例外(例如,“未实现”)。尽管大多数时候程序员会创建自己的异常。(例如,将其与创建新的数字类型或新的集合类型进行比较)。

这对我来说似乎是一个可怕的遗漏。没人知道用户程序中将需要哪种错误?我希望有一种很好的层次结构,类似于数字类型,集合,对象系统等。

更糟糕的是,Goolge和Wikipedia在此问题上提供的帮助很少。到目前为止,我只找到了一篇关于函数异常的论文,该论文的开头是一段:

本文认为,惰性函数式编程不仅不需要内置的异常处理机制,而且还为开发和转换使用异常的程序提供了强大的工具。

(异常的功能理论,Mike Spivey,1988年)

但是我认为例外是好的。我不想转换使用异常的程序,相反,我想减少异常的使用。

问题:

有例外理论吗?如果是这样,它叫什么?概述其基础的基石是什么(如果有)?


例外是一个相当新的发明,可能不到20年,是C的“ longjmp”引起的。它们主要连接到OOP。似乎理想的用法/理论/最佳实践仍在发展中。Java具有更复杂的模型之一。如您所述,有许多与例外相关的“反模式”。其中一些与“容错计算”理论有关,该理论总体上还处于初期。
vzn13年

您可以将例外视为延续理论的子集。参见en.wikipedia.org/wiki/Continuation
jmite

@jmite异常和延续非常不同。异常动态绑定到其处理程序,而延续静态绑定。通常,至少在类型存在的情况下,不能将延续单独使用来实现异常,例如,请参见类型化异常和延续不能相互宏表达
马丁·伯杰

“由语言定义的异常很少被用户程序重用。” 这是真的!很少需要定义自定义异常。例如python及其stdlib定义了160种异常。没有定义您正在考虑的异常的机会很小。这些异常中的某些(大多数?)不是很广为人知。例如,LookupError对于每个自定义容器来说都可以,但是我很多人都不知道它的存在。
2013年

1
@jmite在本主题之前我遇到的另一个例外是本杰明·皮尔斯(Benjamin C. Pierce)的《类型和编程语言》(Types and Programming Languages)。他在定义函数类型的上下文中提到错误的地方。从他的角度来看,错误只是函数返回的另一个值(如果允许的话,它们与另一个参数一起构成一个完整的类型)。
wvxvw

Answers:


8

关于异常的出版物很多,其中有大量的理论研究。这是一个带有一些示例的非结构化列表,与完整列表相去甚远。抱歉,我现在没有时间集中讨论。

  • B. Randell,软件容错的系统结构。
  • JB Goodenough。异常处理:问题和建议的符号。
  • JB Goodenough。结构化异常处理。
  • BG Ryder,ML Soffa,对异常处理设计的影响。
  • D. Teller,A。Spiwack,T。Varoquaux,如果可以,请联系我:在OCaml中实现类型安全,分层,轻量,多态和有效的错误管理。
  • X. Leroy,F。Pessaux,基于类型的未捕获异常分析。
  • R. Miller,A。Tripathi,面向对象系统中的异常处理问题。
  • S. Drew,KJ Gough,J。Ledermann,实现零开销开销异常处理。
  • B. Stroustrup,异常安全性:概念和技术。
  • D. Malayeri,J。Aldrich,实用异常规范。
  • H. Nakano,抓举机制的建设性形式化。
  • A. Nanevski,异常处理的模态演算。
  • P. de Groote,一个简单的异常处理演算。
  • H. Thielecke,关于国家存在下的例外与延续。
  • JG Riecke,H。Thielecke,类型化的异常和连续体不能彼此进行宏表达。
  • M. van Dooren和E. Steegmans,使用锚定异常声明将检查异常的鲁棒性与未检查异常的灵活性相结合。
  • JA Vaughan,对Java样式异常的逻辑解释。
  • S. Marlow,S。Peyton Jones,A。Moran,《 Haskell中的异步异常》。
  • B. Jacobs,F。Piessens,故障框:异常安全的异常处理。

哇,非常感谢!我要花几个月的时间(如果不是更多的话)才能得到肯定的答复:)现在,我被几本不知道从哪里开始的书所困扰!
wvxvw

2
这些论文中有很多是关于以编程语言实现或建模异常的,而不是关于如何设计异常层次结构的。您可以将清单缩小为相关文件吗?
吉尔斯(Gillles)“所以-别再作恶了”

@吉尔斯最初的问题还不清楚。我认为什么才是适当的例外主要取决于应用程序。唯一真正的关于异常的理论问题是(1)通过异常耦合不相关的模块(这就是Java之后没有语言具有强制性异常规范的原因)之间的折衷;(2)向模块的用户提供一些指示,以期望出现哪种错误和(3)编译器帮助进行错误处理。据我所知,尚未找到真正令人信服的解决方案。
Martin Berger 2013年

6

我不知道是否有一种理论,但是可能有一种新兴的实用实验科学。

我能想到的最好的资料是Bjarne Stroustrup,《 C ++的设计和演进》,Addison-Wesley,1994年。如果我没记错的话(这是一本非常好的书,人们一直向我借书而不退还,所以目前我没有副本),其中有一章是关于例外的。Stroustrup领导下的C ++委员会需要大量的经验证据,证明提议的功能是必要的,然后他们才愿意将其添加到语言定义中。该有关异常维基百科页面有来自那本书以下报价:

在1991年11月的Palo Alto [C ++标准化]会议上,我们听取了关于终止语义学论点的精彩摘要,这些论据以个人经验和吉姆·米切尔(来自Sun,原为施乐PARC)的数据为后盾。Jim在20年的时间里使用了六种语言的异常处理,并且是Xerox Cedar / Mesa系统的主要设计者和实现者之一,是恢复语义的早期拥护者。他的信息是终止比恢复更可取;这不是意见问题,而是多年经验的问题。恢复是诱人的,但无效。他以来自多个操作系统的经验来支持此声明。关键示例是Cedar / Mesa:它是由喜欢并使用恢复功能的人编写的,但是使用十年后,50万行系统中只剩下一种恢复使用的方法,那就是上下文查询。由于恢复实际上不是进行此类上下文查询所必需的,因此他们将其删除,发现该系统部分的速度显着提高。在过去的十年中,在每一种使用恢复的情况下,它都成为一个问题,更合适的设计取代了它。基本上,每次使用恢复都表示未能保持单独的抽象层次不相交。在过去的十年中,在每一种使用恢复的情况下,它都成为一个问题,更合适的设计取代了它。基本上,每次使用恢复都表示未能保持单独的抽象层次不相交。在过去的十年中,在每一种使用恢复的情况下,它都成为一个问题,更合适的设计取代了它。基本上,每次使用恢复都表示未能保持单独的抽象层次不相交。

在C ++中,真正的胜利是RAII,它使出错期间处理资源的重新分配变得更加容易。(它不废除需要throwtry- catch,但它意味着你不需要finally。)

我认为说服他们需要异常的是通用容器:容器编写者对所包含的对象可能需要返回的错误种类一无所知(更不用说如何处理它们了),但是将这些对象插入到对象中的代码却一无所知。容器必须了解那些对象的接口是什么。但是,由于我们不知道所包含的对象会抛出哪种错误,因此无法对异常类型进行标准化。(相反:如果我们可以标准化异常类型,那么我们就不需要异常。)

人们多年来学到的另一件事是,异常规范很难正确地用语言表达。例如,请参见:http : //www.gotw.ca/publications/mill22.htmhttp://www.gotw.ca/gotw/082.htm。(不仅是C ++,Java程序员对于使用受检查的异常还是未经检查的异常也有很长的争论。)

关于例外的历史。经典论文是:John B. Goodenough:“异常处理:问题和建议的符号”,Commun。ACM 18(12):683-696,1975年。但是在此之前,例外是已知的。Mesa在1974年左右拥有了它们,PL / I也许也有它们。阿达有一个异常机制1980年之前,我认为C ++的例外情况,大多数与芭芭拉·利斯科夫的CLU编程语言的经验,从大约1976年的影响 在‘CLU的历史’:芭芭拉Liskov的历史编程语言--- II,托马斯J. Bergin,Jr.和Richard G. Gibson,Jr.(编辑)。pp.471-510,ACM,1996


这很有趣,我将需要进行更多的研究才能更好地进行答复。但是到目前为止:我知道在C ++中使用异常是非常强烈的反对(也许是轶事,但是iirc Google编码约定曾经禁止使用异常)。Java检查的异常当然是一个独特且有趣的实验,但是该功能在其历史沿革中赢得了许多坏信誉...大多数人在运行时就将它们重新抛出(尽管这可能仅与语法有关)。
wvxvw

我对Common Lisp异常的分类更为熟悉,在这种分类中,他们试图(尽管收效甚微)根据它们对程序构成的威胁级别进行划分。例如,serious-conditionVS simple-condition。我现在也正在阅读JL Austing,他在其中将错误(与编程无关)根据系统无法执行任务的方式(例如,使用的零件不正确与意图不当)进行分组。这并不立即适用于编程,但可能需要进行一些改进。
wvxvw

@Wandering Logic我之所以大吃一惊,是因为您已经解释了为什么C ++附加语是sux,而受过良好教育的功能集成可能会破坏该语言。
2013年

@wvxvw very strong objectionC ++中的抵制异常有两个事实:没有finally构造,没有其他人使用异常。第一个问题也加剧了第二个问题。也就是说,如果没有finally,则在发生异常时无法关闭资源。因为没有人使用异常,所以所有函数/ API都会避免使用它们,因此您必须投入大量资金来重建整个传统的C ++基础结构,将所有函数与异常包装在一起,以开始从代码中受益。但是缺乏缺乏也finally使这种方法成为不可能。
2013年

@wvxvw:Google的约定禁止跨模块(.so)边界引发异常。这是因为C ++中的异常使用运行时类型信息(RTTI),而Linux在实现运行时类型方面做得不好。在Linux中,如果您使用相同编译器的相同版本并针对相同版本的libstdc ++进行链接,则只能在模块之间可靠地传递运行时类型。实际上,这通常是Linux社区对C ++的拒绝,而不是专门针对异常的拒绝。
逻辑徘徊

3

让我只指出异常是一种计算效果的情况。其他计算影响是可变状态,I / O,不确定性,连续性以及许多其他影响。因此,您的问题可能会更笼统地问:我们如何形成计算效果的层次结构,如何组织它们,以及为什么我们拥有我们拥有的而不是其他,等等。


1
我认为这完全无关紧要。问题不是关于异常概念的建模,而是关于将异常映射到错误的问题-我认为从PLT角度描述异常的正确方法将是异常层次结构的理论。
吉尔(Gilles)'所以

嗯,你是对的。我已确定答案以指出这一点,但我认为无需删除它。你怎么看?
安德烈·鲍尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.