5

私は、Scala で先物を処理する方法を単純化しようとしてきました。ある時点で a を得ました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!!"
})
4

3 に答える 3

3

「モナドトランスフォーマー」は、2 つのモナドの「効果」を組み合わせることができる構造です。scalaz プロジェクトは、いくつかの異なるモナドトランスフォーマーを提供します。私の提案は、ブール値 ( ==および== ) とOption[Unit]同形であるという事実も利用する場合、 OptionT モナド変換子を使用してコードを単純化できるということです。完全な例を次に示します。Some(())trueNonefalse

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)
  }
}
于 2013-04-30T13:41:58.763 に答える
3

あなたの例では、Futuresを aに積極的にインスタンス化しているためval、それらはすべて ( ) を宣言するとすぐに実行を開始することに注意してくださいval x = Future {...}。代わりにメソッドを使用すると、Future は、実行チェーンによって要求された場合にのみ実行されます。

さらなる計算を回避する 1 つの方法は、例外をスローしてから、次のように処理すること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")
}

ここで、「three」が出力されないことがわかります。なぜなら、失敗したからtwoです。これはケースです:

one 
two 
failed somewhere in the chain
于 2013-04-30T06:27:32.630 に答える