How does this implicit val cause a StackOverFlowError?
(pared down my original code, to still cause the error)
object Complicit {
// a class with name, default, and conversion function as implicit val
case class CC[A](name: String, defaultValue: A)(implicit val convert: String => A) {
def getFrom(s: String): A= try {
convert(s)
} catch {
case t: Throwable =>
println("ERROR: %s".format(t)) // just to see the StackOverflowException
defaultValue
}
}
// this works fine
object Works {
val cc1= CC("first", 0.1)(_.toDouble)
}
// this causes java.lang.StackOverflowError due to the implicit
object Fails {
// !!! StackOverFlowError here
implicit val stringToDouble: String => Double= { _.toDouble }
val cc2= CC("second", 0.2)
}
def main(args: Array[String]) {
// this works
println("%s %f".format(Works.cc1.name, Works.cc1.getFrom("2.3")))
// this fails
println("%s %f".format(Fails.cc2.name, Fails.cc2.getFrom("4.5")))
}
}
Am I doing something illegal with implicits?
I believe I can answer what's happening here.. it's related to other implicit conversions, and the one you just created. If you add this trace you can confirm what stack overflow usually relates to - a function calling itself repeatedly until the stack space of java crashes:
implicit val stringsToDouble: String => Double= { x=>println("called inner "+x); x.toDouble }
.... called inner 4.5 called inner 4.5 called inner 4.5 called inner 4.5 called inner 4.5ERROR: java.lang.StackOverflowError
I think what's happening is this - toDouble is not a natural function of java string, but rather happens using an Implicit conversion in StringOps (or StringLike, I'm not really sure but it's the same issue).
So when you call toDouble - the compiler starts seeking an implicit conversion that could contain the function "toDouble". In theory it could be any resulting class.
BUT - what should happen if several implicit conversions could achieve this? Unfortunately, "Double" also contains the function toDouble as proven here:
val x = 44.4
x.toDouble
And guess what? That means your new implicit function, now closest in scope wins the contest and get's called in a circle to accomplish "toDouble" - effectively trying to turn the string into a double, in order to call toDouble (on the class Double), repeatedly.. I'll admit it's fairly confusing, but the evidence fits.
Here's the fix.. it fits the explanation and prevents the recursive calls.
implicit val stringsToDouble: String => Double= { java.lang.Double.parseDouble(_) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With