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

Fel*_*ura 77 for-loop scala type-mismatch for-comprehension scala-option

为什么这种结构会导致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)
Run Code Online (Sandbox Code Playgroud)

如果我用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))
Run Code Online (Sandbox Code Playgroud)

这也很好:

for (first <- Some(1); second <- Some(2)) yield (first,second)
Run Code Online (Sandbox Code Playgroud)

Mad*_*doc 114

用于推导被转换成呼叫到mapflatMap方法.例如这一个:

for(x <- List(1) ; y <- List(1,2,3)) yield (x,y)
Run Code Online (Sandbox Code Playgroud)

成为:

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))
Run Code Online (Sandbox Code Playgroud)

因此,第一个循环值(在这种情况下,List(1))将接收flatMap方法调用.因为flatMapList返回另一个时List,理解的结果当然是一个List.(这对我来说是新的:因为理解并不总是导致流,甚至不一定是Seqs.)

现在,看一下如何flatMap声明Option:

def flatMap [B] (f: (A) ? Option[B]) : Option[B]
Run Code Online (Sandbox Code Playgroud)

记住这一点.让我们看看理解错误(带有错误)如何Some(1)转换为一系列地图调用:

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))
Run Code Online (Sandbox Code Playgroud)

现在,很容易看出flatMap调用的参数是根据需要返回a List而不是an的东西Option.

为了解决问题,您可以执行以下操作:

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)
Run Code Online (Sandbox Code Playgroud)

编译得很好.值得注意的是,这Option不是Seq通常假设的子类型.


huy*_*hjl 31

一个容易记住的提示,因为理解将尝试返回第一个生成器的集合的类型,在这种情况下为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
Run Code Online (Sandbox Code Playgroud)

在一般情况下如何扩展理解实际上是一种相当普遍的机制,将类型的对象M[T]与函数组合(T) => M[U]以获得类型的对象M[U].在您的示例中,M可以是Option或List.一般来说,它必须是相同的类型M.因此,您无法将Option与List结合使用.有关其他可能的示例M,请查看此特征的子类.

为什么结合List[T]使用(T) => Option[T],虽然工作的时候,你开始与列表?在这种情况下,库在有意义的地方使用更通用的类型.因此,您可以将List与Traversable结合使用,并且存在从Option到Traversable的隐式转换.

底线是:考虑您希望表达式返回的类型,并以该类型作为第一个生成器开始.如有必要,请将其包裹在该类型中.


sbl*_*ndy 5

这可能与 Option 不是 Iterable 有关。隐式Option.option2Iterable将处理编译器期望第二个是可迭代的情况。我预计编译器的魔力会根据循环变量的类型而有所不同。