0

我了解 Try/Success/Failure 与 Try Catch 之间存在差异。官方文档也有一个很好的例子:

import scala.io.StdIn
import scala.util.{Try, Success, Failure}

def divide: Try[Int] = {
  val dividend = Try(StdIn.readLine("Enter an Int that you'd like to divide:\n").toInt)
  val divisor = Try(StdIn.readLine("Enter an Int that you'd like to divide by:\n").toInt)
  val problem = dividend.flatMap(x => divisor.map(y => x/y))
  problem match {
    case Success(v) =>
      println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v)
      Success(v)
    case Failure(e) =>
      println("You must've divided by zero or entered something that's not an Int. Try again!")
      println("Info from the exception: " + e.getMessage)
      divide <------------ Here!
  }
}

但是,我很难理解

上面示例中显示的 Try 的一个重要属性是它能够管道或链接操作,并在此过程中捕获异常。上面示例中的 flatMap 和 map 组合器基本上都传递了它们成功完成的值,包装在 Success 类型中以便链中的下一个组合器进一步操作它,或者包装在 Failure 类型中的异常通常是简单地沿着链条传递。诸如recover 和recoverWith 之类的组合器旨在在失败的情况下提供某种类型的默认行为。

是否有任何示例来说明如何使“管道、链、操作......”变得更容易?有什么例子可以说明为什么我们没有 try-catch 的相同好处?

divide另外,我们为什么要回来Failure?否则,我们会有type mismatch? 这是唯一的原因吗?

4

3 回答 3

8

Try[A]

这是一种代数数据类型 (ADT),由 2 种情况组成:Success[A]Failure[A]。这种代数结构定义了很多操作,如map,flatMap和其他。Try具有 amap和 aflatMap的构造函数 from Ato的事实Try[A]使该结构成为 Monad。

Monad 是允许我们以函数方式表达计算序列的抽象。让我们看一个例子

def tryDivide(v1: Double, v2: Double): Try[Double] = Try(v1 / v2)

val tryDivisions = tryDivide(1.0, 2.0).flatMap {
   res => tryDivide(res, 2)
}.flatMap {
   res => tryDivide(res, 0)
}

作为这段代码的结果,我们将得到 a Failure[A],因为最新的操作被零除。Try 是 Monad 的事实允许我们使用这样的 for 理解重写它

for {
   v1 <- tryDivide(1.0, 2.0)
   v2 <- tryDivide(v1, 2)
   res <- tryDivide(v2, 0)
} yield res

try

另一方面try,它只是一种语言语法,可让您捕获可能从代码中抛出的异常,但它不是具有可以执行的方法的代数结构。

于 2018-05-06T17:01:33.300 回答
1

Try[A]表示一个计算,如果成功则为 type 的值A,否则出现问题并为Throwable. 它有两种形式:在成功的计算中,它是一个A包装为Success[A]. 在失败中,它是 的一个实例Failure[A],它包装了一个Throwable异常。

的一个特点Try是它可以让您以一种非常智能的方式使用高阶函数(如Option,Either和其他 monad)和链式操作。

假设你有这个小程序:

scala> def divide(x: Int, y: Int): Try[Int] =
         if (y == 0) Try(throw new Exception("Error: division by zero!"))
         else Try(x / y)
divide: (x: Int, y: Int)scala.util.Try[Int]

你得到

scala> divide(3,2)
res30: scala.util.Try[Int] = Success(1)

scala> divide(3,0)
res31: scala.util.Try[Int] = Failure(java.lang.Exception: Error: division by zero!)

因此,您同样可以使用高阶函数的能力以优雅的方式捕获故障:

scala> divide(3,0).getOrElse(0)
res32: Int = 0

这只是一个示例,但请考虑一下您是否尝试执行可能会失败的更复杂的计算,并且您必须捕获此结果并采取相应措施。

为什么不在try catchsScala 中使用?这不是因为你不能这样做,而是因为它不是很实用。如果您需要处理另一个线程中发生的异常(比如说,由一个演员),您无法捕获该异常,您可能希望向当前主线程传递一条消息,说明计算失败并且您将弄清楚如何处理。

另外,为什么我们在失败中返回分歧?否则会出现类型不匹配?这是唯一的原因吗?

它返回divideFailure因为它让您在程序失败时重新运行程序,但这只是程序员做出的决定。您可以只返回Failure包装某种错误消息。

于 2018-05-06T16:37:36.380 回答
0

首先是关于 flatMap() 的一些背景知识:这个函数在很多方面都引用了众所周知的 monadic >= (bind) 运算符的概念,如果你想更深入地了解它,我强烈推荐阅读《Learn You a Haskell》这本书为了伟大的好!”。

但简单地说,使用 Try 你可以做这样的事情:

someTry.flatMap(//doSomething1).flatMap(//doSomething2)

然后进行一些匹配以从第一个或第二个函数获取值或获取错误(请注意,任何来自错误的 flatMap 都不会被计算,并且错误将保持不变)但是使用旧方法你会这样做:

try{
    //doSomething()
} catch {
    ...
}
try {
    //domSomething2()
} catch {
    ...
}
于 2018-05-06T16:15:19.990 回答