2013-07-15 10 views
7

Czy istnieje sposób na zwrócenie List z TypeSymbol s dla każdej klasy w pakiecie przy użyciu makr?Makra Scala: Uzyskiwanie listy typów symboli do użycia w czasie wykonywania

Co staram się osiągnąć to, aby napisać makro, które daje się coś równoważnego do tej listy:

scala> import scala.reflect.runtime.universe._ 
import scala.reflect.runtime.universe._ 

scala> case class MyClass1() 
defined class MyClass1 

scala> case class MyClass2() 
defined class MyClass2 

scala> val typeSymbols = List(typeOf[MyClass1].typeSymbol, typeOf[MyClass2].typeSymbol) 
typeSymbols: List[reflect.runtime.universe.Symbol] = List(class MyClass1, class MyClass2) 

Oto moja konfiguracja:

Mam pakiet o nazwie foo, w ramach którego są zdefiniowane:

trait FooTrait 

case class Bar() extends FooTrait 

case class Bar() extends FooTrait 

Oto mój makro, które pobiera wszystkie symbole typu dla klas pod foo, które rozciągają FooTrait:

def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(packageName: c.Expr[String]) = { 
    import c.universe._ 

    // Get package name from the expression tree 
    val Literal(Constant(name: String)) = packageName.tree 

    // Get all classes under given package name 
    val pkg = c.mirror.staticPackage(name) 

    // Obtain type symbols for the classes - implementation omitted 
    val types = getTypeSymbols(c.universe)(List(pkg)) 

    // Apply method for List. For easy readability in later applications 
    val listApply = Select(reify(List).tree, newTermName("apply")) 

    val result = types.map { 
    t => 
     val typeName = c.Expr[TypeSymbol](Ident(t)) 
     println(s"Typename: $typeName, $t, ${t.toType}") 

     reify(typeName.splice).tree 
    } 

    println(s"RESULT: ${showRaw(result)}") 

    c.Expr[List[reflect.runtime.universe.TypeSymbol]](Apply(listApply, result.toList)) 
} 

Pierwsze println drukuje:

Typename: Expr[c.universe.TypeSymbol](Bar), class Bar, foo.Bar 
Typename: Expr[c.universe.TypeSymbol](Baz), class Baz, foo.Baz 

drugi drukuje:

RESULT: List(Ident(foo.Bar), Ident(foo.Baz)) 

ale pojawia się ten komunikat o błędzie:

[error] no type parameters for method any2ArrowAssoc: (x: A)ArrowAssoc[A] exist so that it can be applied to arguments (<notype>) 
[error] --- because --- 
[error] argument expression's type is not compatible with formal parameter type; 
[error] found : <notype> 
[error] required: ?A 
[error] Note that <none> extends Any, not AnyRef. 
[error] Such types can participate in value classes, but instances 
[error] cannot appear in singleton types or in reference comparisons. 

Co należy zrobić, aby czy to działa? Podejrzewam, że muszę napisać coś innego niż Ident, ale nie mogłem zrozumieć co.

Używanie Scala 2.10.2.

Z góry dziękuję!

Odpowiedz

7

Musisz użyć reifyType tworzyć odbicia artefakty we wszechświecie Czas:

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object PackageMacros { 
    def allTypeSymbols[T](packageName: String) = macro allTypeSymbols_impl[T] 

    def allTypeSymbols_impl[T: c.WeakTypeTag](c: Context)(
    packageName: c.Expr[String] 
) = { 
    import c.universe._ 

    val pkg = packageName.tree match { 
     case Literal(Constant(name: String)) => c.mirror.staticPackage(name) 
    } 

    val types = pkg.typeSignature.members.collect { 
     case sym: ClassSymbol => 
     c.reifyType(treeBuild.mkRuntimeUniverseRef, EmptyTree, sym.toType) 
    }.toList 

    val listApply = Select(reify(List).tree, newTermName("apply")) 

    c.Expr[List[Any]](Apply(listApply, types)) 
    } 
} 

to daje listę tagów typu, a nie symbole, ale można dość łatwo dostać symbole, albo tak :

scala> PackageMacros.allTypeSymbols("foo").map(_.tpe.typeSymbol) foreach println 
class Baz$ 
class Bar 
class Baz 
trait FooTrait 
class Bar$ 

Lub w samym makrze.

+0

Jeszcze raz dziękuję Travis! Przekształciłem je w 'TypeSymbol's w makrze. Zrobiłem "Mapę" z symboli do pól 'val' zadeklarowanych w klasach przypadków, ale otrzymuję ten błąd podczas próby dostępu do niego: http://pastebin.com/8dHDRMYy Czy powinienem się zrelaksować? wpisz wymóg i idź na "Any" tak jak ty? Ponadto, w jaki sposób byłoby to zrobić dla MethodSymbols? Czy istnieje sposób? – Emre

Powiązane problemy