Scala为什么flatMap处理(x => x)不同于(identity)

时间:2017-11-19 23:29:19

标签: scala flatmap

首先,mapx => x视为与identity

相同
List(1,2,3).map(x => x)   //res0: List[Int] = List(1, 2, 3)
List(1,2,3).map(identity) //res1: List[Int] = List(1, 2, 3)

现在让我们将List [Option [Int]]转换为List [Int],丢弃所有None。我们可以通过.flatten来做到这一点。但问题的关键是要了解flatMap如何对待identity

val maybeNums = List(Some(1), None, Some(-2), None, None, Some(33))

// Works OK, result = List[Int] = List(1, -2, 33)
maybeNums.flatMap(x => x)
maybeNums.flatMap(x => x.map(identity))

// Not working:
maybeNums.flatMap(identity)

Error:(5, 20) type mismatch;
 found   : Option[Int] => Option[Int]
 required: Option[Int] => scala.collection.GenTraversableOnce[?]

问题:为什么maybeNums.flatMap(identity)maybeNums.flatMap(x => x)正常工作时会发出编译错误?

2 个答案:

答案 0 :(得分:4)

有趣的是我遇到了类似的问题。这种行为是由GenTraversableOnce不是GenTraversableOnce但是存在隐式转换的事实引起的。编译器通知你出了什么问题,但不幸的是,在Scala中经常出现这种情况并不能指出错误的真正原因。如果您的集合包含ys.flatMap(identity(_)) 类型的元素,则flatMap方法可以正常工作。

起初我认为隐式转换可以解决这个问题,但事实证明,eta扩展需要明确匹配的类型。更有趣的是以下代码编译:

Option[Int] => Option[Int]

我认为在这种情况下会发生从Option[Int] => GenTraversableOnce[Int]x => x的隐式转换。

如果xunpivot被转换为前面提到的隐式转换,那么代码编译

答案 1 :(得分:2)

对Lampart答案的更详细解释。

归结为类型推断在Scala中的工作原理以及预期类型的​​使用。

要使maybeNums.flatMap(???)生效,???必须包含Option[Int] => GenTraversableOnce[?A]类型(其中?A代表某种未知类型)。这是预期的类型。

???是像x => x这样的lambda表达式时,参数的类型为Option[Int],并且主体的类型为GenTraversableOnce[?A]。没有期望类型的正文的类型为Option[Int],因此会找到并插入从Option[Int]GenTraversableOnce[Int]的隐式转换。

???identity时,对于某些未知类型identity[?B](与上面?B相同的意义上的?A,它是?B => ?B的缩写,但它们不是当然,必须是相同的,因此类型?B => ?B == Option[Int] => GenTraversableOnce[?A]也是如此。因此编译器需要解决具有两种未知类型的等式:?B = Option[Int]。它匹配参数类型以选择?B的{​​{1}},但无法找到合适的?A。打印错误时,?B被替换,我在上面?A写的类型打印为?(因为它是唯一剩下的未知数)。

如果identity(_),它会扩展为x => identity(x)。同样,参数的类型推断为Option[Int],正文已计算出类型Option[Int]和预期类型GenTraversableOnce[?A]