在Scala上键入不匹配以便理解

为什么这种结构会导致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)
如果我用List切换Some,它编译得很好:
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
方法调用。由于
List
上的
flatMap
返回另一个
List
,因此理解的结果当然是
List
。 (这对我来说是新的:因为理解并不总是导致流,甚至不一定是在13年代。) 现在,看看
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
调用的参数是根据需要返回
List
而不是
Option
的东西。 为了解决问题,您可以执行以下操作:
for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)
编译得很好。值得注意的是,
Option
不是
Seq
的子类型,正如通常所假设的那样。     
一个容易记住的提示,因为理解将尝试返回第一个生成器的集合的类型,在这种情况下为Option [Int]。因此,如果从Some(1)开始,您应该期望Option [T]的结果。 如果需要List类型的结果,则应该从List生成器开始。 为什么有这个限制并且不假设你总是想要某种顺序?你可以有一个返回
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可以是Option或List。一般来说,它必须是相同的类型
M
。因此,您无法将Option与List结合使用。有关其他可能是
M
的例子,请查看此特征的子类。 当你开始使用List时,为什么将
List[T]
(T) => Option[T]
结合起来?在这种情况下,库在有意义的地方使用更通用的类型。因此,您可以将List与Traversable结合使用,并且存在从Option到Traversable的隐式转换。 底线是:考虑您希望表达式返回的类型,并以该类型作为第一个生成器开始。如有必要,请将其包裹在该类型中。     
它可能与Option不是Iterable有关。隐式的
Option.option2Iterable
将处理编译器期望第二个为Iterable的情况。我希望编译器魔法根据循环变量的类型而有所不同。     
我总觉得这很有帮助:
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)
    

要回复问题请先登录注册