准引号

用例

语言
此文档页面特定于 Scala 2 中发布的功能,这些功能已在 Scala 3 中删除或被替代功能所取代。除非另有说明,此页面中的所有代码示例均假定你使用的是 Scala 2。

Denys Shabalin 实验性

宏和编译器插件中的 AST 操作

准引号主要设计为宏中进行 AST 操作的工具。常见的工作流是使用准引号模式解构参数,然后使用另一个准引号构造重写的结果

// macro that prints the expression code before executing it
object debug {
  def apply[T](x: => T): T = macro impl
  def impl(c: Context)(x: c.Tree) = { import c.universe._
    val q"..$stats" = x
    val loggedStats = stats.flatMap { stat =>
      val msg = "executing " + showCode(stat)
      List(q"println($msg)", stat)
    }
    q"..$loggedStats"
  }
}

// usage
object Test extends App {
  def faulty: Int = throw new Exception
  debug {
    val x = 1
    val y = x + faulty
    x + y
  }
}

// output
executing val x: Int = 1
executing val y: Int = x.+(Test.this.faulty)
java.lang.Exception
...

为了简化与宏的集成,我们还简化了在宏实现中直接使用树,而不是以前可能使用的以 reify 为中心的 Expr api

// 2.10
object Macro {
  def apply(x: Int): Int = macro impl
  def impl(c: Context)(x: c.Expr[Int]): c.Expr[Int] = { import c.universe._
    c.Expr(q"$x + 1")
  }
}

// in 2.11 you can also do it like that
object Macro {
  def apply(x: Int): Int = macro impl
  def impl(c: Context)(x: c.Tree) = { import c.universe._
    q"$x + 1"
  }
}

你不再需要使用 c.Expr 包装宏的返回值,也不再需要两次指定参数类型,并且 impl 中的返回类型现在是可选的。

准引号也可以“原样”用于编译器插件,因为反射 API 是编译器 Global API 的严格子集。

即时编译

借助 ToolBox API,人们可以在运行时生成、编译和运行 Scala 代码

scala> val code = q"""println("compiled and run at runtime!")"""
scala> val compiledCode = toolbox.compile(code)
scala> val result = compiledCode()
compiled and run at runtime!
result: Any = ()

离线代码生成

借助新的 showCode “漂亮打印机”,人们可以实现离线代码生成器,该生成器借助准引号执行 AST 操作,然后在将其写入磁盘之前将其序列化为实际源代码

object OfflineCodeGen extends App {
  def generateCode() =
    q"package mypackage { class MyClass }"
  def saveToFile(path: String, code: Tree) = {
    val writer = new java.io.PrintWriter(path)
    try writer.write(showCode(code))
    finally writer.close()
  }
  saveToFile("myfile.scala", generateCode())
}

此页面的贡献者