2013-04-30 5 views
5

私はスカラで先物をする方法を単純化しようとしてきました。私はある時点でFuture[Option[Future[Option[Boolean]]を手に入れましたが、私はそれを以下でさらに簡略化しました。これを簡略化する良い方法はありますか?Future [Option [Future [Option [Boolean]]先物とオプションを簡略化しますか?

「失敗」の未来を渡すことは、と思われます。これを実行する最善の方法はです。すなわち、連続した世界では、私は単に「FAIL !!」を返すだけでした。終わりまで続かずに失敗したときはいつでも。他の方法はありますか?

val doSimpleWork = Future { 
    //Do any arbitrary work (can be a different function) 
    true //or false 
} 

val doComplexWork = Future { 
    //Do any arbitrary work (can be a different function) 
    Some("result") //or false 
} 

val failed = Future { 
    //Do no work at all!!! Just return 
    false 
} 

val fut1 = doSimpleWork 
val fut2 = doSimpleWork 

val fut3 = (fut1 zip fut2).map({ 
    case (true, true) => true 
    case _ => false 
}) 

val fut4 = fut3.flatMap({ 
    case true => 
    doComplexWork.flatMap({ 
     case Some("result") => 
     doSimpleWork 
     case None => 
     failed 
    }) 
    case false => 
    failed 
}) 

fut4.map({ 
    case true => 
    "SUCCESS!!!" 
    case _ => 
    "FAIL!!" 
}) 
+0

エラーロジックが過度に複雑になっているという印象があります。このスニペットの失敗を処理するには、未解決の先物、オプション、ブーリアンの3つの方法があります。この状況を最初のステップとして簡素化することをお勧めします。異なる失敗を優先的なフォームに変換することができます(たとえば、今後の失敗した結果のみに固執するなど)。 –

答えて

3

あなたが熱心にvalFuturesをインスタンス化しているので、あなたの例では、それらのすべては、すぐにあなたがそれら(val x = Future {...})を宣言して実行を開始することに注意してください。代わりにメソッドを使用すると、Futuresは実行チェーンによって要求されたときにのみ実行されます。さらに計算は、その後onFailureでそれを処理、例外をスローすることです避けるために

一つの方法:

def one = future { println("one") ; Some(1) } 
def two = future { println("two") ; throw new Exception("no!"); 2 } 
def three = future { println("three") ; 3 } 

val f = one flatMap { 
    result1 => two flatMap { 
    result2 => three 
    } 
} 

f onFailure { 
    case e: Exception => 
    println("failed somewhere in the chain") 
} 

あなたは「3」が我々ので、プリントアウトすることが想定されていないことをここで見ることができますtwoで失敗します。これがケースである:

one 
two 
failed somewhere in the chain 
+0

私は 'val x = Future {...}'が実際にそれをインスタンス化したことを知りませんでした。関数を定義することは素晴らしいヒントです!また、エラーをキャプチャするために 'onFailure'または' fallbackTo'を使用します。ありがとう! –

1

あなたは少しのコードをクリーンアップするために内包するために使用して、このような何かを試みることができる:

def doSimpleWork = Future{ 
    //do some simple work 
    true 
    } 

    def doComplexWork = Future{ 
    //do something complex here 
    Some("result") 
    } 

    val fut1 = doSimpleWork 
    val fut2 = doSimpleWork 

    val fut = for{ 
    f1Result <- fut1 
    f2Result <- fut2 
    if (f1Result && f2Result) 
    f3Result <- doComplexWork 
    if (f3Result.isDefined) 
    f4Result <- doSimpleWork 
    } yield "success" 

    fut onComplete{ 
    case Success(value) => println("I succeeded") 
    case Failure(ex) => println("I failed: " + ex.getMessage) 
    } 

をそして、あなたは本当にただ「成功」をプリントアウトしたい場合は何が起こっているかを説明すること、今

fut.recover{case ex => "failed"} onSuccess{ 
    case value => println(value) 
    } 

:最後に、あなたはへのコードの最後の部分を変更することができ、「失敗しました」。はじめに、我々はいくつかの非同期作業を行っているFuturesを返す2つの関数を定義しました。 doSimpleWork関数は簡単な作業を行い、ブール値成功/失敗標識を返します。 doComplexWork関数は、より複雑な(そして時間のかかる)何かを行い、結果を表すOption [String]を返します。次に、理解のために入力する前に、doSimpleWorkの2つの並行呼び出しを開始します。 for compでは、両方とも成功したかどうかを確認する前に、fut1fut2の結果を(この順序で)取得します。そうでない場合は、ここで停止し、futの値はNoSuchElementExceptionで失敗します。このような条件がfor compで失敗した場合の値です。両方が成功した場合は、引き続きdoComplexWork関数を呼び出してその結果を待ちます。その結果を確認し、それがSomeだった場合は、doSimpleWorkの最後の呼び出しを1回だけ開始します。成功した場合は、文字列 "success"を返します。 futのvalのタイプを確認する場合は、そのタイプはFuture[String]です。

ここからは、非同期コールバック関数の1つを使用して、呼び出しの全体シーケンス(Successの場合)、またはプロセスのどこかで失敗した場合(Failureの場合)どちらの場合にヒットしたか代替の最終コードブロックでは、文字列 "failed"を返すことでエラーをリカバリし、発生した内容に応じて "success"または "failed"のいずれかを出力するonSuccessコールバックを使用します。

+0

'for'を使うのは良い考えです。私は 'filter'を試していましたが、' for'は組み込んでありました。ありがとう。 –

3

" Monad transformer "は、2つのモナドの"効果 "を組み合わせることができるコンストラクトです。scalazプロジェクトは、いくつかの異なるモナド変圧器を提供します。Option[Unit]がブール値に同形である(Some(()) == trueNone == false)という事実を利用すれば、OptionTモナド変換器を使用してコードを単純化することができます。ここに完全な例があります:

import scalaz._ 
import Scalaz._ 
import scala.concurrent._ 
import ExecutionContext.Implicits.global 
import scala.concurrent.duration._ 
object Foo { 

    // We need a Monad instance for Future, here is a valid one, or you can use the implementation 
    // in the scalaz-contrib project, see http://typelevel.org 
    implicit def futureMonad(implicit executor: ExecutionContext): Monad[Future] = new Monad[Future] { 
    override def bind[A, B](fa: Future[A])(f: A ⇒ Future[B]) = fa flatMap f 
    override def point[A](a: ⇒ A) = Future(a) 
    override def map[A, B](fa: Future[A])(f: A ⇒ B) = fa map f 
    } 

    // OptionT allows you to combine the effects of the Future and Option monads 
    // to more easily work with a Future[Option[A]] 
    val doSimpleWork : OptionT[Future,Unit] = OptionT(Future { 
    // Option[Unit] is isomorphic to Boolean 
    Some(()) //or None 
    }) 

    val simpleFail : OptionT[Future,Unit] = OptionT(Future { 
    None 
    }) 

    val doComplexWork: OptionT[Future,String] = OptionT(Future { 
    Some("result") //or None 
    }) 

    val f1 = doSimpleWork 
    val f2 = doSimpleWork 
    val f3 = doComplexWork 
    val f4 = doSimpleWork 

    def main(argv: Array[String]) { 
    val result = for { 
     _ <- f1 
     // we don't get here unless both the future succeeded and the result was Some 
     _ <- f2 
     _ <- f3 
     r <- f4 
    } yield(r) 

    result.fold((_ => println("SUCCESS!!")),println("FAIL!!")) 

    // "run" will get you to the Future inside the OptionT 
    Await.result(result.run, 1 second) 
    } 
} 
+0

これは本当にきれいで面白いです。ありがとう –