If I define a simple stringToInt function and store it as a val, everything works as expected, e.g.
scala> def stringToInt1: (String => Int) = _.toInt
stringToInt1: String => Int
scala> stringToInt1("1")
res0: Int = 1
However, if I then make that implicit, it causes a stack overflow:
scala> implicit def stringToInt2: (String => Int) = _.toInt
stringToInt2: String => Int
scala> stringToInt2("1")
java.lang.StackOverflowError
at .stringToInt2(<console>:7)
at $anonfun$stringToInt2$1.apply(<console>:7)
at $anonfun$stringToInt2$1.apply(<console>:7)
...
At first I suspected that this was because the underscore wasn't resolving to what I expected, but that's not the case, as this style of implicit val works fine for the following simple function:
scala> implicit def plusTwo: (Int => Int) = _ + 2
plusTwo: Int => Int
scala> plusTwo(2)
res2: Int = 4
If I define the parameter explicitly, no stack overflow:
scala> implicit def stringToInt3(s: String) = s.toInt
stringToInt3: (s: String)Int
scala> stringToInt3("1")
res3: Int = 1
(If trying this yourself and this last case stack overflows, restart the scala console and redo this last step)
So my question is, why is the original implicit not correctly resolving?
Edit
Ok digging a little deeper here, it seems that the problem is with the implicit conversion from String to StringOps. If we cut that out, it works fine:
scala> import scala.collection.immutable.StringOps
import scala.collection.immutable.StringOps
scala> implicit def stringToInt4: (String => Int) = new StringOps(_).toInt
stringToInt4: String => Int
scala> stringToInt4("1")
res4: Int = 1
But why would that implicit conversion be causing the issue?
Adding to the other replies.
There is no
toInt
method onString
. Scala has to find an implicit conversion that will yield a type that has atoInt
method.Usually the
StringOps
conversion provides thistoInt
. HoweverInt
has atoInt
too, so scala finds your conversion fromString => Int
and decides that it has precedence over theStringOps
conversion, thus applying it recursively.This is why
StringToInt4
works, as you explicitly tell the compiler what conversion you want. Maybe you could write it as:implicit def stringToInt5: (StringOps => Int) = _.toInt
or check how implicits are resolved and how one takes precedence over the other.