此文件頁面特定於 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
...
為了簡化與巨集的整合,我們也讓在巨集實作中使用樹狀結構變得更容易,而不是之前可能使用的以具體化為中心的 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())
}