Предположим, я хочу создать тип 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
для литералов кажется уродливой.
Используя макрос, мы можем определить apply
только для литералов, поэтому 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
Я может найти шаблон для Expr(Constant(_))
в моем макросе, но как насчет NonZero(X + 1)
? Я бы предпочел не реализовывать свой собственный оценщик выражений scala.
Есть ли помощник или какой-нибудь простой способ определить, известно ли значение выражения, переданного макросу, во время компиляции (что С++ вызовет constexpr
)?
final val X = 3
для встраивания и сворачивания констант. Тип — константный тип. ЕстьToolbox.eval
. Но, может быть, вы хотите проверить определение X и т. Д., Следовательно, помощник. Пример только с константами github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/ - person som-snytt   schedule 17.10.2018