2013-03-01 9 views
8

私はDBレコードを表す文字列IDのリストを持っています。 DBから非同期的にロードしたいのですが、各レコードをリモートサーバに非同期でアップロードし、すべてのアップロードが完了したら、アップロードされたレコードのIDを記録します。スカラでの理解のための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] = .... 

私はマイルだ何
scala: type mismatch; 
found : com.twitter.util.Future[String] 
required: Option[?] 
    uploadedId <- upload(record) 
       ^

:次のエラーになり、これをコンパイルする

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

ssing?なぜコンパイラはuploadedIdをOptionと期待していますか?私はこれを回避できる方法はありますか?

+0

[理解のためのScalaで型の不一致](http://stackoverflow.com/questions/4719592/type-mismatch-on-scala-for-comprehension) –

+1

の可能重複モナドはモノイドであります** Endo **のカテゴリ - ファンクタ。 Just sayin ' – folone

+1

@folone:私は誰もが[冗談]を取得する恐れがあります(http://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of -endofunctors-whats-the-problem)を実行します。 Just sayin ' –

答えて

6

flatMap(またはバインド)関数のシグネチャ考えてみましょう:あなたのケースでは

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

を、あなたはそれをFutureを生成fを与え、OptionflatMapを使用しようとしています。しかし、上記の署名のように、fは、それが呼び出されたのと同じモナドに何かを生成しているはずです。

Scalaは、これに関係なく、任意のflatMapの呼び出しを一緒にすることができるという印象を受けるように(例えば、Seqに)周囲のものを変換するのにはかなり良いので、の容器。

あなたが望むものは、モナドを構成する能力を与える「モナドトランス」です。 Debasish Ghoshは、Scalazモナド変圧器hereを使用した記事を掲載しています。

0

あなたは理解のためにすべての異なるタイプを混在させることはできません。私はあなたが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) 
    } 
} 
関連する問題