问题
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