With the below MRE I would expect the following output:
$ scala mre.scala
1
object Main {
def main(args: Array[String]): Unit = {
implicit val thing = List[String]().map(_.to(List))
for (x <- 1 to 1) {
println(x)
}
}
}
However what I do get is the following
$ scala mre.scala
mre.scala:4: error: type mismatch;
found : Int(1)
required: scala.collection.Factory[Char,?]
for (x <- 1 to 1) {
If I remove either the implicit
or the .map(_.to(List))
then it works as expected.
What on earth is going on here? I have no idea where to even begin.
$ scala --version
Scala code runner version 2.13.7 -- Copyright 2002-2021, LAMP/EPFL and Lightbend, Inc.
CodePudding user response:
An educated guess (sth like 99% of certainty): any collection extends (Partial
)Function
therefore by making collection implicit you are providing an implicit conversion (because any unary implicit def
OR implicit function is implicit conversion):
// this
implicit val thing = List[String]().map(_.to(List))
// implies this
implicit def intToCharList(idx: Int): List[Char]] = thing(idx)
Then here:
1 to 1
you have ambiguity: it might be either:
- extension method
.to(Int)
onInt
- normal method
.to(CollectionType)
onList
(since Scala 2.13) afterInt
is implicitly converted
If compiler picks the latter, then you have something like
thing(1).to(1) // .to expects collection factory and got 1 instead
which triggers the error that you see. And it might pick the latter because you declared your implicit in the same scope and richInt
implicit conversion is declared in LowPriorityImplicit
in scala.Predef
, so Scala will prefer yours.
In Scala 3 implicit conversions are a subtype of Function
so this issue is not happening. In Scala 2 I can only strongly recommend against making any collection implicit.