假设我有这个宏:检查Scala中宏可变参数类型归属
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)
}
这工作与预期的“真实”的可变参数:
scala> FooExample.foo(1, 2, 3)
res0: Int = 3
但冲高的一个序列行为可变参数类型是混乱的,我(在斯卡拉2.10.0-RC3):
scala> FooExample.foo(List(1, 2, 3): _*)
res1: Int = 1
,并表明没有腥使用Wi去推断类型:
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): _*)
这感觉就像一个黑客对我来说,though-它将一个验证(检查参数是文字)与另一个(确认我们确实有可变参数)混合在一起。我还可以设想一些情况,我不需要参数是文字(或者我希望他们的类型是通用的)。
我知道我可以做到以下几点:
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)
}
}
}
但是,这是处理参数验证的一个非常简单的(和我想广泛必要)有点丑陋的方式。有没有我在这里失踪的诡计?我可以确保我的第一个示例中的foo(1 :: Nil: _*)
给出编译时错误的最简单方法是什么?
当您编写“我希望编译时错误在这里”时,请您澄清一下吗?你会期望这是一个错误,因为这是你的域名的要求?或者这应该是一个错误的各种可变宏? –
@EugeneBurmako:我的担心是,在归属情况下,'xs.head'实际上并不是'c.Expr [A]' - 它更像是一个'c.Expr [Seq [A]]'。这里有[几个例子](https://gist.github.com/4191360)。 –