理解のためにSCALAの不一致をタイプします
-
12-10-2019 - |
質問
なぜこの構造はSCALAでタイプの不一致エラーを引き起こすのですか?
for (first <- Some(1); second <- List(1,2,3)) yield (first,second)
<console>:6: error: type mismatch;
found : List[(Int, Int)]
required: Option[?]
for (first <- Some(1); second <- List(1,2,3)) yield (first,second)
リストでいくつかを切り替えた場合、それは正常にコンパイルされます:
for (first <- List(1,2,3); second <- Some(1)) yield (first,second)
res41: List[(Int, Int)] = List((1,1), (2,1), (3,1))
これも正常に機能します:
for (first <- Some(1); second <- Some(2)) yield (first,second)
解決
包含は、への呼び出しに変換されます map
また flatMap
方法。たとえば、これ:
for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)
それになります:
List(1).flatMap(x => List(1,2,3).map(y => (x,y)))
したがって、最初のループ値(この場合、 List(1)
) flatMap
メソッド呼び出し。以来 flatMap
aで List
別のものを返します List
, 、理解のための結果はもちろん List
. 。 (これは私にとって新しいものでした:包含は常にストリームをもたらすとは限らず、必ずしもでさえありません Seq
s。)
さて、その方法を見てください flatMap
で宣言されています Option
:
def flatMap [B] (f: (A) ⇒ Option[B]) : Option[B]
これに留意してください。理解に誤ったものを見てみましょう( Some(1)
)マップコールのシーケンスに変換されます。
Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))
今、のパラメーターが flatMap
呼び出しはaを返すものです List
, 、ではありません Option
, 、 要求に応じ。
物事を修正するために、次のことを行うことができます。
for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)
それはうまくコンパイルされます。それは注目に値します Option
のサブタイプではありません Seq
, 、よく想定されるように。
他のヒント
覚えておくのが簡単なヒント、 包括的 この場合、最初のジェネレーター、オプション[int]のコレクションの種類を返しようとします。だから、あなたがから始めたら いくつか(1) オプション[t]の結果を期待する必要があります。
の結果が必要な場合 リスト タイプ、リストジェネレーターから始める必要があります。
なぜこの制限があり、常に何らかのシーケンスが必要だと仮定しないのですか?あなたは戻るのが理にかなっている状況を持つことができます Option
. 。多分あなたは持っています Option[Int]
あなたが何かと組み合わせて取得したいこと Option[List[Int]]
, 、次の関数で: (i:Int) => if (i > 0) List.range(0, i) else None
;それからあなたはこれを書いて、物事が「意味をなさない」ときに何も得ることができません:
val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None
for (i <- Some(5); j <- f(i)) yield j
// returns: Option[List[Int]] = Some(List(0, 1, 2, 3, 4))
for (i <- None; j <- f(i)) yield j
// returns: Option[List[Int]] = None
for (i <- Some(-3); j <- f(i)) yield j
// returns: Option[List[Int]] = None
どのように 包括的 一般的なケースで拡張されるのは、実際にはタイプのオブジェクトを組み合わせるかなり一般的なメカニズムです M[T]
関数付き (T) => M[U]
タイプのオブジェクトを取得します M[U]
. 。例では、mはオプションまたはリストにすることができます。一般に、それは同じタイプでなければなりません M
. 。したがって、オプションをリストと組み合わせることはできません。可能な他のものの例として M
, 、 見る この特性のサブクラス.
なぜ結合したのか List[T]
と (T) => Option[T]
あなたがリストを始めたときに働きますか?この場合、ライブラリは理にかなっているより一般的なタイプを使用します。したがって、リストをTraversableと組み合わせることができ、オプションからトラバー可能への暗黙的な変換があります。
一番下の行は次のとおりです。式を返すタイプを考えて、最初のジェネレーターとしてそのタイプから始めてください。必要に応じて、そのタイプで包みます。
おそらく、オプションが反復性ではないことと関係があります。暗黙的 Option.option2Iterable
コンパイラが2番目が反復可能であると予想しているケースを処理します。ループ変数のタイプによって、コンパイラマジックは異なると思います。
私はいつもこれを役立つと感じました:
scala> val foo: Option[Seq[Int]] = Some(Seq(1, 2, 3, 4, 5))
foo: Option[Seq[Int]] = Some(List(1, 2, 3, 4, 5))
scala> foo.flatten
<console>:13: error: Cannot prove that Seq[Int] <:< Option[B].
foo.flatten
^
scala> val bar: Seq[Seq[Int]] = Seq(Seq(1, 2, 3, 4, 5))
bar: Seq[Seq[Int]] = List(List(1, 2, 3, 4, 5))
scala> bar.flatten
res1: Seq[Int] = List(1, 2, 3, 4, 5)
scala> foo.toSeq.flatten
res2: Seq[Int] = List(1, 2, 3, 4, 5)