web-dev-qa-db-ja.com

Scala-先物の連鎖ブロックを試してみませんか?

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」ブロックを持つことに興味がありますまたは試行)。

22
James Davies

このような問題に直面した場合、理解のために異なるタイプを使用したい場合、1つの解決策は、タイプの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に影響が及ぶことを覚えておいてください。

[〜#〜]編集[〜#〜]

Viktorがコメントで指摘したように、以下の更新された例のようにFuture.fromTryを使用するだけで、TryをaFutureに変換するプロセスはさらに簡単になります。

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

これは、暗黙的に何かを実行したり、独自の変換ロジックをローリングしたりするよりも、おそらく最善の策です。

27
cmbaxter

どうですか

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

きちんと見えませんが。

2
derek

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

  implicit def tryToFuture[T](t:Try[T]):Future[T] = Promise.fromTry(t).future
2
Carlos Fau
  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)
  }
}
1
chenhry

もあります

Future.fromTry(Try { ... })

だからあなたはすることができます

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