2013-04-30 24 views
5

我一直在试图简化我在Scala中做未来的方式。我得到了一个Future[Option[Future[Option[Boolean]],但我已经在下面进一步简化了它。有没有更好的方法来简化这一点?未来[期权[期权[期权[布尔]]简化期货和期权?

通过未来的“失败”不会似乎像这样做的最好方法。即在顺序世界中,我只是返回“失败!!”任何时候它失败而不是继续结束。有其他方法吗?

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

我的印象是你的错误逻辑变得过于复杂。有三种可能的方式来处理这个片段中的失败:失败的期货,期权,布尔。我建议简化这种情况作为第一步,也许将不同的失败转化为首选形式(例如只坚持未来失败的结果)。 – 2013-04-30 08:03:25

回答

3

注意,在你的榜样,因为你急切地实例化Futuresval,所有的人都会只要你声明它们(val x = Future {...})开始执行。相反,使用方法只会在执行链要求时执行期货。为了避免进一步的计算

一种方法是抛出一个异常,那么处理它与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") 
} 

你可以在这里看到,“三”是不应该被打印出来,因为我们在two上失败。这种情况:

one 
two 
failed somewhere in the chain 
+0

我不知道'val x = Future {...}'实际上是将它实例化的。定义一个函数是一个很棒的提示!我也将使用'onFailure'或'fallbackTo'来捕获错误。谢谢! – 2013-05-01 01:18:30

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正在做一些异步工作。 doSimpleWork函数将执行一些简单的工作并返回一个布尔成功/失败指示器。 doComplexWork函数将执行更复杂(耗时)的操作并返回表示结果的Option [String]。然后,我们在进入理解之前启动doSimpleWork的两个并行调用。为了比较,我们在检查它们是否都成功之前得到fut1fut2(以该顺序)的结果。如果没有,我们会在这里停下来,fut val会失败,NoSuchElementException就是这样的情况,当这样的条件在for comp中失败。如果两者都成功,我们将继续并调用doComplexWork函数并等待其结果。然后我们检查它的结果,如果它是Some,我们将启动doSimpleWork的最后一次调用。如果成功,我们会产生字符串“成功”。如果您检查fut val的类型,它的类型为Future[String]

从那里,我们使用其中一个异步回调函数来检查整个调用序列是否完成(Success个案),或者在过程的某个地方(Failure个案)是否失败,打印出与它遇到的情况有关。在备选的最终代码块中,我们通过返回字符串“failed”来恢复任何可能的故障,然后使用回调,该回调将根据发生的情况打印“成功”或“失败”。

+0

使用'for'是个好主意。我正在尝试'过滤器',但'''有他们内置。谢谢。 – 2013-05-01 01:21:30

3

“单声道变压器“是一种结构,可以让您将两个单声道的”效果“结合起来,斯凯拉兹项目提供了几种不同的单声道变压器。我的建议是,如果您还使用Option[Unit]与布尔同构的事实(Some(()) == trueNone == false),则可以使用OptionT monad变换器简化代码。这里有一个完整的例子:

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

这真的很整齐有趣。谢谢 – 2013-05-01 01:25:30