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.parse
和toolbox.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中的“评估”的主要内容,如果未能解决你的问题,请参考以下文章