使用宏时会遇到一些常见问题。
页面其余部分将展示这些问题的示例解决方案。
宏实现将位于 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
Project 定义现在将如下所示
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 := {}
)
此处描述的技术也可能用于上一节中描述的公共接口。