2014-02-24 147 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

當然。未分化的上下文更被棄用。 –