检查Scala宏中的varargs类型归属

Tra*_*own 9 macros scala 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)
}
Run Code Online (Sandbox Code Playgroud)

这与"真正的"varargs一样有效:

scala> FooExample.foo(1, 2, 3)
res0: Int = 3
Run Code Online (Sandbox Code Playgroud)

但是归因于varargs类型的序列的行为让我感到困惑(在Scala 2.10.0-RC3中):

scala> FooExample.foo(List(1, 2, 3): _*)
res1: Int = 1
Run Code Online (Sandbox Code Playgroud)

并且表明推断类型没有任何可疑的事情:

scala> FooExample.foo[Int](List(1, 2, 3): _*)
res2: Int = 1
Run Code Online (Sandbox Code Playgroud)

我原本期望编译时错误,这就是我想要的.我在我编写的大多数宏中使用了以下方法:

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!")
    )
  }
}
Run Code Online (Sandbox Code Playgroud)

这在编译时捕获了问题:

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): _*)
Run Code Online (Sandbox Code Playgroud)

这对我来说就像是一个黑客 - 虽然它混淆了一点验证(检查参数是文字)与另一个(确认我们确实有varargs).我还可以想象我不需要将参数作为文字(或者我希望它们的类型是通用的)的情况.

我知道我可以做以下事情:

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)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

但这是处理一个非常简单(我认为非常必要)的一点参数验证的丑陋方式.这里有一个我不知道的伎俩吗?我能确保foo(1 :: Nil: _*)在第一个示例中给出编译时错误的最简单方法是什么?

ido*_*nie 1

这是否按预期工作?

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!")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)