【问题标题】:Checking for varargs type ascription in Scala macros检查 Scala 宏中的可变参数类型归属
【发布时间】:2012-11-19 17:40:09
【问题描述】:

假设我有这个宏:

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

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

scala> FooExample.foo(List(1, 2, 3): _*)
res1: Int = 1

并表明推断的类型没有任何可疑之处:

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): _*)

不过,这对我来说就像是一种 hack——它混合了一个验证(检查参数是否为文字)和另一个验证(确认我们确实有可变参数)。我还可以想象我不需要参数是文字的情况(或者我希望它们的类型是通用的)。

我知道我可以做到以下几点:

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]]。这里是a couple of examples

标签: scala macros variadic-functions scala-2.10 scala-macros


【解决方案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!")
    }
  }
}

【讨论】:

  • 谢谢,但这与我的BarExample 基本相同,在一般情况下不起作用。
猜你喜欢
  • 2011-06-10
  • 1970-01-01
  • 2017-07-13
  • 1970-01-01
  • 2013-11-16
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
  • 2015-05-19
相关资源
最近更新 更多