Scala why flatMap treats (x => x) different than (identity)

China☆狼群 提交于 2019-12-14 03:57:16

问题


First, map treats x => x identical than 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)

Now let's transform a List[Option[Int]] into List[Int] discarding all None. We can do that by .flatten. But the point of this question is to understand how flatMap treats 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[?]

Question: why does maybeNums.flatMap(identity) give a compilation error while maybeNums.flatMap(x => x) works OK?


回答1:


A bit more detailed explanation of Lampart's answer.

It comes down to how type inference works in Scala and use of expected types.

For maybeNums.flatMap(???) to work, ??? must have type Option[Int] => GenTraversableOnce[?A] (where ?A stands for some unknown type). That's the expected type.

When ??? is a lambda expression like x => x, the argument is typed as Option[Int] and the body is typed with the expected type GenTraversableOnce[?A]. The type of the body without expected type is Option[Int], so the implicit conversion from Option[Int] to GenTraversableOnce[Int] is found and inserted.

When ??? is identity, it's short for identity[?B] for some yet unknown type ?B (in the same sense as ?A above, but they don't have to be the same, of course), and so has type ?B => ?B. So the compiler needs to solve an equation with two unknown types: ?B => ?B == Option[Int] => GenTraversableOnce[?A]. It matches argument types to pick ?B = Option[Int] for ?B, but can't find a suitable ?A. When the error is printed, ?B is substituted and the type which I wrote as ?A above is printed as ? (because it's the only remaining unknown).

In case identity(_), it's expanded to x => identity(x). Again, the type of the argument is inferred to be Option[Int] and the body has calculated type Option[Int] and expected type GenTraversableOnce[?A].




回答2:


Funny thing i had a similar problem to yours. This behavior is caused by the fact that Option is not GenTraversableOnce but there exists an implicit conversion to one. Compiler informs you what is wrong, but unfortunately as it is the case frequently in Scala it does not point the real reason of a mistake. If your collection was containing elements of GenTraversableOnce type, flatMap method would work just fine.

At first i thought that implicit conversion would solve this problem, but it turns out that eta expansion needs types to match explicitly. What's more interesting is that the following code compiles:

ys.flatMap(identity(_))

I assume that in this case implicit conversion from Option[Int] => Option[Int] to Option[Int] => GenTraversableOnce[Int] happens.

In the case of x => x right x is converted with the previously mentioned implicit conversion so the code compiles



来源:https://stackoverflow.com/questions/47383152/scala-why-flatmap-treats-x-x-different-than-identity

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!