链接scala尝试包含选项的实例

时间:2013-12-19 23:24:32

标签: scala try-catch option

我正在尝试找到一种更清晰的方式来表达与此类似的代码:

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,则无论成功或失败,都会返回其结果。这在代码中运行良好,但我发现很难跟踪发生的事情。

我很乐意使用for comprehension

for
{
  attempt1 <- method1
  attempt2 <- method2
  attempt3 <- method3
}
  yield
{
  List(attempt1, attempt2, attempt3).find(_.isDefined)
}

因为它的美丽和它的作用是非常清楚的。但是,如果所有方法都成功,那么无论先前的方法是否返回可用的答案,它们都会每次执行。不幸的是我不能拥有它。

任何建议都将不胜感激。

5 个答案:

答案 0 :(得分:8)

scalaz在这里可以提供帮助。您需要为try添加monad实例的scalaz-contrib,然后您可以使用具有漂亮组合器的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)

答案 1 :(得分:1)

如果您不介意为每种方法创建一个函数,可以执行以下操作:

(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
}

答案 2 :(得分:0)

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
  }
}

答案 3 :(得分:0)

method1.flatMap(_.map(Success _).getOrElse(method2)).flatMap(_.map(Success _).getOrElse(method3))

这是如何运作的:

第一个flatMap采用Try [Option [String]],如果是Failure,则返回Failure,如果是Success,则返回选项上的_.map(Success _)。getOrElse(method2)。如果选项是Some,那么它返回Some的Success,如果是None,则返回method2的结果,可以是Success [None],Success [Some [String]]或Failure。

第二张图与其获得的结果类似,可以来自method1或method2。

由于getOrElse采用名字参数,因此只有在需要时才调用方法2和方法3。

你也可以使用fold而不是map和getOrElse,虽然我认为不太清楚。

答案 4 :(得分:-1)

From this blog

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,这里不需要选件吗?