宏返回类型取决于参数

Ale*_*nov 3 scala scala-macros

我想写一个返回类型依赖于参数的宏.简化示例:

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
}
Run Code Online (Sandbox Code Playgroud)

显然,退出的退货类型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]                                          ^
Run Code Online (Sandbox Code Playgroud)

有可能写这样的宏吗?显然,如果将返回类型作为另一个类型参数传递,则可能会在回答中编写一个scala宏,其返回类型取决于参数?但这不是我想要的.

Tra*_*own 8

是的,这是可能的,这要归功于白盒宏的神奇之处:你可以告诉编译器返回类型是什么c.Expr[Any],它会推断出更精确的类型.

当我第一次碰到它时,这种行为让我感到震惊 - 它非常非常强大且非常非常可怕 - 但它绝对是有意的,并将继续得到支持,尽管2.11将区分whitebox和blackbox宏,而前者可能会更长时间保持实验状态(如果他们完全离开它).

例如,下面是你要求的快速草图(我在这里通过2.10 的宏天堂插件使用quasiquotes,但如果没有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]
Run Code Online (Sandbox Code Playgroud)

然后:

scala> fun[String]("length")
res0: String => Int = <function1>
Run Code Online (Sandbox Code Playgroud)

您可以看到推断类型正是您想要的,而不是Any.你可以(并且可能应该)设定的返回类型funImpl,以c.Expr[T => Any]和返回类似c.Expr[T => Any](q"_.$methodName"),但是这基本上只是文件,它并没有对如何在宏的返回类型在这种情况下推断出任何影响.