我正在努力理解scala中的理解。
当第一个生成器是Option以外的集合时,第二个生成器也可以是Option:以外的集合
case class D(p1: Option[Int], p2: List[Int])
val d = for
p1 <- List(1)
p2 <- List(2)
yield D(Option(p1), List(p2))
assertEquals(d, List(D(Some(1), List(2))))
但是,当第一个生成器是Option时,第二个生成器不能是Option以外的集合(但它可以是Option(:
val e = for
p1 <- Option(1)
// p2 <- List(2) // cannot compile: Found: List[D], required: Option[Nothing]
yield D(Option(p1), List(2))
assertEquals(e, Some(D(Some(1), List(2))))
请注意,第二个生成器的类型是List[Int],而不是Intellij Idea 2022.2.1编译器所说的List[D]。使用scala 3.1.1。
谢谢。
理解是以map
结尾的一系列flatMap
的句法糖。
你的第一个例子相当于:
val d2 = List(1).flatMap(p1 => List(2).map(p2 => D(Option(p1), List(p2)))) // ok
如果您的第二个示例在编译时不注释该行,那么它将如下所示:
val d3 = Option(1).flatMap(p1 => List(2).map(p2 => D(Option(p1), List(2)))) // compile error
正如您所看到的,这是不可能的,因为Option
上的flatMap
必须导致另一个Option
而不是List[D]
。可以看到List[D]
,因为这是flatMap
:List(2).map(p2 => D(Option(p1), List(2)))
中函数文字右侧的类型。
在这种情况下,警告是正确的。你想要一个Option
,而不是List[D]
,但在用于理解时,很难理解警告。
通常,类型检查器推断最小上界,即Option[Nothing]
,因为Nothing
是其他所有类型的子类型,而Option
是协变。你得到了Option[Nothing]
,它是Option[Int]
的一个子类型,所以它是类型声音。
你可以在Scastie上玩这个例子。