为什么scala.util.Success.flatMap(Try.flatMap)在应用其参数后再次使用Try-catch?
下面是取自scala.util.Success的为什么scala.util.Success.flatMap(Try.flatMap)在应用其参数后再次使用Try-catch?,scala,exception-handling,monads,Scala,Exception Handling,Monads,下面是取自scala.util.Success的flatMap的定义 final case class Success[+T](value: T) extends Try[T] { def flatMap[U](f: T => Try[U]): Try[U] = try f(value) catch { case NonFatal(e) => Failure(e) } } 此处,f函数采用类型为T的值,并执行可能存在故障的操作。我不明白的是,
flatMap
的定义
final case class Success[+T](value: T) extends Try[T] {
def flatMap[U](f: T => Try[U]): Try[U] =
try f(value)
catch {
case NonFatal(e) => Failure(e)
}
}
此处,f
函数采用类型为T
的值,并执行可能存在故障的操作。我不明白的是,为什么我们需要将f
的应用程序包装为try-catch
。下面是Try
的同伴对象的应用方法:
object Try {
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
scala> import scala.util.Try
import scala.util.Try
scala> val someTry = Try(1)
someTry: scala.util.Try[Int] = Success(1)
scala> someTry.flatMap(theInt => {
| throw new Exception("")
| Try(2)
| })
res1: scala.util.Try[Int] = Failure(java.lang.Exception: )
如您所见,该操作发生在已被Try catch
覆盖的Try
应用程序主体中,不会抛出异常,而是安全地存储抛出的异常。因此,在我看来,调用f
(传递到flatMap
)的任何内容都应该是安全的(因为不会引发异常)
我的问题是为什么flatMap
再次用try-catch
包装应用程序,这有什么必要
附言:这里有一个相关的问题,但没有回答我的问题:
如果我正确理解了你的问题,你会想知道为什么你需要(可能)双重
try/catch
,那么f
可能会在返回try
之前抛出异常:
object Try {
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
scala> import scala.util.Try
import scala.util.Try
scala> val someTry = Try(1)
someTry: scala.util.Try[Int] = Success(1)
scala> someTry.flatMap(theInt => {
| throw new Exception("")
| Try(2)
| })
res1: scala.util.Try[Int] = Failure(java.lang.Exception: )
如果没有
flatMap
中的try/catch
,这里会发生的情况是异常会升级,这就破坏了最初使用try
的全部意义。问题在于假设函数的返回类型为try[T]
它不能抛出任何异常
您引用了objecttry的apply
方法的定义,认为它会处理所有非致命异常。但是我们如何知道用户定义的函数已经使用了它呢
def test1(x: Int): Try[Int] = Try { if (x > 0) x
else throw new Exception("failed") }
// ideally all functions should be like test1.
def test2(x: Int): Try[Int] = if (x > 0) Success(x)
else Failure(new Exception("Failed"))
def test3(x: Int): Try[Int] = if (x > 0) Success(x)
else throw new Exception("Failed") // no Try.apply here.
// this compiles fine.
// since the type of else branch is Nothing, the overall return type is 'Try union Nothing' which is Try.
出于同样的原因,scala.concurrent.Future
在其平面图中也使用try-catch:提供具有防弹异常处理的操作链接
它还表明,对于scala编译器,
Try
或Future
只是普通类型。我有点期待这可能是原因,但这不是f的一个不好的值吗?你能给我一个更具体的例子吗,返回Try
的函数在返回Try
实例之前也可以正确地抛出?是的,我的例子是极端的,但想想这样一个事实,你可以在flatMap
中进行所有类型的操作,最危险的操作,数学的,I/O,数据库等等。我想象他们也会被包装在一次尝试中。我是不是想得太理想了?我猜实现试图涵盖这些类型的误用,对吗?是的,想想你不能将f
约束为“安全”的事实,我现在真的想不出一个好的例子,因为老实说,误用似乎很难,但它可能发生,而且就本质而言Try
必须是健壮的。我明白了。谢谢你的回答。我想我会等待一点,看看是否有其他答案,为了完整性,然后如果没有更好的答案,我会将其标记为答案。因此,原因似乎是有一个防弹的实现,它确保链接不会因意外的异常抛出而失败。谢谢