下面是一个明显的可变参数函数:
def fun(xs: Any*) = ???
我们可以用类似的方式定义一个宏:
def funImpl(c: Context)(xs: c.Expr[Any]*) = ???
fun(1,"1",1.0)
但在这种情况下,所有参数都输入为Any
. 事实上,编译器在编译时就知道类型,但对我们隐藏了它。是否可以在宏中获取参数列表及其类型?
下面是一个明显的可变参数函数:
def fun(xs: Any*) = ???
我们可以用类似的方式定义一个宏:
def funImpl(c: Context)(xs: c.Expr[Any]*) = ???
fun(1,"1",1.0)
但在这种情况下,所有参数都输入为Any
. 事实上,编译器在编译时就知道类型,但对我们隐藏了它。是否可以在宏中获取参数列表及其类型?
当然——例如:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object Demo {
def at(xs: Any*)(i: Int) = macro at_impl
def at_impl(c: Context)(xs: c.Expr[Any]*)(i: c.Expr[Int]) = {
import c.universe._
// First let's show that we can recover the types:
println(xs.map(_.actualType))
i.tree match {
case Literal(Constant(index: Int)) => xs.lift(index).getOrElse(
c.abort(c.enclosingPosition, "Invalid index!")
)
case _ => c.abort(c.enclosingPosition, "Need a literal index!")
}
}
}
接着:
scala> Demo.at(1, 'b, "c", 'd')(1)
List(Int(1), Symbol, String("c"), Char('d'))
res0: Symbol = 'b
scala> Demo.at(1, 'b, "c", 'd')(2)
List(Int(1), Symbol, String("c"), Char('d'))
res1: String = c
请注意,推断的类型是精确且正确的。
另请注意,如果参数是具有类型归属的序列,这将不起作用_*
,当然,如果您想捕捉这种情况并提供有用的错误消息,则需要编写如下内容:
def at_impl(c: Context)(xs: c.Expr[Any]*)(i: c.Expr[Int]) = {
import c.universe._
xs.toList.map(_.tree) match {
case Typed(_, Ident(tpnme.WILDCARD_STAR)) :: Nil =>
c.abort(c.enclosingPosition, "Needs real varargs!")
case _ =>
i.tree match {
case Literal(Constant(index: Int)) => xs.lift(index).getOrElse(
c.abort(c.enclosingPosition, "Invalid index!")
)
case _ => c.abort(c.enclosingPosition, "Need a literal index!")
}
}
}