2013-12-19 40 views
5

我試圖找到表達的代碼看起來類似於這樣的清潔方法:鏈接斯卡拉嘗試包含實例選項

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 
} 

正如你所看到的,這個嘗試在序列中的每個方法,如果再一個失敗執行停止並且基本匹配解決該故障。如果方法1或方法2成功但包含無,則會嘗試序列中的下一個方法。如果執行到達方法3,其結果總是返回,而不管成功或失敗。這在代碼中工作正常,但我發現很難跟蹤發生的事情。

我喜歡使用一種理解

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

,因爲它的美麗,它是什麼做的是相當清楚的。但是,如果所有方法都成功,則每次都執行它們,而不管先前的方法是否返回可用的答案。不幸的是,我不能那樣做。

任何建議,將不勝感激。

回答

7

scalaz能有所幫助這裏。你需要scalaz-contrib,它爲Try增加了一個monad實例,然後你可以使用具有很好combinator的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) 
0

foo方法應該做同樣的東西,你的代碼,我不認爲這是可能的話,使用做了理解

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 
    } 
} 
0
method1.flatMap(_.map(Success _).getOrElse(method2)).flatMap(_.map(Success _).getOrElse(method3)) 

這是如何工作的:

第一flatMap需要嘗試[選項[字符串],如果它是一個失敗則返回失敗,如果它是一個成功時,它會返回選項上的_.map(Success _)。getOrElse(method2)。如果選項是Some,則返回Some的成功,如果是None,則返回method2的結果,可能是Success [None],Success [Some [String]]或Failure。

第二張地圖與其得到的結果類似,可能來自method1或method2。

由於getOrElse需要一個名稱參數method2和method3只在需要的時候被調用。

你也可以使用fold而不是map和getOrElse,儘管在我看來這並不清楚。

+0

類型不匹配。 'Option'中的'orElse'不會帶'Try [Option]'。 –

+0

固定在我的編輯,雖然它不像我現在想的那樣高雅現在:( – Thayne

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 
}