毎回回避しなければならないこの問題があります。 for内包表記を使用して、Futureに含まれているものをマッピングすることはできません。
例:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
val f = Future( List("A", "B", "C") )
for {
list <- f
e <- list
} yield (e -> 1)
これは私にエラーを与えます:
error: type mismatch;
found : List[(String, Int)]
required: scala.concurrent.Future[?]
e <- list
^
しかし、これを行うとうまくいきます:
f.map( _.map( (_ -> 1) ) )
For内包表記を使用してこれを行うことはできないはずですが、フラットマップを使用しない他の例で機能するのはなぜですか? Scala 2.10.0を使用しています。
まあ、あなたが理解のために単一の複数のジェネレータを持っているとき、あなたはflattening結果の型です。つまり、List[List[T]]
を取得する代わりに、List[T]
を取得します。
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> for (a <- list) yield for (b <- list) yield (a, b)
res0: List[List[(Int, Int)]] = List(List((1,1), (1,2), (1,3)), List((2,1
), (2,2), (2,3)), List((3,1), (3,2), (3,3)))
scala> for (a <- list; b <- list) yield (a, b)
res1: List[(Int, Int)] = List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3),
(3,1), (3,2), (3,3))
では、Future[List[T]]
をどのようにフラット化しますか?複数のT
を取得するため、Future[T]
にすることはできません。Future
は(List
とは対照的に)1つしか格納できませんそのうちの。ところで、同じ問題がOption
でも発生します。
scala> for (a <- Some(3); b <- list) yield (a, b)
<console>:9: error: type mismatch;
found : List[(Int, Int)]
required: Option[?]
for (a <- Some(3); b <- list) yield (a, b)
^
それを回避する最も簡単な方法は、理解のために単に複数をネストすることです:
scala> for {
| list <- f
| } yield for {
| e <- list
| } yield (e -> 1)
res3: scala.concurrent.Future[List[(String, Int)]] = scala.concurrent.im
pl.Promise$DefaultPromise@4f498585
振り返ってみると、この制限はかなり明白でした。問題は、ほとんどすべての例がコレクションを使用し、すべてのコレクションがGenTraversableOnce
であるため、自由に混合できることです。これに加えて、Scalaが批判されているCanBuildFrom
メカニズムにより、GenTraversableOnce
の代わりに、任意のコレクションを混合して特定の型を取り戻すことが可能になります。 。
そして、さらにぼやけさせるために、Option
をIterable
に変換できます。これにより、オプションが最初に来ない限り、オプションとコレクションを組み合わせることができます。
しかし、私の意見では、混乱の主な原因は、理解力を教えるときに誰もこの制限に触れたことがないということです。
うーん、わかったと思います。 for内包表記がフラットマップを追加するので、私は未来の中でラップする必要があります。
これは機能します:
for {
list <- f
e <- Future( list )
} yield (e -> 1)
上記を追加したところ、まだ回答がありませんでした。ただし、これをさらに詳しく説明するために、1つのfor内包内で作業することができます。ただし、将来のオーバーヘッドの価値があるかどうかはわかりません(編集:成功を使用するとオーバーヘッドは発生しません)。
for {
list1 <- f
list2 <- Future.successful( list1.map( _ -> 1) )
list3 <- Future.successful( list2.filter( _._2 == 1 ) )
} yield list3
補遺、半年後
これを解決するもう1つの方法は、最初のマップの戻り値の型とは別の型がある場合に、=
ではなく<-
を使用することです。
割り当てを使用する場合、その行はフラットマップされません。これで、異なるタイプを返す明示的なマップ(または他の変換)を自由に実行できます。
これは、1つのステップに他のステップと同じ戻り値の型がない複数の変換がある場合に役立ちますが、コードを読みやすくするためにfor-comprehension構文を使用する必要があります。
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
val f = Future( List("A", "B", "C") )
def longRunning( l:List[(String, Int)] ) = Future.successful( l.map(_._2) )
for {
list <- f
e = list.map( _ -> 1 )
s <- longRunning( e )
} yield s
List
とFuture
は異なるモナドであるため、元のバージョンはコンパイルされません。これが問題である理由を確認するには、次のことを検討してください。
_f.flatMap(list => list.map(e => e -> 1))
_
明らかにlist.map(_ -> 1)
は_(String, Int)
_ペアのリストであるため、flatMap
への引数は文字列のリストをこれらのペアのリストにマップする関数です。しかし、文字列のリストを何らかのFuture
にマッピングするものが必要です。したがって、これはコンパイルされません。
あなたの答えのバージョンはコンパイルされますが、あなたが望むことはしません。それはこれに脱糖します:
_f.flatMap(list => Future(list).map(e => e -> 1))
_
今回は型が揃っていますが、興味深いことは何もしていません。単にFuture
から値を引き出してFuture
に戻し、結果をマッピングしています。 。したがって、Future[(List[String], Int)]
が必要なときに、タイプFuture[List[(String, Int)]]
になることになります。
あなたがしていることは、2つの(異なる)ネストされたモナドを持つ一種の二重マッピング操作であり、for
- comprehensionが役立つものではありません。幸いなことに、f.map(_.map(_ -> 1))
はユーザーの意図どおりに機能し、明確で簡潔です。
このフォームは、シリアルマップまたはシリアルイールドよりも読みやすくなっています。
for (vs <- future(data);
xs = for (x <- vs) yield g(x)
) yield xs
タプリングマップを犠牲にして:
f.map((_, xs)).map(_._2)
より正確には:
f.map((vs: List[Int]) => (vs, for (x <- vs) yield g(x))).map(_._2)