2013-07-28 110 views
21

鏈接scala.util.Try和scala.concurrent.Future是否可能?它們都有效地提供相同的monadic接口,但試圖鏈接它們會導致編譯錯誤。斯卡拉 - 鏈接期貨嘗試塊?

例如。鑑於下面的兩個簽名

def someFuture:Future[String] = ??? 
def processResult(value:String):Try[String] = ??? 

是否有可能做類似以下的事情?

val result = for(a <- someFuture; b <- processResult(a)) yield b; 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

這顯然會導致編譯錯誤,因爲未來和嘗試不能flatmapp'ed在一起。

然而,這將是一個很好的功能,能夠鏈接它們 - 這是可能的嗎?或者我需要將它們組合成未來[嘗試[字符串]]?

(特別是,我有興趣在未來的嘗試有一個'恢復'塊來捕獲異常。

回答

27

當遇到像這樣的問題時,您想在理解中使用不同類型的解決方案,一種解決方案是嘗試選擇其中一種類型並將其他類型映射到它。鑑於您的情況,考慮到期貨的獨特屬性(異步),我會選擇Future作爲最低公分母,並將Try映射到Future。你可以簡單地是這樣做的:

val result = for{ 
    a <- someFuture 
    b <- tryToFuture(processResult(a)) 
} yield b 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

def tryToFuture[T](t:Try[T]):Future[T] = { 
    t match{ 
    case Success(s) => Future.successful(s) 
    case Failure(ex) => Future.failed(ex) 
    } 
} 

現在,如果你發現這是一個很常見的情況,你不喜歡經常不得不在顯式轉換添加,我想你可以定義tryToFuture方法隱一些輔助對象,並導入需要的地方是這樣的:

object FutureHelpers{ 
    implicit def tryToFuture[T](t:Try[T]):Future[T] = { 
    t match{ 
     case Success(s) => Future.successful(s) 
     case Failure(ex) => Future.failed(ex) 
    } 
    } 
} 

import FutureHelpers._ 
val result = for{ 
    a <- someFuture 
    b <- processResult(a) 
} yield b 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

只要記住調用Future.successFuture.failed都有影響對任何ExecutionContext是,它會提交另一項任務給它的引擎蓋下的範圍。

編輯

正如尤在評論中指出,轉換TryFuture的過程更容易,如果你只是在更新的例子使用Future.fromTry像下面:

val result = for{ 
    a <- someFuture 
    b <- Future.fromTry(processResult(a)) 
} yield b 
result.map { /* Success Block */ } recover { /* Failure Block */ } 

這可能是您最好的選擇,而不是用暗示或滾動您自己的轉換邏輯來做事。

+4

當前存在[討論在scala用戶](https://groups.google.com/d/topic/scala-user/Mu4_lZAWxz0 /討論)關於將'Try'轉換爲'Future'的確切問題。也許像這樣的幫手應該包含在標準庫中。 – gourlaysama

+0

Future.fromTry? –

+0

@ViktorKlang,是的,你是對的。我更新了我的答案以包含該方法。感謝您的高舉。 – cmbaxter

2

如何

val result = for(a <- someFuture) yield for(b <- processResult(a)) yield b; 

雖然它看起來並不整齊。

1
implicit def convFuture[T](ft: Future[Try[T]]): Future[T] = 
ft.flatMap { 
    _ match { 
    case Success(s) => Future.successful(s) 
    case Failure(f) => Future.failed(f) 
    } 
} 
2

問題也許是舊的,但現在您可以:

implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future 
1

還有

Future.fromTry(Try { ... }) 

所以,你可以做

val result = for { 
    a <- someFuture 
    b <- Future.fromTry(processResult(a)) 
} yield b;