2012-12-01 21 views
9

假设我有这个宏:检查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: _*)给出编译时错误的最简单方法是什么?

+0

当您编写“我希望编译时错误在这里”时,请您澄清一下吗?你会期望这是一个错误,因为这是你的域名的要求?或者这应该是一个错误的各种可变宏? –

+0

@EugeneBurmako:我的担心是,在归属情况下,'xs.head'实际上并不是'c.Expr [A]' - 它更像是一个'c.Expr [Seq [A]]'。这里有[几个例子](https://gist.github.com/4191360)。 –

回答

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!") 
    } 
    } 
} 
+0

谢谢,但这与我的BarExample基本相同,在一般情况下不起作用。 –