我想编写一个返回类型依赖于参数的宏。简化示例:
def fun[T](methodName: String) = macro funImpl[T]
def funImpl[T: WeakTypeTag](c: Context)(methodName: c.Expr[String]): /* c.Expr[T => return type of T.methodName] */ = {
// return x => x.methodName
}
显然,funImpl
的注释掉的返回类型是非法的。我试过简单地返回Tree
,但这会产生错误:
[error] macro implementation has wrong shape:
[error] required: (c: scala.reflect.macros.Context): c.Expr[Any]
[error] found : (context: scala.reflect.macros.Context): context.Tree
[error] type mismatch for return type: c.universe.Tree does not conform to c.Expr[Any]
[error] def fun[T] = macro PrivateMethodMacro.funImpl[T]
[error] ^
是否可以像这样写一个宏?显然,如果将返回类型作为另一个类型参数传递,就像在Is it possible to write a scala macro whose returntype depends on argument?的答案中那样,但这不是我想要的。
答案 0 :(得分:8)
是的,这是可能的,这要归功于whitebox macros的神奇之处:你可以告诉编译器返回类型是c.Expr[Any]
,它会推断出更精确的类型。
这种行为shocked me when I first ran into it - 它非常,非常强大且非常非常可怕 - 但它绝对是有意的,并将继续受到支持,尽管2.11将区分whitebox和blackbox宏,而前者可能会更长时间保持实验状态(如果他们完全离开它)。
例如,以下是您要求的快速草图(我在这里通过quasiquotes使用macro paradise plugin获取2.10,但如果没有{{3}},那么它只会更加冗长quasiquotes):
import scala.language.experimental.macros
import scala.reflect.macros.Context
def funImpl[T: c.WeakTypeTag](c: Context)(
method: c.Expr[String]
): c.Expr[Any] = {
import c.universe._
val T = weakTypeOf[T]
val methodName: TermName = method.tree match {
case Literal(Constant(s: String)) => newTermName(s)
case _ => c.abort(c.enclosingPosition, "Must provide a string literal.")
}
c.Expr(q"(t: $T) => t.$methodName")
}
def fun[T](method: String) = macro funImpl[T]
然后:
scala> fun[String]("length")
res0: String => Int = <function1>
您可以看到推断类型正是您想要的,而不是Any
。您可以(也可能应该)将funImpl
的返回类型设置为c.Expr[T => Any]
并返回类似c.Expr[T => Any](q"_.$methodName")
的内容,但这基本上只是文档 - 它对返回的方式没有任何影响在这种情况下推断出宏的类型。