为什么scala.util.Success.flatMap(Try.flatMap)在应用其参数后再次使用Try-catch?

为什么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的值,并执行可能存在故障的操作。我不明白的是,

下面是取自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
的值,并执行可能存在故障的操作。我不明白的是,为什么我们需要将
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
必须是健壮的。我明白了。谢谢你的回答。我想我会等待一点,看看是否有其他答案,为了完整性,然后如果没有更好的答案,我会将其标记为答案。因此,原因似乎是有一个防弹的实现,它确保链接不会因意外的异常抛出而失败。谢谢