2014-02-24 109 views
3

我想编写一个宏,其中返回类型取决于参数。简单示例:宏返回类型取决于参数

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?的回答中一样,但这不是我想要的。

回答

7

是的,这是可能的,这要归功于whitebox macros的魔力:您可以告诉编译器返回类型为c.Expr[Any],它会推断出更精确的类型。

此行为shocked me when I first ran into it - 它非常非常强大,非常非常可怕 - 但它绝对是有意的,并且将继续得到支持,尽管2.11会区分白盒和黑盒宏,而前者很可能会长时间处于实验状态(如果他们永远离开它)。

例如,以下是你问什么的速写(我使用quasiquotes这里通过macro paradise plugin为2.10,但它只会是一个小更详细的无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")的内容,但这基本上只是文档 - 对于在这种情况下如何推断宏的返回类型没有任何影响。

+1

在2.11中,'警告:宏定义必须明确指定返回类型(推断任何来自宏的impl的c.Expr [Any]已被弃用,并将在2.12中停止工作)' –

+0

您是否正在使用上下文'白盒'套餐? –

+0

当然。未分化的上下文更被弃用。 –