8

DB レコードを表す文字列 ID のリストがあります。DB から非同期的にロードし、各レコードをリモート サーバーに非同期的にアップロードし、すべてのアップロードが完了したら、アップロードされたレコードの ID を記録します。

私は Scala 2.9.2 を使用しているので、Twitter の core-util Future 実装を使用していますが、Monadic 変換に関しては 2.10 の future とまったく同じように機能するはずです。

一般的な概念は次のとおりです。

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)
                  ^

私は何が欠けていますか?なぜコンパイラは、uploadedId がオプションであると想定するのですか? これを回避できるかなりの方法はありますか?

4

2 に答える 2

6

flatMap(またはバインド) 関数の署名を検討してください。

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

あなたの場合、を生成するflatMapOption与えてを使用しようとしています。しかし、上記の署名のように、呼び出されたのと同じモナドで何かを生成する必要があります。fFuturef

Scala は、この点で必ずしも非常に役立つとは限りません。コンテナーに関係なく、Seq任意の呼び出しを連鎖できるという印象を与えるような方法で (たとえば、 s に)変換するのがかなり得意だからです。flatMap

おそらく必要なのは、モナドを構成する機能を提供する「モナドトランスフォーマー」です。Debasish Ghosh は、Scalaz モナド変換子の使用に関する投稿をここに投稿しています。

于 2013-03-01T09:59:36.260 に答える
0

理解のためにすべての異なるタイプを 1 つに混在させることはできません。Seq と Option を混在させると、最初のものに応じて結果が Seq または Option になることがわかりました。Future と Seq または Option を混在させることはできません。for-comprehension を使用したい場合は、それらをいくつかカスケードする必要があります。そのような場合は、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)
  }
}
于 2016-09-22T17:55:14.670 に答える