使用for-comprehension中的List组合选项在顺序上会导致类型不匹配。

10 浏览
0 Comments

使用for-comprehension中的List组合选项在顺序上会导致类型不匹配。

为什么这个构造在Scala中会导致类型不匹配的错误?

for (first <- Some(1); second <- List(1,2,3)) yield (first,second)
:6: error: type mismatch;
 found   : List[(Int, Int)]
 required: Option[?]
       for (first <- Some(1); second <- List(1,2,3)) yield (first,second)

如果我将Some和List交换位置,它就可以编译通过:

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)

0
0 Comments

在使用for-comprehension时,根据生成器的顺序,它会尝试返回第一个生成器的集合类型,这里是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;你可以这样写,并在事情“不合理”的时候得到None:

val f = (i:Int) => if (i > 0) Some(List.range(0, i)) else None

for (i <- Some(5); j <- f(i)) yield j

// 返回:Option[List[Int]] = Some(List(0, 1, 2, 3, 4))

for (i <- None; j <- f(i)) yield j

// 返回:Option[List[Int]] = None

for (i <- Some(-3); j <- f(i)) yield j

// 返回:Option[List[Int]] = None

在一般情况下,for-comprehension的展开实际上是一种将类型为M[T]的对象与函数(T) => M[U]结合起来得到类型为M[U]的对象的机制。在你的例子中,M可以是Option或List。通常它必须是相同的类型M。所以你不能将Option与List组合在一起。关于其他可以作为M的东西的例子,请查看这个trait的子类。

为什么以List开始与(T) => Option[T]结合的组合能够起作用?在这种情况下,库使用了更通用的类型,只要它有意义。所以你可以将List与Traversable组合,而且Option到Traversable有一个隐式转换。

底线是:考虑一下你想要表达式返回的类型,并以该类型作为第一个生成器。必要时将其包装在该类型中。

我认为使常规的for语法做这种类型的函子/单子解糖是一个糟糕的设计选择。为什么不为函子/单子映射等使用不同的命名方法,比如fmap等,并将for语法保留为一个非常简单的行为,与来自几乎任何其他主流编程语言的预期相匹配?

你可以使单独的fmap / lift等类似的东西尽可能通用,而不会使一个主流的顺序计算控制流语句变得非常令人惊讶,并且有微妙的性能复杂性等。“一切都适用于for”不值得。

原文地址:https://stackoverflow.com/questions/44733711

0
0 Comments

出现该问题的原因可能与Option不是Iterable有关。隐式Option.option2Iterable可以处理编译器期望second是Iterable的情况。我预计,编译器的魔法会根据循环变量的类型而有所不同。

在Scala中,Option是一个表示可能存在或不存在值的容器。它有两个子类,Some表示存在一个值,None表示不存在值。在for-comprehension中使用Option时,有时会出现类型不匹配的问题。

问题的原因在于for-comprehension中的列表推导式(List comprehension)期望循环变量是一个Iterable类型,而Option并不是Iterable。然而,Scala提供了一个隐式转换函数Option.option2Iterable,它可以将Option转换为Iterable。这个隐式转换函数可以解决编译器期望second是Iterable的问题。

解决方法很简单,只需要在for-comprehension中使用Option时,将Option类型的变量传递给Option.option2Iterable函数,将其转换为Iterable类型。这样,编译器就能正确地识别循环变量的类型,并进行类型匹配。

下面是一个示例代码,演示了如何使用Option.option2Iterable解决类型不匹配的问题:

val option: Option[Int] = Some(5)
val result = for {
  i <- option
  j <- Option.option2Iterable(option) // 使用Option.option2Iterable转换为Iterable
} yield i + j
println(result) // 输出Some(10)

在上面的代码中,我们定义了一个Option[Int]类型的变量option,并将其初始化为Some(5)。然后,在for-comprehension中,使用option进行列表推导式。在第二个循环变量j中,我们使用Option.option2Iterable将option转换为Iterable类型,以满足编译器的期望。最后,将i和j相加,并将结果赋值给result变量。

运行上面的代码,会输出Some(10),表示成功解决了类型不匹配的问题。

总结一下,当在for-comprehension中使用Option时,可能会遇到类型不匹配的问题。解决这个问题的方法是使用Option.option2Iterable将Option转换为Iterable类型。这样,编译器就能正确地识别循环变量的类型,并进行类型匹配。

0
0 Comments

问题的出现原因是在for循环中,使用了List和Option两种不同的数据类型进行组合。在for循环中,它会被转换成map或flatMap方法的调用。对于List类型的for循环,转换后的代码可以正确运行,但是对于Option类型的for循环,转换后的代码会导致类型不匹配的错误。

在代码中,List类型的for循环将被转换成下面这样的代码:

List(1).flatMap(x => List(1,2,3).map(y => (x,y)))

而Option类型的for循环将被转换成下面这样的代码:

Some(1).flatMap(x => List(1,2,3).map(y => (x, y)))

这里出现了问题,因为Option的flatMap方法要求传入的参数返回的是Option类型的结果,但是转换后的代码中传入的参数返回的是List类型的结果,导致类型不匹配的错误。

为了解决这个问题,可以将Option类型的值转换成Seq类型的值,然后再进行for循环。修改后的代码如下:

for(x <- Some(1).toSeq ; y <- List(1,2,3)) yield (x, y)

这样修改后的代码可以正确编译和运行。需要注意的是,Option类型并不是Seq类型的子类型,尽管通常会被认为是。

0