检查Scala宏中的varargs类型归属 [英] Checking for varargs type ascription in Scala macros

查看:74
本文介绍了检查Scala宏中的varargs类型归属的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有这个宏:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆