[英]Chaining scala Try instances that contain Options
我试图找到一种更简洁的方式来表达与此类似的代码:
def method1: Try[Option[String]] = ???
def method2: Try[Option[String]] = ???
def method3: Try[Option[String]] = ???
method1 match
{
case f: Failure[Option[String]] => f
case Success(None) =>
method2 match
{
case f:Failure[Option[String]] => f
case Success(None) =>
{
method3
}
case s: Success[Option[String]] => s
}
case s: Success[Option[String]] => s
}
如您所见,这会按顺序尝试每个方法,如果一个方法失败,则执行停止并且基本匹配解析为该失败。 如果 method1 或 method2 成功但包含 None 则尝试序列中的下一个方法。 如果执行到达 method3,无论成功或失败,它的结果总是被返回。 这在代码中工作正常,但我发现很难跟踪正在发生的事情。
我很想用 a 来理解
for
{
attempt1 <- method1
attempt2 <- method2
attempt3 <- method3
}
yield
{
List(attempt1, attempt2, attempt3).find(_.isDefined)
}
因为它很漂亮,它在做什么很清楚。 但是,如果所有方法都成功,那么它们每次都会被执行,而不管较早的方法是否返回可用的答案。 不幸的是,我不能拥有它。
任何建议将不胜感激。
scalaz 可以在这里提供帮助。 您将需要 scalaz-contrib,它为 Try 添加一个 monad 实例,然后您可以使用 OptionT ,它具有很好的组合器。 下面是一个例子:
import scalaz.OptionT
import scalaz.contrib.std.utilTry._
import scala.util.Try
def method1: OptionT[Try, String] = OptionT(Try(Some("method1")))
def method2: OptionT[Try, String] = OptionT(Try(Some("method2")))
def method3: OptionT[Try, String] = { println("method 3 is never called") ; OptionT(Try(Some("method3"))) }
def method4: OptionT[Try, String] = OptionT(Try(None))
def method5: OptionT[Try, String] = OptionT(Try(throw new Exception("fail")))
println((method1 orElse method2 orElse method3).run) // Success(Some(method1))
println((method4 orElse method2 orElse method3).run) // Success(Some(method2))
println((method5 orElse method2 orElse method3).run) // Failure(java.lang.Exception: fail)
如果您不介意为每个方法创建一个函数,您可以执行以下操作:
(Try(None: Option[String]) /: Seq(method1 _, method2 _, method3 _)){ (l,r) =>
l match { case Success(None) => r(); case _ => l }
}
这根本不是惯用的,但我想指出,有一个相当短的命令式版本,还有一些小方法:
def okay(tos: Try[Option[String]]) = tos.isFailure || tos.success.isDefined
val ans = {
var m = method1
if (okay(m)) m
else if ({m = method2; okay(m)}) m
method3
}
foo
方法应该和你的代码做同样的事情,我认为使用 for comprehension 是不可能的
type tryOpt = Try[Option[String]]
def foo(m1: tryOpt, m2: tryOpt, m3: tryOpt) = m1 flatMap {
case x: Some[String] => Try(x)
case None => m2 flatMap {
case y: Some[String] => Try(y)
case None => m3
}
}
method1.flatMap(_.map(Success _).getOrElse(method2)).flatMap(_.map(Success _).getOrElse(method3))
这是如何工作的:
第一个 flatMap 接受一个 Try[Option[String]],如果它是一个失败,它返回失败,如果它是一个成功,它返回 _.map(Success _).getOrElse(method2) 选项。 如果选项是 Some 则返回 Some 的 Success,如果是 None 则返回 method2 的结果,可能是 Success[None]、Success[Some[String]] 或 Failure。
第二个映射与它得到的结果类似,它可能来自方法 1 或方法 2。
由于 getOrElse 需要一个按名称的参数,method2 和 method3 仅在需要时调用。
您也可以使用 fold 代替 map 和 getOrElse,尽管在我看来这不太清楚。
def riskyCodeInvoked(input: String): Int = ???
def anotherRiskyMethod(firstOutput: Int): String = ???
def yetAnotherRiskyMethod(secondOutput: String): Try[String] = ???
val result: Try[String] = Try(riskyCodeInvoked("Exception Expected in certain cases"))
.map(anotherRiskyMethod(_))
.flatMap(yetAnotherRiskyMethod(_))
result match {
case Success(res) => info("Operation Was successful")
case Failure(ex: ArithmeticException) => error("ArithmeticException occurred", ex)
case Failure(ex) => error("Some Exception occurred", ex)
}
顺便说一句,IMO,这里不需要选项吗?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.