2013-09-01 17 views
8

Quasiquotes są niesamowite - sprawiają, że pisanie makr w Scali jest znacznie mniej bolesne, az mojego doświadczenia prawie zawsze pracują dokładnie tak, jak się spodziewam. A co najważniejsze, są teraz dostępne w Scali 2..Quasiquotes dla wielu parametrów i list parametrów

To pytanie dotyczy niewielkiego problemu, który napotkałem podczas pisania this blog post. Jest na mojej liście rzeczy do sprawdzenia, kiedy znajdę kilka minut, ale pomyślałem, że opublikuję go tutaj, na wypadek, gdyby ktoś inny mógł mnie do tego pobić, i pomóc innym, którzy prowadzą to samo pytanie.

Załóżmy, że mam listę list par nazwa-Type:

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

Chcę włączyć je do drzewa, które wygląda tak:

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

następujące prace w porządku:

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

Oznacza to, że buduje następujące drzewa:

def bar(x: Int) = ??? 

co sugeruje, że powinienem być w stanie napisać coś takiego:

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

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

lub nieco prościej, z wielu parametrów w jednym liście parametrów:

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

Ani roboty - Otrzymuję takie błędy:

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

Wystarczająco Wystarczająco - mogę se e, jak to wyglądałoby w quasiquoter, jakbym budował wyrażeń pisanych, a nie definiowanie parametrów w quoted. Żadna z oczywistych rzeczy, które mogłem wymyślić, nie działała (dodając = _, jawnie wpisując quasiquote jako ValDef itd.).

wiem, że mogę zbudować definicje parametrów ręcznie:

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

A teraz baz wersja (z jednej listy parametrów) działa:

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

ale nie wersja foo (jeden z wieloma listami parametrów).

Są więc dwa pytania. Po pierwsze, w jaki sposób mogę użyć quasiquotes do przekształcenia pary typu nazwa w parametr ValDef poza kontekstem cytowanej listy parametrów? Po drugie, jak mogę zamienić listę list definicji parametrów na wiele list parametrów?

Łatwo jest wrócić do ręcznej konstrukcji AST dla całego cholerstwa (patrz na przykład my post), ale chciałbym móc użyć quasiquotes zamiast tego.

+2

1) Wierzę q "val $ n: $ t" będzie działać 2) Użyj ... $ splatać list list –

+0

"val" działa tutaj idealnie - dzięki! '... $ quoted' nie, chociaż - zgaduję, ponieważ wewnętrzne listy nie są automatycznie przenoszone na listy parametrów? –

Odpowiedz

4

Oto szybkie rozwiązanie problemu:

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

Jak widać specjalnego ... $ splice pozwala zdefiniować funkcję z wielu list argument. Argumenty funkcji są reprezentowane w ten sam sposób, co zwykłe zmienne.

Innym przykładem gdzie ... $ to może być przydatne:

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

Działa doskonale, dzięki! Nie sądziłem, żebym spróbował umieścić listę list parametrów w nawiasach, ale widzę, że sensowne jest użycie tego jako składni tutaj. –

Powiązane problemy