Scala.util.Tryとscala.concurrent.Futureをチェーンすることは可能ですか?どちらも効果的に同じモナドインターフェイスを提供しますが、それらをチェーンしようとするとコンパイルエラーが発生します。
例えば。以下の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することができないため、これは明らかにコンパイルエラーになります。
しかし、それらをチェーンできるのは素晴らしい機能です-これは可能ですか?または、それらを組み合わせてFuture [Try [String]]にする必要がありますか?
(特に、将来のいずれかの例外をキャッチするために、単一の「recover」ブロックを持つことに興味がありますまたは試行)。
このような問題に直面した場合、理解のために異なるタイプを使用したい場合、1つの解決策は、タイプの1つを選択して、他のタイプをそれにマップすることです。あなたの状況では、先物の固有のプロパティ(非同期)を考慮して、最小公分母として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.success
とFuture.failed
を呼び出すと、内部で別のタスクが送信されるという点で、スコープ内のExecutionContext
に影響が及ぶことを覚えておいてください。
[〜#〜]編集[〜#〜]
Viktorがコメントで指摘したように、以下の更新された例のようにFuture.fromTry
を使用するだけで、Try
をaFuture
に変換するプロセスはさらに簡単になります。
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;
きちんと見えませんが。
おそらく問題は古いですが、現在は次のことができます。
implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future
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)
}
}
もあります
Future.fromTry(Try { ... })
だからあなたはすることができます
val result = for {
a <- someFuture
b <- Future.fromTry(processResult(a))
} yield b;