2

我需要编译函数,然后使用不同的 type 参数对其进行评估List[Map[String, AnyRef]]。我有以下代码不使用这种类型编译,而是使用简单类型(如List[Int].

我发现只有某些实现Liftableinscala.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)")
                                ^
4

1 回答 1

1

问题不是来自复杂的类型,而是来自尝试使用AnyRef. 当您取消引用某些文字时,这意味着您希望基础架构能够创建有效的语法树来创建与您传递的对象完全匹配的对象。不幸的是,这显然不适用于所有对象。例如,假设您已将引用Thread.currentThread()作为Map. 它怎么可能起作用?编译器只是无法重新创建如此复杂的对象(更不用说使其成为当前线程)。所以你有两个明显的选择:

  1. 让你争论也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")
  }

这种方法的明显缺点是您在编译时失去了类型安全性,并且可能需要为树提供大量上下文才能工作

  1. 另一种方法是不要尝试将任何包含的内容传递AnyRef给编译器基础设施。这意味着您创建了一些类似函数Wrapper
package so {

  trait Wrapper {
    def call(args: List[Map[String, AnyRef]]): Map[String, AnyRef]
  }

}

然后让您生成的代码返回 aWrapper而不是直接执行逻辑并Wrapper从通常的 Scala 代码而不是内部编译代码中调用。像这样的东西:

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")
}

PS我不确定你在做什么,但要注意性能问题。Scala 是一种难以编译的语言,因此编译自定义代码可能比运行它需要更多时间。如果性能成为问题,您可能需要使用其他一些方法,例如成熟的宏代码生成或至少缓存已编译代码。

于 2017-12-11T02:51:48.043 回答