Scala-如何在运行时从外部文件编译代码? [英] Scala - How to compile code from an external file at runtime?

查看:285
本文介绍了Scala-如何在运行时从外部文件编译代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想设计一个Scala程序,该程序接受Scala文件作为参数,可以自定义程序的执行.特别是,我想在运行时提供一些文件,这些文件包含将由程序调用的方法的实现.如何正确依赖外部文件并在运行时动态调用其方法?理想情况下,我还希望这些文件能够依赖程序中的方法和类.

I want to design a Scala program that accepts Scala files as parameters which can customize the execution of the program. In particular, I want to supply at runtime files that contain implementations of methods that will be invoked by the program. How can I properly depend on external files and invoke their methods dynamically at runtime? Ideally, I would also like those files to be able to depend on methods and classes in my program.

场景示例:我有一个包含val p: Plant = Greenhouse.getPlant()行的函数,并且在运行时将提供的文件之一中定义了带有getPlant方法的Greenhouse类.在该文件中,方法getPlant返回Rose,其中Rose <: PlantPlant在原始程序中定义.假设文件仅在运行时而不是在编译时加入,我如何实现(或近似)这种相互依赖性?

Example Scenario: I have a function that contains the line val p: Plant = Greenhouse.getPlant(), and the Greenhouse class with the getPlant method is defined in one of the files that will be supplied at runtime. In that file, the method getPlant returns a Rose, where Rose <: Plant and Plant is defined in the original program. How do I achieve (or approximate) this interdependency, assuming the files are only joined at runtime and not at compile-time?

推荐答案

以下是仅使用标准Scala进行操作的方法.不明显的内容全部在GreenhouseFactory中:

Here's how to do it using only standard Scala. The non-obvious stuff is all in GreenhouseFactory:

package customizable

abstract class Plant

case class Rose() extends Plant

abstract class Greenhouse {
  def getPlant(): Plant
}

case class GreenhouseFactory(implFilename: String) {
  import reflect.runtime.currentMirror
  import tools.reflect.ToolBox
  val toolbox = currentMirror.mkToolBox()
  import toolbox.u._
  import io.Source

  val fileContents = Source.fromFile(implFilename).getLines.mkString("\n")
  val tree = toolbox.parse("import customizable._; " + fileContents)
  val compiledCode = toolbox.compile(tree)

  def make(): Greenhouse = compiledCode().asInstanceOf[Greenhouse]
}

object Main {
  def main(args: Array[String]) {
    val greenhouseFactory = GreenhouseFactory("external.scala")
    val greenhouse = greenhouseFactory.make()
    val p = greenhouse.getPlant()

    println(p)
  }
}

将替代表达式放入external.scala:

new Greenhouse {
  override def getPlant() = new Rose()
}

输出为:

Rose()

唯一棘手的事情是GreenhouseFactory需要在该import语句之前加上前缀,以提供对外部文件所需的所有类型和符号的访问.为了使之简单,将所有这些东西制作成一个包装.

The only tricky thing is that GreenhouseFactory needs to prepend that import statement to provide access to all the types and symbols needed by the external files. To make that easy, make a single package with all those things.

编译器ToolBox类似于怪异的进口,您真正需要知道的唯一一件事是toolbox.parse将字符串(Scala源代码)转换为抽象语法树,而toolbox.compile将抽象语法树转换为具有签名() => Any的函数.由于这是动态编译的代码,因此必须将Any强制转换为所需的类型.

The compiler ToolBox is sort of documented here. The only thing you really need to know, other than the weird imports, is that toolbox.parse converts a string (Scala source code) into an abstract syntax tree, and toolbox.compile converts the abstract syntax tree into a function with signature () => Any. Since this is dynamically compiled code, you have to live with casting the Any to the type that you expect.

这篇关于Scala-如何在运行时从外部文件编译代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆