【发布时间】:2018-10-17 14:34:00
【问题描述】:
假设我想创建一个NonZero 类型,以便我的整数除法函数是总计:
def div(numerator: Int, denominator: NonZero): Int =
numerator / denominator.value
我可以通过创建一个带有私有构造函数的NonZero 类来实现这一点:
class NonZero private[NonZero] (val value : Int) { /*...*/ }
还有一个辅助对象来保存Int => Option[NonZero] 构造函数和unapply,因此它可以在match 表达式中使用:
object NonZero {
def build(n:Int): Option[NonZero] = n match {
case 0 => None
case n => Some(new NonZero(n))
}
def unapply(nz: NonZero): Option[Int] = Some(nz.value)
// ...
}
build 适用于运行时值,但必须为文字执行 NonZero.build(3).get 感觉很难看。
使用宏,我们可以定义applyonly for literals,所以NonZero(3)有效,但NonZero(0)是编译时错误:
object NonZero {
// ...
def apply(n: Int): NonZero = macro apply_impl
def apply_impl(c: Context)(n: c.Expr[Int]): c.Expr[NonZero] = {
import c.universe._
n match {
case Expr(Literal(Constant(nValue: Int))) if nValue != 0 =>
c.Expr(q"NonZero.build(n).get")
case _ => throw new IllegalArgumentException("Expected non-zero integer literal")
}
}
}
然而,这个宏的用处并不大,因为它只允许文字,而不是编译时常量表达式:
final val X: Int = 3
NonZero(X) // compile-time error
我的宏中有could pattern match on Expr(Constant(_)),但是NonZero(X + 1) 呢?我宁愿不必实现自己的 scala 表达式求值器。
是否有帮助程序或一些简单的方法来确定在编译时是否知道赋予宏的表达式的值(C++ 将调用 constexpr)?
【问题讨论】:
-
常量写成
final val X = 3,用于内联和常量折叠。类型是常量类型。有Toolbox.eval。但也许你想检查 X 等的定义,因此是一个助手。仅包含常量 github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/… 的示例
标签: scala macros constant-expression