使用巨集時會出現一些常見問題。
此頁面的其餘部分顯示了這些問題的範例解決方案。
巨集實作將放在 macro/
目錄中的子專案中。core/
目錄中的核心專案將相依於此子專案並使用巨集。此組態顯示在下列建置定義中。build.sbt
lazy val commonSettings = Seq(
scalaVersion := "2.12.18",
organization := "com.example"
)
lazy val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value }
lazy val core = (project in file("core"))
.dependsOn(macroSub)
.settings(
commonSettings,
// other settings here
)
lazy val macroSub = (project in file("macro"))
.settings(
commonSettings,
libraryDependencies += scalaReflect.value
// other settings here
)
這指定了巨集實作將放在 macro/src/main/scala/
中,而測試將放在 macro/src/test/scala/
中。它也顯示了我們需要對編譯器有相依性以進行巨集實作。作為巨集範例,我們將使用來自 macrocosm 的 desugar
。macro/src/main/scala/demo/Demo.scala
package demo
import language.experimental.macros
import scala.reflect.macros.blackbox.Context
object Demo {
// Returns the tree of `a` after the typer, printed as source code.
def desugar(a: Any): String = macro desugarImpl
def desugarImpl(c: Context)(a: c.Expr[Any]) = {
import c.universe._
val s = show(a.tree)
c.Expr(
Literal(Constant(s))
)
}
}
macro/src/test/scala/demo/Usage.scala
:
package demo
object Usage {
def main(args: Array[String]): Unit = {
val s = Demo.desugar(List(1, 2, 3).reverse)
println(s)
}
}
然後可以在主控台中執行它
$ sbt
> macroSub/Test/run
scala.collection.immutable.List.apply[Int](1, 2, 3).reverse
實際的測試可以使用 macro/test
像平常一樣定義和執行。
主要專案可以使用與測試相同的方式使用巨集。例如,
core/src/main/scala/MainUsage.scala
:
package demo
object Usage {
def main(args: Array[String]): Unit = {
val s = Demo.desugar(List(6, 4, 5).sorted)
println(s)
}
}
$ sbt
> core/run
scala.collection.immutable.List.apply[Int](6, 4, 5).sorted[Int](math.this.Ordering.Int)
有時,巨集實作和巨集使用應該共用一些通用程式碼。在這種情況下,為通用程式碼宣告另一個子專案,並讓主要專案和巨集子專案相依於新的子專案。例如,來自上方的專案定義將如下所示
lazy val commonSettings = Seq(
scalaVersion := "2.12.18",
organization := "com.example"
)
lazy val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value }
lazy val core = (project in file("core"))
.dependsOn(macroSub, util)
.settings(
commonSettings,
// other settings here
)
lazy val macroSub = (project in file("macro"))
.dependsOn(util)
.settings(
commonSettings,
libraryDependencies += scalaReflect.value
// other settings here
)
lazy util = (project in file("util"))
.settings(
commonSettings,
// other setting here
)
util/src/main/scala/
中的程式碼可供 macroSub
和 main
專案使用。
若要將巨集程式碼與核心程式碼一起包含,請將巨集子專案的二進位檔和來源對應新增至核心專案。而且在發佈時也應該從核心專案相依性中移除巨集子專案。例如,上面的 core
專案定義現在會如下所示
lazy val core = (project in file("core"))
.dependsOn(macroSub % "compile-internal, test-internal")
.settings(
commonSettings,
// include the macro classes and resources in the main jar
Compile / packageBin / mappings ++= (macroSub / Compile / packageBin / mappings).value,
// include the macro sources in the main source jar
Compile / packageSrc / mappings ++= (macroSub / Compile / packageSrc / mappings).value
)
您可能希望停用發佈巨集實作。這可以透過覆寫 publish
和 publishLocal
以不執行任何動作來完成
lazy val macroSub = (project in file("macro"))
.settings(
commonSettings,
libraryDependencies += scalaReflect.value,
publish := {},
publishLocal := {}
)
這裡描述的技術也可以用於上一節中描述的通用介面。