>我需要编译函数,然后用List[Map[String, AnyRef]]
类型的不同参数对其进行评估。 我有以下代码,它不使用这种类型进行编译,而是使用List[Int]
等简单类型进行编译。
我发现Liftable
在scala.reflect.api.StandardLiftables.StandardLiftableInstances
中只有某些实现
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
val functionWrapper =
"""
object FunctionWrapper {
def makeBody(messages: List[Map[String, AnyRef]]) = Map.empty
}""".stripMargin
val functionSymbol =
tb.define(tb.parse(functionWrapper).asInstanceOf[tb.u.ImplDef])
val list: List[Map[String, AnyRef]] = List(Map("1" -> "2"))
tb.eval(q"$functionSymbol.function($list)")
为此出现编译错误,如何使其工作?
Error:(22, 38) Can't unquote List[Map[String,AnyRef]], consider using
... or providing an implicit instance of
Liftable[List[Map[String,AnyRef]]]
tb.eval(q"$functionSymbol.function($list)")
^
问题不是来自复杂类型,而是来自尝试使用AnyRef
。当您取消引用某些文字时,这意味着您希望基础结构能够创建有效的语法树,以创建与您传递的对象完全匹配的对象。不幸的是,这显然不可能适用于所有对象。例如,假设您已将对Thread.currentThread()
的引用作为Map
的一部分传递。它怎么可能工作?编译器只是无法重新创建如此复杂的对象(更不用说使其成为当前线程了)。因此,您有两个明显的选择:
- 让你的论点也成为
Tree
,即像这样的东西
def testTree() = {
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
val functionWrapper =
"""
| object FunctionWrapper {
|
| def makeBody(messages: List[Map[String, AnyRef]]) = Map.empty
|
| }
""".stripMargin
val functionSymbol =
tb.define(tb.parse(functionWrapper).asInstanceOf[tb.u.ImplDef])
//val list: List[Map[String, AnyRef]] = List(Map("1" -> "2"))
val list = q"""List(Map("1" -> "2"))"""
val res = tb.eval(q"$functionSymbol.makeBody($list)")
println(s"testTree = $res")
}
这种方法的明显缺点是你在编译时松散了类型安全,并且可能需要为树工作提供大量上下文
- 另一种方法是不尝试将任何包含
AnyRef
的内容传递给编译器基础结构。这意味着您创建了一些类似函数的Wrapper
:
package so {
trait Wrapper {
def call(args: List[Map[String, AnyRef]]): Map[String, AnyRef]
}
}
然后让你生成的代码返回一个Wrapper
而不是直接执行逻辑,并从通常的 Scala 代码而不是在编译的代码中调用Wrapper
。像这样:
def testWrapper() = {
val tb = universe.runtimeMirror(getClass.getClassLoader).mkToolBox()
val functionWrapper =
"""
|object FunctionWrapper {
| import scala.collection._
| import so.Wrapper /* <- here probably different package :) */
|
| def createWrapper(): Wrapper = new Wrapper {
| override def call(args: List[Map[String, AnyRef]]): Map[String, AnyRef] = Map.empty
| }
|}
| """.stripMargin
val functionSymbol = tb.define(tb.parse(functionWrapper).asInstanceOf[tb.u.ImplDef])
val list: List[Map[String, AnyRef]] = List(Map("1" -> "2"))
val tree: tb.u.Tree = q"$functionSymbol.createWrapper()"
val wrapper = tb.eval(tree).asInstanceOf[Wrapper]
val res = wrapper.call(list)
println(s"testWrapper = $res")
}
附言我不确定你在做什么,但要注意性能问题。Scala 是一种很难编译的语言,因此编译自定义代码可能比运行它花费更多时间。如果性能成为一个问题,您可能需要使用一些其他方法,例如成熟的宏代码生成或至少缓存已编译的代码。