2013-09-01 80 views
8

Quasiquotes是驚人的 - 他們使Scala編寫的宏非常痛苦,而且根據我的經驗,他們幾乎總是像我所期望的那樣工作。最重要的是,它們現在可用於Scala 2.10中的as a pluginQuasiquotes多參數和參數列表

這個問題是關於我寫入this blog post時遇到的一個小問題。當我能夠找到幾分鐘時,它就在我的清單上,但我想我會在這裏發佈它,以防其他人能夠打敗我,並幫助其他遇到同樣問題的人。

假設我有名字型對列表的列表:

val pss = List(
    List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]), 
    List(newTermName("z") -> typeOf[String]) 
) 

我希望把這些成樹,看起來像這樣:

def foo(x: Int, y: Char)(z: String) = ??? 

下面的作品就好了:

q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???" 

也就是說,它構建了以下三種:

def bar(x: Int) = ??? 

這表明,我應該能夠編寫這樣的事:

val quoted = pss.map(_.map { case (n, t) => q"$n: $t" }) 

q"def foo..${quoted.map(ps => q"($ps)")} = 1" 

或者多一點簡單的,具有多種參數一個參數列表:

q"def baz(..${quoted.head}) = ???" 

無論作品 - 我得到這樣的錯誤:

<console>:28: error: type mismatch; 
found : List[c.universe.Typed] 
required: List[c.universe.ValDef] 
      q"def baz(..${quoted.head}) = ???" 
           ^

夠公平 - 我可以se e它如何看待分析器,就像我在構建輸入表達式而不是在quoted中定義參數一樣。沒有任何我能想到的顯而易見的事情能夠奏效(添加= _,明確地將quasiquote鍵入爲ValDef等)。

我知道,我可以建立手動參數定義:

val valDefs = pss.map(
    _.map { 
    case (n, t) => ValDef(Modifiers(Flag.PARAM), n, TypeTree(t), EmptyTree) 
    } 
) 

而且現在baz版(帶一個參數列表)的工作原理:

q"def baz(..${valDefs.head}) = ???" 

但不是foo版本(一個與多個參數列表)。

所以這裏有兩個問題。首先,如何使用quasiquotes將一個名稱類型變成參數ValDef以外的引用參數列表的上下文?第二,如何將參數定義列表轉換​​爲多個參數列表?

對於整個該死的事情來說,回到手工AST結構很容易(例如參見my post),但我希望能夠使用quasiquotes。

+2

1)我相信Q 「VAL $ N:$ T」 將工作2)使用... $拼接名單 –

+0

名單'val'在這裏完美的工作 - 謝謝! '... $引用'不會,但我猜是因爲內部列表不會自動提升到參數列表? –

回答

4

這裏是快速的解決問題的方法:

val pss = List(
    List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]), 
    List(newTermName("z") -> typeOf[String]) 
) 
val vparamss: List[List[ValDef]] = pss.map { _.map { case (name, tpe) => q"val $name: $tpe" } } 
q"def foo(...$vparamss)" 

正如你所看到的特殊... $拼接讓你定義一個函數有多個參數列表。函數參數本身以與常規值相同的方式表示。

另一個例子... $它可能是有用的:

val xy = List(List(q"x"), List(q"y")) 
q"f(...$xy)" // same as q"f(x)(y)" 
+0

完美地工作,謝謝!我沒有想過嘗試把參數列表放在圓括號中,但是我可以看到在這裏使用它作爲語法是有意義的。 –