如何判断Scala宏的参数是否为函数?

时间:2013-12-03 06:09:05

标签: scala scala-2.10 scala-macros

我正在尝试编写一个对函数参数进行操作的宏。我通过将参数设置为宏Any然后匹配Function(body, params)来非常轻松地处理所有参数长度的匿名函数,但我希望能够传入对函数变量的引用:

val valFunction = (something: Int) => (something * 3).toString
val functionVal = Macros.myMacro(valFunction)

我可以根据official documentation使用WeakTypeTag来使用一个参数的函数来完成此工作:

def myMacro[T, U](param: T => U) = macro myMacroImpl[T, U]

def myMacroImpl[T, U](c: Context)(param: c.Expr[T => U])
     (implicit tt: c.WeakTypeTag[T], implicit ut: c.WeakTypeTag[T]) = {...}

但是宏只适用于一个参数的功能。我尝试检查名为“apply”的成员,这也是一种方法,但我遇到了擦除问题,并且还担心重载方法等等。我目前正在努力解决的问题是为Function0Function22创建23个宏,但是对于男孩我并不满意。我可以采取另一种方法吗?

2 个答案:

答案 0 :(得分:2)

您可能必须接受Any(或未绑定的通用)作为您的宏参数类型,然后在您的宏内手动检查该参数实际上是Function0Function22之一。不过,这并不是那么乏味:

val functionTypes = for(i <- 0 to 22) 
  yield c.mirror.staticClass(s"scala.Function$i").toType.erasure

现在,我的Tree是上述类型之一吗?

functionTypes.exists(funTpe => tree.tpe <:< funTpe)

答案 1 :(得分:1)

解决此问题的一种可能方法是创建一个仅适用于函数的简单类型类:

trait Functionable[T]

使用隐式宏来实现隐式值:

object Functionable {
  implicit def materialize[T]: Functionable[T] = macro materialize_impl[T]
  def materialize_impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[Functionable[T]] = {
    val funcs = (0 to 22).map { c.universe.definitions.FunctionClass(_) }.toList
    if (funcs.exists { ttag.tpe.typeSymbol == _ })
      c.universe.reify { new Functionable[T] { } }
    else {
      c.abort(c.macroApplication.pos, "not a function")
    }
  }
}

此特定实现不适用于子类型,但如果需要更多,则可以自定义它。

然后我们可以将宏定义为:

def myMacro[T](f: T)(implicit functionable: Functionable[T]) = macro myMacroImpl[T]
def myMacroImpl[T](c: Ctx)(f: c.Expr[T])(functionable: c.Expr[Functionable[T]])(implicit ttag: c.WeakTypeTag[T]): c.Expr[String] = c.literal(ttag.tpe.toString)

此宏只会针对函数进行扩展,如果参数不是函数,则会因编译时隐式未找到错误而失败。通过使用宏,您可以通过函数类型的弱类型标记反射性地获取有关类型的信息。

Whole example can be found here