2013-09-01 12 views
8

Quasiquotes sind erstaunlich - sie machen das Schreiben von Makros in Scala sehr viel weniger schmerzhaft, und meiner Erfahrung nach funktionieren sie fast immer genau so, wie ich es erwarten würde. Und das Beste von allem, sie sind jetzt in Scala 2.10 as a plugin verfügbar.Quasiquotes für mehrere Parameter und Parameterlisten

Diese Frage bezieht sich auf ein kleines Problem, das ich beim Schreiben this blog post stieß. Es steht auf meiner Liste, auf die ich achten muss, wenn ich ein paar Minuten finde, aber ich dachte mir, ich würde es hier posten, falls jemand anderes mich dazu bringen könnte und anderen Leuten zu helfen, die die gleiche Frage haben.

Angenommen habe ich eine Liste von Listen von Namen-Typ-Paare:

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

Ich möchte diese in einen Baum verwandeln, die wie folgt aussieht:

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

Die folgenden funktioniert gut:

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

das heißt, es baut den folgenden Baum:

def bar(x: Int) = ??? 

was darauf schließen lässt, dass ich in der Lage sein sollte, so etwas zu schreiben:

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

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

Oder ein wenig einfacher, mit mehreren Parametern in einer einzigen Parameterliste:

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

Weder Werke -Ich bekomme Fehler wie folgt:

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

Fair genug-ich kann se e, wie es zum Quasiquoter aussehen würde, als ob ich getippte Ausdrücke eher baue, als Parameter in quoted zu definieren. Keines der offensichtlichen Dinge, die ich mir vorstellen konnte, funktionierte (Hinzufügen = _, ausdrückliche Eingabe der Quasiquote als ValDef, etc.).

Ich weiß, dass ich manuell die Parameterdefinitionen aufbauen kann:

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

Und nun die baz Version (mit einer Parameterliste) funktioniert:

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

Aber nicht die foo Version (die mit mehreren Parameterlisten).

So gibt es zwei Fragen hier. Erstens, wie kann ich Quasiquotes verwenden, um ein Name-Typ-Paar in einen Parameter ValDef außerhalb des Kontexts einer in Anführungszeichen gesetzten Parameterliste umzuwandeln? Und zweitens, wie kann ich eine Liste von Listen mit Parameterdefinitionen in mehrere Parameterlisten umwandeln?

Es ist einfach genug, auf die manuelle AST-Konstruktion für das ganze verdammte Ding zurückzugreifen (siehe my post für ein Beispiel), aber ich möchte stattdessen Quasiquotes verwenden können.

+2

1) Ich glaube, q "val $ n: $ t" wird 2 arbeiten) Verwenden Sie ... $ Listen von Listen spleißbar –

+0

"val" funktioniert hier perfekt - danke! '... $ quoted' tut das aber nicht, da die inneren Listen nicht automatisch in Parameterlisten übernommen werden? –

Antwort

4

Hier ist die schnelle Lösung für Ihr Problem:

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

Wie Sie sehen können, besondere ... $ Spleiß können Sie eine Funktion mit mehreren Argumentlisten definieren. Funktionsargumente selbst werden auf die gleiche Weise wie reguläre Werte dargestellt.

Ein anderes Beispiel, wo ... $ könnte es nützlich sein:

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

Funktioniert perfekt, danke! Ich habe nicht daran gedacht, die Liste der Parameterlisten in Klammern zu setzen, aber ich sehe, dass es sinnvoll ist, das hier als Syntax zu verwenden. –

Verwandte Themen