2013-04-30 22 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