scala 2.8控件异常 - 有什么意义?

oxb*_*kes 21 scala exception-handling

在即将发布的scala 2.8中,util.control添加了一个包,其中包含一个break库和一个用于处理异常的构造,以便代码看起来像:

type NFE = NumberFormatException
val arg = "1"
val maybeInt = try { Some(arg.toInt) } catch { case e: NFE => None }
Run Code Online (Sandbox Code Playgroud)

可以用以下代码替换:

import util.control.Exception._
val maybeInt = catching(classOf[NFE]) opt arg.toInt
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么?除了提供另一种(完全不同的)表达同一事物的方式之外,这又增加了什么呢?是否可以使用新控件表达但不能通过try-catch?它是一个DSL应该在Scala中使异常处理看起来像其他语言(如果是这样,哪一个)?

Dan*_*ral 28

有两种方法可以考虑异常.一种方法是将它们视为流控制:异常会改变程序的执行流程,使执行从一个地方跳到另一个地方.第二种方法是将它们视为数据:例外是关于程序执行的信息,然后可以将其用作程序其他部分的输入.

C++和Java中使用的try/ catch范例是第一种(*).

但是,如果您更喜欢将异常作为数据处理,那么您将不得不求助于显示的代码.对于简单的情况,这很容易.然而,当涉及到构图为王的功能风格时,事情开始变得复杂.您要么全部重复代码,要么滚动自己的库来处理它.

因此,在一种声称支持功能和OO风格的语言中,看到库支持将异常视为数据时,不应感到惊讶.

请注意,Exception处理事物提供了许多其他可能性.例如,您可以使用链式捕获处理程序,就像Lift链的部分功能一样,可以轻松地将责任委托给处理网页请求.

这是可以做什么的一个例子,因为如今自动资源管理很流行:

def arm[T <: java.io.Closeable,R](resource: T)(body: T => R)(handlers: Catch[R]):R = (
  handlers 
  andFinally (ignoring(classOf[Any]) { resource.close() }) 
  apply body(resource)
)
Run Code Online (Sandbox Code Playgroud)

这使您可以安全地关闭资源(注意使用忽略),并且仍然应用您可能想要使用的任何捕获逻辑.

(*)奇怪的是,Forth的异常控制catch&throw,是它们的混合.流程从跳转throwcatch,但随后该信息被视为数据.

编辑

好的,好的,我屈服了.我举个例子.一个例子,就是这样!我希望这不是太做作,但没有办法绕过它.这种东西在大型框架中最有用,而不是小样本.

无论如何,让我们首先定义与资源有关的事情.我决定打印行并返回打印的行数,这里是代码:

def linePrinter(lnr: java.io.LineNumberReader) = arm(lnr) { lnr =>
  var lineNumber = 0
  var lineText = lnr.readLine()
  while (null != lineText) {
    lineNumber += 1
    println("%4d: %s" format (lineNumber, lineText))
    lineText = lnr.readLine()
  }
  lineNumber
} _
Run Code Online (Sandbox Code Playgroud)

这是这个函数的类型:

linePrinter: (lnr: java.io.LineNumberReader)(util.control.Exception.Catch[Int]) => Int
Run Code Online (Sandbox Code Playgroud)

所以,arm收到一个通用的Closeable,但我需要一个LineNumberReader,所以当我调用这个函数时,我需要传递它.然而,我返回的是一个函数Catch[Int] => Int,这意味着我需要传递两个参数linePrinter才能使它工作.我们想出一个Reader,现在:

val functionText = """def linePrinter(lnr: java.io.LineNumberReader) = arm(lnr) { lnr =>
  var lineNumber = 1
  var lineText = lnr.readLine()
  while (null != lineText) {
    println("%4d: %s" format (lineNumber, lineText))
    lineNumber += 1
    lineText = lnr.readLine()
  }
  lineNumber
} _"""

val reader = new java.io.LineNumberReader(new java.io.StringReader(functionText))
Run Code Online (Sandbox Code Playgroud)

所以,现在,让我们使用它.首先,一个简单的例子:

scala> linePrinter(new java.io.LineNumberReader(reader))(noCatch)
   1: def linePrinter(lnr: java.io.LineNumberReader) = arm(lnr) { lnr =>
   2:          var lineNumber = 1
   3:          var lineText = lnr.readLine()
   4:          while (null != lineText) {
   5:            println("%4d: %s" format (lineNumber, lineText))
   6:            lineNumber += 1
   7:            lineText = lnr.readLine()
   8:          }
   9:          lineNumber
  10:        } _
res6: Int = 10
Run Code Online (Sandbox Code Playgroud)

如果我再试一次,我会得到这个:

scala> linePrinter(new java.io.LineNumberReader(reader))(noCatch)
java.io.IOException: Stream closed
Run Code Online (Sandbox Code Playgroud)

现在假设我想在发生任何异常时返回0.我可以这样做:

linePrinter(new java.io.LineNumberReader(reader))(allCatch withApply (_ => 0))
Run Code Online (Sandbox Code Playgroud)

这里有趣的是,我完全将异常处理(/ 的catch部分)与资源的关闭分离,这是通过完成的.此外,错误处理是我可以传递给函数的值.至少,它使得//语句的模拟更容易.:-)trycatchfinallytrycatchfinally

此外,我可以Catch使用该or方法组合多个,以便我的代码的不同层可能选择为不同的异常添加不同的处理程序.这真的是我的主要观点,但我找不到异常丰富的界面(在我看的简短时间:).

我将结束对arm我给出的定义的评论.这不是一个好的.特别是,我不能使用Catch诸如toEithertoOption将结果更改为R其他方法,这会严重降低其使用价值Catch.不过,我不知道如何改变它.

  • 我担心scala 2.8中的玩具太多了,虽然它是图书馆设计师的强大助手,但如果大多数开发人员急切地想要这样做,那将是灾难性的.Josh Bloch在2008年就Java中语言变化的功率重量比进行了精彩的讨论.我认为这也适用于Scala中的一些功能. (2认同)
  • 最佳答案,丹尼尔.我想知道是否有一个长期答案的徽章...... (2认同)

psp*_*psp 9

作为编写它的人,原因是组合和封装.scala编译器(我打赌最合适的源代码库)充满了吞噬所有异常的地方 - 你可以通过-Ywarn-catches看到这些 - 因为程序员懒得枚举相关的,这是可以理解的因为它很烦人 通过使得可以独立于try逻辑来定义,重用和组合catch以及最终阻塞,我希望降低写入敏感块的障碍.

并且,它还没有完全完成,我也在其他一百万个领域工作.如果你查看actor代码,你可以看到巨大的剪切和粘贴的多重嵌套try/catch/finally块的例子.我不愿意接受try { catch { try { catch { try { catch ...

我最终的计划是抓住实际的PartialFunction而不是需要一个案例陈述的文字列表,这提供了一个全新的机会水平,即try foo() catch getPF()

  • 哦,我忘了提到我的最终计划是捕获实际的部分功能,而不是需要一个案例语句的文字列表,这提供了一个全新的机会级别,即尝试foo()catch getPF() (2认同)
  • 很久以前实现了,它在scala 2.9.1中. (2认同)