这种编译时检查是使用宏的好地方,它将在 2.10 中可用
一个名叫Jason Zaugg的非常聪明的人已经实现了与您需要的类似的东西,但它适用于正则表达式:正则表达式编译时间检查。
您可能想查看它的 Macrocosm 以了解它是如何完成的,以及您如何以相同的目的编写自己的宏。
https://github.com/retronym/macrocosm
如果你真的想了解更多关于宏的知识,首先我想说你需要勇敢,因为目前文档很少,API 可能会发生变化。Jason Zaugg 的作品在 2.10-M3 上编译得很好,但我不确定它是否适用于较新的版本。
如果你想从一些读数开始:
现在,进入主题,Scala 宏是CAT:“编译时 AST 转换”。抽象语法树是编译器表示您的源代码的方式。编译器对 AST 应用后续转换,并在最后一步实际生成 java 字节码。
现在让我们看看 Jason Zaugg 代码:
def regex(s: String): scala.util.matching.Regex = macro regexImpl
def regexImpl(c: Context)(s: c.Expr[String]): c.Expr[scala.util.matching.Regex] = {
import c.universe._
s.tree match {
case Literal(Constant(string: String)) =>
string.r // just to check
c.reify(s.splice.r)
}
}
正如你所看到的,regex 是一个特殊的函数,它接受一个字符串并通过调用宏 regexImpl 返回一个正则表达式
宏函数在第一个参数列表中接收上下文,在第二个参数列表中以 c.Expr[A] 的形式接收宏的参数并返回 c.Expr[B]。请注意,c.Expr 是一个路径依赖类型,即它是一个定义在 Context 内部的类,因此如果您有两个上下文,则以下内容是非法的
val c1: context1.Expr[String] = ...
val c2: context2.Expr[String] = ...
val c3: context1.Expr[String] = context2.Expr[String] // illegal , compile error
现在,如果您查看代码中发生的情况:
- 有一个匹配 s.tree 的匹配块
- 如果 s.tree 是 Literal,包含常量 String ,则调用 string.r
这里发生的事情是 Predef.scala 中定义了从字符串到 StringOps 的隐式转换,该转换在每个 scala 源的编译中自动导入
implicit def augmentString(x: String): StringOps = new StringOps(x)
StringOps 扩展了 scala.collection.immutable.StringLike,其中包含:
def r: Regex = new Regex(toString)
由于宏是在编译时执行的,这将在编译时执行,如果抛出异常,编译将失败(即从无效的正则表达式字符串创建正则表达式的行为)
注意:不幸的是,API 非常不稳定,如果您查看 http://scalamacros.org/documentation/reference.html,您会看到指向 Context.scala 的链接断开。正确的链接是 https://github.com/scala/scala/blob/2.10.x/src/reflect/scala/reflect/makro/Context.scala