scala中生成器类型的限制以便于理解



我正在努力理解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上玩这个例子。

最新更新