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

时间:2015-02-16 13:18:33

标签: 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的值,并执行可能错误的操作。我不明白为什么我们需要将ftry-catch一起包装。以下是Try的伴随对象的apply方法:

object Try {
  def apply[T](r: => T): Try[T] =
    try Success(r) catch {
    case NonFatal(e) => Failure(e)
  }
}

正如您所看到的,操作发生在Try已覆盖try-catch的应用程序主体中,并且不会抛出,而是安全地存储抛出的异常。所以在我看来,调用任何f(传递给flatMap的那个)是安全的(因为不会抛出异常)。

我的问题是为什么flatMap再次使用try-catch包装应用程序,这有什么必要?

P.S:这是一个相关问题,但没有回答我的问题: Scala: how to understand the flatMap method of Try?

2 个答案:

答案 0 :(得分:4)

如果我理解你的问题,你会想知道为什么你需要(可能)加倍try/catch,而f可能会在返回Try之前抛出异常:

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中没有flatMap,那么会发生什么事情会导致异常升级,从而导致首先出现Try的全部内容。

答案 1 :(得分:1)

问题在于假设当函数的返回类型为Try[T]时,它不会抛出任何异常。

您引用了对象Try {s} 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也在其flatMap中使用try-catch:通过防弹异常处理提供操作链接。

它还表明,对于scala编译器,TryFuture只是普通类型。