Учитывая следующий макрос (спасибо @TravisBrown для этого help):Использование макросов сделать Case класса
JetDim.scala
case class JetDim(dimension: Int) {
require(dimension > 0)
}
object JetDim {
def validate(dimension: Int): Int = macro JetDimMacro.apply
def build(dimension: Int): JetDim = JetDim(validate(dimension))
}
JetDimMacro.scala
import reflect.macros.Context
object JetDimMacro {
sealed trait PosIntCheckResult
case class LteqZero(x: Int) extends PosIntCheckResult
case object NotConstant extends PosIntCheckResult
def apply(c: Context)(dimension: c.Expr[Int]): c.Expr[Int] = {
import c.universe._
getInt(c)(dimension) match {
case Right(_) => reify { dimension.splice }
case Left(LteqZero(x)) => c.abort(c.enclosingPosition, s"$x must be > 0.")
case Left(NotConstant) => reify { dimension.splice }
}
}
def getInt(c: Context)(dimension: c.Expr[Int]): Either[PosIntCheckResult, Int] = {
import c.universe._
dimension.tree match {
case Literal(Constant(x: Int)) => if (x > 0) Right(x) else Left(LteqZero(x))
case _ => Left(NotConstant)
}
}
}
It работы от REPL:
scala> import spire.math.JetDim
import spire.math.JetDim
scala> JetDim.validate(-55)
<console>:9: error: -55 must be > 0.
JetDim.validate(-55)
^
scala> JetDim.validate(100)
res1: Int = 100
Но я хотел бы построить это время компиляции проверку (через JetDimMacro
) в apply
метод случае класса.
Покушение 1
case class JetDim(dimension: Int) {
require(dimension > 0)
}
object JetDim {
private def validate(dimension: Int): Int = macro JetDimMacro.apply
def build(dimension: Int): JetDim = JetDim(validate(dimension))
}
Но это не удалось:
scala> import spire.math.JetDim
import spire.math.JetDim
scala> JetDim.build(-55)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at spire.math.JetDim.<init>(Jet.scala:21)
at spire.math.JetDim$.build(Jet.scala:26)
... 43 elided
Покушение 2
class JetDim(dim: Int) {
require(dim > 0)
def dimension: Int = dim
}
object JetDim {
private def validate(dimension: Int): Int = macro JetDimMacro.apply
def apply(dimension: Int): JetDim = {
validate(dimension)
new JetDim(dimension)
}
}
Тем не менее, что тоже не получилось:
scala> import spire.math.JetDim
import spire.math.JetDim
scala> JetDim(555)
res0: spire.math.JetDim = [email protected]
scala> JetDim(-555)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:207)
at spire.math.JetDim.<init>(Jet.scala:21)
at spire.math.JetDim$.apply(Jet.scala:30)
... 43 elided
Я думал изменить JetDimMacro#apply
, чтобы вернуть JetDim
, а не Int
. Однако JetDim
живет в проекте core
, который, как я вижу, зависит от проекта macros
(где живет JetDimMacro
).
Как использовать этот метод validate
из сопутствующего объекта JetDim
, чтобы проверить положительный int во время компиляции?
спасибо. При таком подходе объект-компаньон «PositiveInt», содержащий макрос impl, будет жить в том же классе, что и «JetDim», нет? Причина, по которой я спрашиваю, - это если мы хотим использовать «PositiveInt» в другом месте. –
@KevinMeredith Да, это было бы полезно в других местах, поэтому было бы разумно, если бы он жил сам по себе, поэтому другие методы/классы могут его использовать. – tixxit