Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scala implicit causes StackOverflowError

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?

like image 238
Core Avatar asked Jul 24 '13 03:07

Core


1 Answers

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(_)  }
like image 131
LaloInDublin Avatar answered Sep 28 '22 16:09

LaloInDublin