3

Scala (ランタイム) リフレクション API を使用して、暗黙 (実際には spire.math ライブラリ) を多用するコードをコンパイルしようとしています。

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

これらの Implicit は toolbox.eval が呼び出されるスコープで表示されますが、リフレクション コンパイルは引き続き失敗します。

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

この情報をツールボックスで利用できるようにするにはどうすればよいですか?

4

1 に答える 1

4

この質問に答える前に、まず 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._スコープに含まれているにもかかわらず、なぜこれが失敗するのかということです。toolBoxeval

さて、あなたのユース ケースでは、コンパイラが個別に呼び出される 2 つの段階があることに注意してください。第 1 段階は のコンパイルでTest.scala、第 2 段階は のコンパイルと実行です。(a: spire.math.Jet[Double],b: spire.math.Jet[Double]) => a + b

この 2 つのステージは、同じランタイムでは動作しません。最初の段階ではコンパイラが呼び出されてTest.scalaファイルがコンパイルされ、2 番目の段階では JVM ランタイム内で呼び出されてsrc文字列がコンパイルされます。結果として、これら 2 つのステージは、異なるランタイムで実行されるという理由だけで、同じスコープを共有しません。

この問題の簡単な解決策の 1 つは、第 2 段階のスコープで暗黙を「再導入」することです。つまり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 コンパイラがどのようにスコープ内の暗黙を検索しているかについての詳細な回答が必要な場合は、ここから始めるのがよいでしょう。

于 2016-07-13T00:08:59.917 に答える