簡體   English   中英

鏈接包含選項的 scala Try 實例

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM