Scala中的“评估”

Posted

技术标签:

【中文标题】Scala中的“评估”【英文标题】:"eval" in Scala 【发布时间】:2010-11-14 02:16:37 【问题描述】:

Scala 可用于编写 Java 应用程序脚本吗?

我需要从 Java 加载一段 Scala 代码,为其设置执行范围(主机应用程序公开的数据),对其进行评估并从中检索结果对象。

Scala 文档展示了从 Java 调用已编译的 Scala 代码是多么容易(因为它会变成常规的 JVM 字节码)。

但是我如何动态评估一个 Scala 表达式(来自 Java,或者如果更简单,从 Scala 内部)?

对于许多其他语言,有 javax.scripting 接口。 Scala 似乎不支持它,我在 Java/Scala 互操作性文档中找不到任何不依赖提前编译的内容。

【问题讨论】:

【参考方案1】:

现在是 2011 年,您可以通过 scala.tools.nsc.Interpreter 这样做

见http://blog.darevay.com/2009/01/remedial-scala-interpreting-scala-from-scala/

【讨论】:

这不是“第三方”包,它是 Scala 语言实现的一部分。 值得指出的是,解释器包含在 Scala 编译器中,而不是面向公众的标准库 API,因此可能会发生变化。链接的博客是为 Scala 2.8 版编写的。从 2.9 版开始,必须将 scala.tools.nsc.Interpreter 替换为 scala.tools.nsc.interpreter.IMain。博客中的其余代码应该是一样的。 我认为 @Kipton 和 nsc.interpreter.ISettings 也是如此。不幸的是,我和其他人正在获得值 ClassCastExceptions:***.com/questions/6367393/…(当我最近尝试测试我的答案时)和***.com/questions/6164138/scala-as-scripting-language/… 从 2.10 开始,您可以构建源代码树并使用 scala.tools.reflect.ToolBox#eval 编译它们。参见scalamacros.org/talks/2012-04-28-MetaprogrammingInScala210.pdf的幻灯片 35 404:找不到页面【参考方案2】:

Scala 不是脚本语言。它可能看起来有点像脚本语言,人们可能会为此而提倡它,但它并不真正适合 JSR 223 脚本框架(面向动态类型语言)。要回答您最初的问题,Scala 没有 eval 函数,就像 Java 没有 eval 一样。鉴于它们本质上是静态的,这样的函数对于这两种语言中的任何一种都没有真正意义。

我的建议:重新考虑你的代码,这样你就不需要eval(你很少这样做,即使在有它的语言中,比如Ruby)。或者,也许您根本不想在这部分应用程序中使用 Scala。如果您真的需要eval,请尝试使用 JRuby。 JRuby、Scala 和 Java 很好地结合在一起。很容易让你的系统的一部分用 Java,一部分用 Scala,另一部分(需要 eval 的位)用 Ruby。

【讨论】:

+1 Scala 确实支持像脚本语言一样从文本文件运行,但它不是脚本语言。听起来作者有兴趣为他的应用程序提供脚本接口,javascript、Groovy 或 Lua 更适合。 解释器不是语言功能,它只是工具集的一部分。以 Haskell 为例。 GHC Haskell 提供了ghc 工具,它是编译器,ghci,它是交互式shell。就像 Scala 的 REPL 一样,它是一个“解释器”,但实际上没有办法在 Haskell 函数中使用它。如前所述,允许这样做是非常不安全的(类型方面的),并且不符合整个语言。 @woky 答案非常明确:“Scala 没有 eval 函数,就像 Java 没有 eval 一样”。回答者没有说他对 Scala 的看法,他说 Scala 不是一种脚本语言——它不是。这是事实。询问 JS 开发者社区如何为您使用 Javascript 构建的设备驱动程序进行内联汇编 - 您会得到相同的答案:“您使用了错误的语言”。这不是关于内联汇编或设备驱动程序的意见。 “鉴于它们本质上是静态的,这样的函数对于这两种语言中的任何一种都没有真正意义。”|我认为这句话需要更多的理由。在对包含静态类型语言代码的 sn-p 文本执行 eval 时,我没有看到任何内在问题。 这个答案在十年前可能更正确,但它被称为“Scala”,因为该语言(本身)是可扩展的,从小脚本到应用程序。可以改进工具以在动态上下文中进行评估,但脚本(因此)显然是其任务的一部分。【参考方案3】:

Scala 在 2.11 (https://issues.scala-lang.org/browse/SI-874) 中添加了对 JSR-223 的官方支持。

因此,如果您在考虑了 Daniel Spiewak 当前接受的答案中的考虑因素(关于以不需要的方式重新考虑)之后仍然需要它,那么这应该是官方的替代方案。

【讨论】:

【参考方案4】:

您可以通过获取 Scala 代码,将其包装在一个类中,编译该类,使用反射创建一个新实例,然后调用它来模拟“eval”。这有点复杂,而且 scala 编译器的初始化速度非常慢(大约 2 秒),但它工作正常。

这里有一个库,叫做“util-eval”:https://github.com/twitter/util/

有问题的代码在这里:https://github.com/twitter/util/blob/master/util-eval/src/main/scala/com/twitter/util/Eval.scala

它是这样工作的:

val sum = Eval[Int]("1 + 1")
// sum will be 2

【讨论】:

不错!尽管上面的语法仍然有效,但它现在会打印一条警告:“不推荐使用包 util 中的对象 Eval:请改用 Eval 的一次性实例。”新的推荐代码为:val i: Int = new Eval()("1 + 1")。请参阅 Eval.scala 的不同 apply() 函数以了解变化(从文件或 InputStreams 读取)。【参考方案5】:

我不确定这是否是一个好方法,但我使用toolbox.parsetoolbox.eval 解决了这个问题

要在 Scala 中进行 eval,您必须:

    导入 scala-reflect

libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.7"

    使用工具箱中的 eval:

  import scala.reflect.runtime.currentMirror
  import scala.tools.reflect.ToolBox
  val toolbox = currentMirror.mkToolBox()

  val as = "2*(2+3)"
  val compe = toolbox.eval(toolbox.parse(as))

  println(compe.getClass) // prints class java.lang.Integer
  println(compe) // prints 10

【讨论】:

不起作用:“对象工具不是包 scala 的成员” 需要在"scala-reflect"中引用"org.scala-lang" % "scala-compiler",然后上面的代码才能正常编译运行。【参考方案6】:

您始终可以使用 scalac 编译 scala 类,然后动态加载该类。但我想这不是你想要的。

【讨论】:

好吧,那行得通。 “解释器”无论如何都会在内部调用编译器。但似乎编译器并不比解释器更容易嵌入。 取决于你想如何“嵌入”它。最简单的方法是将其称为外部程序。但是,如果您希望更好地将程序与编译器集成,您可以在此处找到有关如何执行此操作的讨论:nabble.com/Compiler-API-td12050645.html

以上是关于Scala中的“评估”的主要内容,如果未能解决你的问题,请参考以下文章

评估Scala

Scala实现风险评估

在 IntelliJ 中评估返回值的表达式

Scala 流及其内存使用情况

Scala如何使用akka actor有效地处理超时操作

在 FRP 中实现快照