如何使隐式可用于反射
How to make implicits available to reflection
使用 Scala(运行时)反射 API,我正在尝试编译大量使用隐含函数的代码(实际上是 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]
如何让工具箱可以使用这些信息?
在我们回答这个问题之前,让我们首先修复 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
字符串。因此,这两个阶段不会共享相同的范围,只是因为它们在不同的运行时执行。
此问题的一个快速解决方案是 'reintroduce' 第二阶段范围内的隐式。换句话说,您在尝试编译的字符串中添加 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 编译器如何在作用域中搜索隐式,那么 here.
是个不错的开始
使用 Scala(运行时)反射 API,我正在尝试编译大量使用隐含函数的代码(实际上是 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]
如何让工具箱可以使用这些信息?
在我们回答这个问题之前,让我们首先修复 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
字符串。因此,这两个阶段不会共享相同的范围,只是因为它们在不同的运行时执行。
此问题的一个快速解决方案是 'reintroduce' 第二阶段范围内的隐式。换句话说,您在尝试编译的字符串中添加 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 编译器如何在作用域中搜索隐式,那么 here.
是个不错的开始