2017-01-04 4 views
1

Ich versuche, ein Makro zu schreiben, das eine bestimmte Instanz einer Fallklasse in eine Zeichenfolgendarstellung erweitert. Z.B. case class Foo(a: Int); Foo(1) würde a -> 1 werden.Referenzfeld des Objekts mit Zeichenfolgenwert scala Makro

Also habe ich eine Typenklasse geschrieben, um mir die Feldnamen von einem formlosen LabelledGeneric namens FieldList zu geben. Ich gebe die LabelledGeneric- und FieldList-Instanzen an mein Makro weiter, und ich kann das Makro leicht dazu bringen, eine Liste aller Felder zu erzeugen. Ich bin mir jedoch nicht sicher, wie ich die Zeichenfolge, die das Feld darstellt, verwenden kann, um über das Objekt in meinem Makrokörper auf das Feld zuzugreifen. Hier ist der Makrocode:

import FieldList._ 
import shapeless.{HList, LabelledGeneric} 
import scala.language.experimental.macros 
import scala.reflect.macros.blackbox 

object Foo { 

    def foo_impl[T, L <: HList](c: blackbox.Context) 
           (t: c.Expr[T]) 
           (gen: c.Expr[LabelledGeneric.Aux[T, L]], 
            fl: c.Expr[FieldList[L]]): c.Expr[String] = { 
    import c.universe._ 
    reify { 
     val sb = new StringBuilder 
     val obj = t.splice 
     val generic = gen.splice 
     val fieldList = fl.splice 
     // T.fieldList returns a List[String] of the class' fields. 
     obj.fieldList(generic, fieldList).foldLeft(sb) { case (builder, next) => 
     builder.append(next) 
     builder.append(" -> ") 
     builder.append() // How to get the value of obj.$next? 
     }.toString() 
    } 
    } 

    def foo[T, L <: HList](t: T)(implicit gen: LabelledGeneric.Aux[T, L], fl: FieldList[L]): String = macro foo_impl[T, L] 

} 

Die Linie, wo ich bin nicht sicher, was zu tun ist, die dritte Linie meiner Falte mit dem Kommentar.

Ich bin sicher, was ich versuche zu tun ist mit schlicht formlos möglich, aber ich versuche, Makros zu lernen. Ich habe Quasiquotes angeschaut, die aussehen, als könnten sie dieses Verhalten unterstützen, aber es sieht so aus, als müsste ich zwischen reify und quasiquotes wählen und AFAICT kann nur auf die Werte eines Expr innerhalb eines retify Blocks zugreifen (Quasiquotes funktionieren also nicht?) .

Antwort

1

Spleißen ist mit Quasiquoten durchaus möglich. Nur Sie spleißen ein Tree, nicht ein Expr (Sie können von einem Expr zu einem Tree mit expr.tree gehen). Hier ist eine Lösung mit minimalen Änderungen an Ihrem Code:

import FieldList._ 
import shapeless.{HList, LabelledGeneric} 
import scala.language.experimental.macros 
import scala.reflect.macros.blackbox 

object Foo { 

    def foo_impl[T, L <: HList](c: blackbox.Context) 
           (t: c.Tree) 
           (gen: c.Tree, 
            fl: c.Tree): c.Tree = { 
    import c.universe._ 
    val q"$_(..$args)" = t 
    q""" 
     val sb = new _root_.scala.StringBuilder 
     val obj = $t 
     val generic = $gen 
     val fieldList = $fl 
     val argsList = _root_.scala.List(..$args) 
     // T.fieldList returns a List[String] of the class' fields. 
     obj.fieldList(generic, fieldList) 
     .zip(argsList) 
     .foldLeft(sb) { case (builder, (next, value)) => 
     builder.append(next) 
     builder.append(" -> ") 
     builder.append(value) 
     }.toString() 
    """ 
    } 

    def foo[T, L <: HList](t: T)(implicit gen: LabelledGeneric.Aux[T, L], fl: FieldList[L]): String = macro foo_impl[T, L] 

} 

Sie beachten Sie, dass ich das nicht kompiliert oder getestet, da ich nicht den Rest des Codes haben, auf dem dieser abhängt. Und wenn das funktioniert, wird es wahrscheinlich nicht für komplexere Fälle wie foo(Foo(b = "bar", a = 1)) funktionieren. Aber es könnte Ihnen eine allgemeine Vorstellung davon geben, wie man mit Quasiquoten arbeitet.

+0

Großartig, danke Jasper. Ich werde es gleich versuchen. – rahilb

+0

@rahilb Das hat übrigens bei mir funktioniert, als ich einen Dummy 'FieldList' benutzt habe. –

+0

Danke Jasper: Ich werde deine Antwort akzeptieren, aber mir wurde klar, dass ich tatsächlich die falsche Frage für das gestellt habe, was ich zu tun versuchte! Für meinen Zweck muss ich die formlosen Typen-Tags aus dem Eingabe-Baum extrahieren, da ich zur Kompilierzeit gerne die Feldnamen wissen möchte (ich kämpfe immer noch damit, aber das ist eine andere Geschichte :)). Danke noch einmal. – rahilb

Verwandte Themen