Seb*_*ber 49 scala exception try-catch either
我正在关注Coursera的Scala课程.我也开始阅读Odersky的Scala书.
我经常听到的是,在函数式语言中抛出异常并不是一个好主意,因为它会破坏控制流,我们通常会返回一个带有失败或成功的Either.似乎Scala 2.10也将提供那个方向的Try.
但是在书和课程中,马丁奥德斯基似乎并没有(至少现在)说异常是坏的,而且他经常使用它们.我也注意到方法断言/要求......
最后我有点困惑,因为我想遵循最佳实践,但他们不清楚,语言似乎是双向的...
有人可以解释一下我应该在哪种情况下使用什么?
Rex*_*err 48
基本准则是使用异常来实现非常特殊的**.对于"普通"故障,使用Option
或更好Either
.如果您正在与Java接口,当有人以错误的方式打喷嚏时会抛出异常,您可以使用它Try
来保证自己的安全.
我们来举一些例子.
假设您有一个从地图中获取内容的方法.怎么可能出错?好吧,像segfault*堆栈溢出一样戏剧性和危险的东西,或者找不到像元素一样的东西.你让segfault堆栈溢出抛出一个异常,但如果你只是找不到一个元素,为什么不返回一个Option[V]
而不是一个值或一个异常(或null
)?
现在假设您正在编写一个用户应该输入文件名的程序.现在,如果你不是只是在出现问题时立即保释计划,那Either
就是要走的路:
def main(args: Array[String]) {
val f = {
if (args.length < 1) Left("No filename given")
else {
val file = new File(args(0))
if (!file.exists) Left("File does not exist: "+args(0))
else Right(file)
}
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
现在假设您要解析带有空格分隔数字的字符串.
val numbers = "1 2 3 fish 5 6" // Uh-oh
// numbers.split(" ").map(_.toInt) <- will throw exception!
val tried = numbers.split(" ").map(s => Try(s.toInt)) // Caught it!
val good = tried.collect{ case Success(n) => n }
Run Code Online (Sandbox Code Playgroud)
所以你有三种方法(至少)可以处理不同类型的失败:Option
因为它有效/没有,如果不工作是预期的行为,不是令人震惊和惊人的失败; Either
当事情可以工作或不工作(或者,实际上,任何情况下,你有两个互斥的选项),你想要保存一些有关出错的信息; 而且Try
当你不想让自己处理异常处理的整个头痛时,仍然需要与异常快乐的代码接口.
顺便提一下,例外情况就是很好的例子 - 所以你会发现它们在教科书或学习材料中比其他地方更常见,我认为:教科书的例子往往是不完整的,这意味着通常会通过精心设计来防止的严重问题应该而是通过抛出异常来标记.
*编辑:Segfaults崩溃JVM,不管字节码如何都不应该发生; 即使例外也无济于事.我的意思是堆栈溢出.
**编辑:异常(没有堆栈跟踪)也用于Scala中的控制流 - 它们实际上是一种非常有效的机制,并且它们启用了库定义的break
语句和return
从方法返回的内容,即使控件也是如此实际上已经进入了一个或多个闭包.大多数情况下,你不应该自己担心,除了意识到捕获所有 Throwable
s并不是一个超级想法,因为你可能会错误地捕获其中一个控制流异常.
Dav*_*ith 11
因此,这是Scala专门用于转换功能纯度的地方之一,以便从传统语言和环境(特别是Java)轻松转换/互操作.功能纯度被例外打破,因为它们破坏了参照完整性并使得不可能在等同上进行推理.(当然,非终止递归也是如此,但是很少有语言愿意强制执行那些使这些不可能的限制.)为了保持功能纯度,你使用Option/Maybe/Either/Try/Validation,所有这些都编码成功或者作为引用透明类型的失败,并使用它们提供的各种高阶函数或基础语言特殊的monad语法来使事情更清楚.或者,在Scala中,您可以简单地决定放弃功能纯度,知道它可能会在短期内使事情变得更容易,但在长期内会更加困难.这类似于在Scala,可变集合或本地"var"中使用"null".轻微的可耻,并没有做太多,但每个人都在截止日期.