在Scala宏中检查varargs类型归属
假设我有这个宏:在Scala宏中检查varargs类型归属,scala,macros,variadic-functions,scala-2.10,scala-macros,Scala,Macros,Variadic Functions,Scala 2.10,Scala Macros,假设我有这个宏: import language.experimental.macros import scala.reflect.macros.Context object FooExample { def foo[A](xs: A*): Int = macro foo_impl[A] def foo_impl[A](c: Context)(xs: c.Expr[A]*) = c.literal(xs.size) } 这与“真实”varargs的预期效果一样: 但我对varargs
import language.experimental.macros
import scala.reflect.macros.Context
object FooExample {
def foo[A](xs: A*): Int = macro foo_impl[A]
def foo_impl[A](c: Context)(xs: c.Expr[A]*) = c.literal(xs.size)
}
这与“真实”varargs的预期效果一样:
但我对varargs类型的序列行为感到困惑(在Scala 2.10.0-RC3中):
为了证明推断的类型没有可疑之处:
scala> FooExample.foo[Int](List(1, 2, 3): _*)
res2: Int = 1
我希望这里会出现编译时错误,这就是我想要的。在我编写的大多数宏中,我都使用了以下方法:
object BarExample {
def bar(xs: Int*): Int = macro bar_impl
def bar_impl(c: Context)(xs: c.Expr[Int]*) = {
import c.universe._
c.literal(
xs.map(_.tree).headOption map {
case Literal(Constant(x: Int)) => x
case _ => c.abort(c.enclosingPosition, "bar wants literal arguments!")
} getOrElse c.abort(c.enclosingPosition, "bar wants arguments!")
)
}
}
这在编译时解决了问题:
scala> BarExample.bar(3, 2, 1)
res3: Int = 3
scala> BarExample.bar(List(3, 2, 1): _*)
<console>:8: error: bar wants literal arguments!
BarExample.bar(List(3, 2, 1): _*)
但这是一种处理非常简单(我认为是广泛必要的)论证验证的丑陋方式。这里有我错过的把戏吗?我可以用什么最简单的方法来确保我的第一个示例中的
foo(1::Nil:*)
出现编译时错误?这是否按预期工作
object BarExample {
def bar(xs: Int*): Int = macro bar_impl
def bar_impl(c: Context)(xs: c.Expr[Int]*) = {
import c.universe._
import scala.collection.immutable.Stack
Stack[Tree](xs map (_.tree): _*) match {
case Stack(Literal(Constant(x: Int)), _*) => c.literal(x)
case _ => c.abort(c.enclosingPosition, "bar wants integer constant arguments!")
}
}
}
谢谢,但这与我的
BarExample
基本相同,并且在一般情况下不起作用。当您编写“我在这里可能会遇到编译时错误”时,您能澄清一下吗?您会认为这是一个错误,因为这是您的域的要求?或者这对于所有类型的vararg宏都应该是一个错误?@EugeneBurmako:我担心的是,在归属案例中,xs.head
实际上并不是一个c.Expr[a]
它更像是一个c.Expr[Seq[a]
。给你。
scala> BarExample.bar(3, 2, 1)
res3: Int = 3
scala> BarExample.bar(List(3, 2, 1): _*)
<console>:8: error: bar wants literal arguments!
BarExample.bar(List(3, 2, 1): _*)
object BazExample {
def baz[A](xs: A*): Int = macro baz_impl[A]
def baz_impl[A](c: Context)(xs: c.Expr[A]*) = {
import c.universe._
xs.toList.map(_.tree) match {
case Typed(_, Ident(tpnme.WILDCARD_STAR)) :: Nil =>
c.abort(c.enclosingPosition, "baz wants real varargs!")
case _ => c.literal(xs.size)
}
}
}
object BarExample {
def bar(xs: Int*): Int = macro bar_impl
def bar_impl(c: Context)(xs: c.Expr[Int]*) = {
import c.universe._
import scala.collection.immutable.Stack
Stack[Tree](xs map (_.tree): _*) match {
case Stack(Literal(Constant(x: Int)), _*) => c.literal(x)
case _ => c.abort(c.enclosingPosition, "bar wants integer constant arguments!")
}
}
}