我正在尝试编写一个对函数参数进行操作的宏。我通过将参数设置为宏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”的成员,这也是一种方法,但我遇到了擦除问题,并且还担心重载方法等等。我目前正在努力解决的问题是为Function0
到Function22
创建23个宏,但是对于男孩我并不满意。我可以采取另一种方法吗?
答案 0 :(得分:2)
您可能必须接受Any
(或未绑定的通用)作为您的宏参数类型,然后在您的宏内手动检查该参数实际上是Function0
到Function22
之一。不过,这并不是那么乏味:
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)
此宏只会针对函数进行扩展,如果参数不是函数,则会因编译时隐式未找到错误而失败。通过使用宏,您可以通过函数类型的弱类型标记反射性地获取有关类型的信息。