2013-07-28 5 views
21

scala.util.Tryとscala.concurrent.Futureをチェーン化することは可能ですか?どちらも効果的に同じモナドインタフェースを提供しますが、それらをチェーンしようとするとコンパイルエラーが発生します。Scala - Chaining Futuresブロックを試すか?

たとえば、与えられた2つの署名

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

FutureとTryを一緒にflatMappすることができないため、明らかにコンパイルエラーが発生します。

しかし、チェーンを張ることができるのは素晴らしい機能です。これはすべて可能ですか?または、それらを将来[Try [String]]に結合する必要がありますか?

(特に、「recover」ブロックを将来のいずれかで例外をキャッチすることに興味がありますまたは try)。

答えて

27

このような問題に直面した場合、さまざまな型を理解のために使用したい場合は、1つの型を試して選択し、他の型をその型にマップすることができます。あなたの状況では、先物のユニークなプロパティ(非同期)を考えると、私はFutureを最低共通の分母として選択し、TryFutureにマッピングします。

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に影響を与えていることを覚えておいてください。

EDIT

ヴィクトルはコメントで指摘したように、あなただけの下からの更新例のようにFuture.fromTryを使用している場合、FutureTryを変換するプロセスがさらに簡単です:

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

これはおそらく、暗黙のうちに自分のコンバージョンロジックを動かすことと比べて最良の賭けです。およそ

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

それがきちんと見えませんがどのように

+4

は現在、[議論がありませんでしたscala-user](https://groups.google.com/d/topic/scala-user)/Mu4_lZAWxz0/discussion)に 'Try'を' Future'に変換する正確な問題について説明します。おそらく、このようなヘルパーはどこかの標準ライブラリに含まれるべきです。 – gourlaysama

+0

Future.fromTry? –

+0

@ViktorKlang、そうです。私はそのアプローチを含めるために私の答えを更新しました。ヘッドアップをありがとう。 – cmbaxter

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

おそらく問題は古いですが、現在は次のことができます。また、そう

Future.fromTry(Try { ... }) 

あり

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

あなたは

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