检查Scala宏中的varargs类型归属 [英] Checking for varargs type ascription in 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)
}
这与真实"可变参数一样可以正常工作:
This works as expected with "real" varargs:
scala> FooExample.foo(1, 2, 3)
res0: Int = 3
但是具有归因于varargs类型的序列的行为令我感到困惑(在Scala 2.10.0-RC3中):
But the behavior with a sequence ascribed to the varargs type is confusing to me (in Scala 2.10.0-RC3):
scala> FooExample.foo(List(1, 2, 3): _*)
res1: Int = 1
和表明没什么腥发生的事情与推断出的类型:
And to show that nothing fishy is going on with the inferred type:
scala> FooExample.foo[Int](List(1, 2, 3): _*)
res2: Int = 1
我会在这里预期编译时错误,这就是我想要的.我用下面的方法在大多数我写的宏:
I would have expected a compile-time error here, and that's what I want. I've used the following approach in most of the macros I've written:
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!")
)
}
}
这会在编译时捕获问题:
And this catches the problem at compile time:
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): _*)
但是,这对我来说就像是一个hack —将一种验证(检查参数是否为文字)与另一种验证(确认我们确实有可变参数)混合在一起.我也可以想像情况下,我并不需要的参数是文字(或,我想他们的类型为通用).
This feels like a hack to me, though—it's mixing up one bit of validation (checking that the arguments are literals) with another (confirming that we really have varargs). I can also imagine cases where I don't need the arguments to be literals (or where I want their type to be generic).
我知道我可以做到以下几点:
I know I could do the following:
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)
}
}
}
但是,这是一种处理非常简单(我想广泛必要)位参数验证的丑陋的方式.有没有窍门,我在这里失踪?那是什么,我可以确保
But this is an ugly way of handling a very simple (and I'd suppose widely necessary) bit of argument validation. Is there a trick I'm missing here? What's the simplest way that I can make sure that foo(1 :: Nil: _*)
in my first example gives a compile-time error?
推荐答案
此功能是否按预期工作?
Does this work as expected?
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!")
}
}
}
这篇关于检查Scala宏中的varargs类型归属的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!