如何使反射可用

时间:2016-04-19 10:28:28

标签: scala scala-reflect spire

使用Scala(运行时)relection API,我正在尝试编译大量使用implicits的代码(实际上是spire.math库):

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

虽然这些隐含在调用toolbox.eval的范围内是可见的,但反射编译仍然失败:

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

如何将此信息提供给ToolBox?

1 个答案:

答案 0 :(得分:3)

在我们回答这个问题之前,先让我们修复Scala版本并让您的问题重现。假设我们使用Scala 2.11.8,sbt 0.13.11和spire-math 0.11.0。

然后裸build.sbt可能如下所示:

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"

,您的代码可以存储在Test.scala文件中,如下所示:

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)))
  }
}

执行sbt run后,您将获得:

$ 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]

因此,您的问题是,即使import spire.implicits._中定义的含义包含在toolBox被实例化并且eval被调用的范围内,为什么会失败。

嗯,请注意,在您的用例中,您有两个独立调用编译器的阶段。第一阶段是Test.scala的汇编,第二阶段是(a: spire.math.Jet[Double],b: spire.math.Jet[Double]) => a + b

的汇编和执行

这两个阶段不在同一个运行时运行。在第一阶段,将调用编译器来编译Test.scala文件,在第二阶段,它将在JVM运行时内调用以编译src字符串。因此,这两个阶段不会共享相同的范围,只是因为它们在不同的运行时执行。

这个问题的一个快速解决方案是在第二阶段的范围内“重新引入”含义。换句话说,在您尝试编译的字符串中添加import spire.implicits._

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)))
  }
}

导致:

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

希望这能回答你的问题。如果您想深入了解Scala编译器如何在作用域中搜索implicits,那么一个好的开始就是here