3

Using the Scala (runtime) relection API, I'm trying to compile code that makes heavy use of implicits (actually the spire.math library):

    val src = "(a: spire.math.Jet[Double],b: spire.math.Jet[Double]) => a + b"
    println( toolBox.eval(toolBox.parse(src)))

Although these implicits are visible in the scope in which toolbox.eval is called, reflective compilation still fails:

could not find implicit value for parameter f: spire.algebra.Field[Double]

How can I make this information available to the ToolBox?

NietzscheanAI
  • 878
  • 6
  • 16

1 Answers1

4

Before we answer this question, lets first fix the Scala version and make your question reproducible. Let's assume that we use Scala 2.11.8, sbt 0.13.11 and spire-math 0.11.0.

Then the bare build.sbt could look like the following:

name := "test"

version := "1.0"

scalaVersion := "2.11.8"

libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value

libraryDependencies += "org.spire-math" %% "spire" % "0.11.0"

and your code can be stored in Test.scala file, looking like:

import spire.implicits._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

object Test {
  def main(args: Array[String]) = {
    val toolBox = currentMirror.mkToolBox()
    val src ="""
        |(a: spire.math.Jet[Double],b: spire.math.Jet[Double]) => a + b
      """.stripMargin
    println (toolBox.eval(toolBox.parse(src)))
  }
}

Upon executing sbt run, you obtain:

$ sbt run
[info] Running Test 
[error] scala.tools.reflect.ToolBoxError: reflective compilation has failed:
[error] could not find implicit value for parameter f: spire.algebra.Field[Double]

Thus, your question is, why does this fail even though the implicits defined in import spire.implicits._ are included in the scope where toolBox is instantiated and eval is invoked.

Well, note that in your use case, you have two stages where the compiler is invoked independently. The first stage is the compilation of Test.scala and the second stage is the compilation and execution of (a: spire.math.Jet[Double],b: spire.math.Jet[Double]) => a + b

This two staged do not operate in the same runtime. In the first stage, the compiler will be invoked to compile the Test.scala file, and in the second it will be invoked inside the JVM runtime to compile the src string. As a result, these two stages will not share the same scope, simply because they are executed in different runtimes.

One quick solution to this problem is to 'reintroduce' the implicits in the scope of the second stage. In other words you prepend import spire.implicits._ in the string that you try to compile:

import spire.implicits._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

object Test {
  def main(args: Array[String]) = {
    val toolBox = currentMirror.mkToolBox()
    val src ="""
        |import spire.implicits._
        |(a: spire.math.Jet[Double],b: spire.math.Jet[Double]) => a + b
      """.stripMargin
    println (toolBox.eval(toolBox.parse(src)))
  }
}

which results to:

$ sbt run
[info] Running Test 
<function2>
[success] Total time: 5 s, completed Jul 13, 2016 1:48:59 AM

Hope this answers your question. If you like an in-depth answer on how the Scala compiler is searching for implicits in the scopes, a good start would be here.

Alen Stojanov
  • 960
  • 8
  • 14