2013-03-01 29 views
8

我有一個表示DB記錄的字符串ID列表。我想從數據庫異步加載它們,然後將每條記錄異步上傳到遠程服務器,然後當所有數據完成上傳時,記錄上傳的記錄的ID。Scala中的TraversableOnce,Future和Option用於理解

由於我在Scala 2.9.2上,我正在使用Twitter的core-util Future實現,但它應該像Monadic轉換中的2.10期貨一樣工作。

的總體思路是這樣的:

def fetch(id: String): Future[Option[Record]] 
def upload(record: Record): Future[String] 
def notifyUploaded(ids: Seq[String]): Unit 

val ids: Seq[String] = .... 

我試圖通過對修真但獲取收益期權的未來使得它晦澀的代碼不編譯其實要做到這一點:

for { 
    id <- ids 
    maybeRecord <- fetch(id) 
    record <- maybeRecord 
    uploadedId <- upload(record) 
} yield uploadedId 

編譯這將導致以下錯誤:

scala: type mismatch; 
found : com.twitter.util.Future[String] 
required: Option[?] 
    uploadedId <- upload(record) 
       ^

我是什麼MI ssing?爲什麼編譯器期望uploadedId是一個Option?有沒有什麼方法可以解決這個問題?

+0

[類型不匹配的Scala上對於理解]的可能重複(http://stackoverflow.com/questions/4719592/type-mismatch-on-scala-for-comprehension) – 2013-03-01 09:21:16

+1

單子是在一個Monoid ** Endo **的類別 - 函子。只是說' – folone 2013-03-01 09:34:27

+1

@folone:我擔心不是每個人都會得到[笑話](http://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of -endofunctors-什麼最問題)。只是說' – 2013-03-01 10:06:01

回答

6

考慮flatMap(或綁定)函數的簽名:

trait Monad[M[_]] { 
    def flatMap[A](a : M[A], f : A => M[B]) : M[B] 
    .... 

在你的情況,你想上的Option使用flatMap,給它產生Futuref。但是,如上面的簽名中所示,f應該在被調用的相同monad中生成一些東西。

Scala是沒有必要在這方面非常有幫助的,因爲它是在大約轉換的東西還不錯(以Seq S,例如)中,你得到的印象是,你可以鏈任意flatMap電話一起這樣的方式,無論的容器。

你可能想要的是一個'Monad變壓器',它給你一些組合單聲道的能力。 Debasish Ghosh在使用斯卡拉茲單體變壓器here發佈了一篇文章。

0

你不能混合所有不同類型的理解,我發現你可能會混合使用Seq和Option,結果可能是Seq或Option,具體取決於第一個。將Future和Seq或Option混合是不可能的。如果你想使用理解,你將不得不將它們串聯起來。在這種情況下,map/flatMap可能會更好。我用兩種方式實現了你的問題,併爲幾個中間結果添加了類型,以便你看到在處理所有不同類型時正在創建的混亂。

object TestClass { 

    import scala.concurrent.Future 
    import scala.concurrent.ExecutionContext.Implicits.global 
    import scala.concurrent._ 
    import scala.concurrent.duration._ 

    case class Record(id: String) 


    def fetch(id: String): Future[Option[Record]] = Future { 
    Thread.sleep(1000); 
    Some(Record(id)) 
    } 

    def upload(record: Record): Future[String] = Future { 
    Thread.sleep(3000); 
    record.id + "_uploaded" 
    } 

    def notifyUploaded(ids: Seq[String]): Unit = println("notified" + ids) 

    val ids: Seq[String] = Seq("a", "b", "c") 

    def main(args: Array[String]): Unit = { 
    forComprehensionImpl() 
    mapAndFlatMapImpl() 
    } 

    def forComprehensionImpl() = { 
    val result: Seq[Future[Option[Future[String]]]] = for { 
     id <- ids 
    } yield { 
     for { 
     maybeRecord <- fetch(id) 
     } yield { 
     for { 
      record <- maybeRecord 
     } yield { 
      for { 
      uploadedId <- upload(record) 
      } yield uploadedId 
     } 
     } 
    } 
    val result2: Future[Seq[Option[Future[String]]]] = Future.sequence(result) 
    val result3: Future[Unit] = result2.flatMap { x: Seq[Option[Future[String]]] => 
     Future.sequence(x.flatten).map(notifyUploaded) 
    } 
    Await.result(result3, Duration.Inf) 
    } 


    def mapAndFlatMapImpl() = { 
    val res: Seq[Future[Iterable[String]]] = ids.map { id => 
     fetch(id).flatMap { maybeRecord => 
     val res1: Option[Future[Seq[String]]] = maybeRecord.map { record => 
      upload(record) map (Seq(_)) 
     } 
     res1 match { 
      case Some(a) => a 
      case None => Future(Seq()) 
     } 
     } 
    } 
    val res3: Future[Unit] = Future.sequence(res) map (a => notifyUploaded(a.flatten)) 
    Await.result(res3, Duration.Inf) 
    } 
} 
相關問題