準引號

使用案例

語言
此文件頁面特定於 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())
}

此頁面的貢獻者