2013-09-01 11 views
8

Quasiquotes sono sorprendenti: rendono estremamente difficile la scrittura di macro in Scala, e nella mia esperienza funzionano quasi sempre esattamente come mi aspetterei. E soprattutto, ora sono disponibili as a plugin in Scala 2.10.Quasiquote per più parametri e liste di parametri

Questa domanda riguarda un piccolo problema che ho riscontrato durante la scrittura di this blog post. È sulla mia lista di cose da esaminare quando riesco a trovare qualche minuto, ma ho pensato di postarlo qui nel caso in cui qualcun altro mi potesse battere e per aiutare altre persone che si imbattono nella stessa domanda.

Supponiamo che io ho una lista di liste di coppie nome-tipo:

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

Voglio trasformare questi in un albero che assomiglia a questo:

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

I seguenti funziona bene:

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

Cioè, si costruisce la seguente struttura:

def bar(x: Int) = ??? 

il che suggerisce che dovrei essere in grado di scrivere qualcosa del genere:

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

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

O un po 'più semplice, con parametri multipli in un unico elenco di parametri:

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

Né opere -Vengo errori come questo:

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

Abbastanza Giusto-posso vedere e come sembrerebbe al quasiquoter come sto costruendo espressioni tipizzate piuttosto che definire parametri in quoted. Nessuna delle cose ovvie che potrei pensare di provare ha funzionato (aggiungendo = _, digitando esplicitamente la quasiquote come ValDef, ecc.).

so che posso costruire le definizioni dei parametri manualmente:

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

E ora la baz versione (con una lista di parametri) Works:

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

Ma non la versione foo (quella con più liste di parametri).

Quindi ci sono due domande qui. Innanzitutto, come posso usare quasiquotes per trasformare una coppia nome-tipo in un parametro ValDef al di fuori del contesto di un elenco di parametri quotati? E in secondo luogo, come posso trasformare un elenco di liste di definizioni di parametri in più liste di parametri?

È abbastanza facile ricorrere alla costruzione AST manuale per l'intera dannata cosa (vedere my post per un esempio), ma mi piacerebbe poter usare invece quasiquotes.

+2

1) Credo q "val $ n: $ t" funzionerà 2) Utilizzare ... $ impiombare liste di liste –

+0

'val' funziona perfettamente qui-grazie! "... $ quoted', tuttavia, non credo, perché le liste interne non vengono automaticamente aggiunte agli elenchi di parametri? –

risposta

4

Ecco la rapida soluzione al vostro problema:

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)" 

Come si può vedere speciale ... $ giunzione consente di definire una funzione con più liste di argomenti. Gli stessi argomenti delle funzioni sono rappresentati nello stesso modo dei valori regolari.

Un altro esempio in cui ... $ potrebbe essere utile:

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

Funziona perfettamente, grazie! Non ho pensato di provare a mettere l'elenco degli elenchi di parametri tra parentesi, ma posso vedere che ha senso utilizzarlo come la sintassi qui. –