2013-07-15 6 views
7

C'è un modo per restituire uno List di TypeSymbol s per ogni classe in un pacchetto utilizzando le macro?Scala macro: ottenere un elenco di TypeSymbols da utilizzare in fase di esecuzione

Quello che sto cercando di realizzare è quello di scrivere una macro che dà fuori qualcosa di equivalente a questo elenco:

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) 

Qui è la mia messa a punto:

Ho un pacchetto chiamato foo, in base al quale questi sono definito:

trait FooTrait 

case class Bar() extends FooTrait 

case class Bar() extends FooTrait 

Ecco la mia macro che ottiene tutti i simboli di tipo per le classi sotto foo che si estendono 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)) 
} 

I primi println stampa:

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

la seconda stampa:

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

Ma ottengo questo messaggio di errore:

[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. 

Cosa devo fare per fare questo lavoro? Sospetto di dover scrivere qualcos'altro invece di Ident, ma non riuscivo a capire cosa.

Uso di Scala 2.10.2.

Grazie in anticipo!

risposta

7

bisogna usare reifyType per creare artefatti riflessione nell'universo runtime:

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

Questo vi darà un elenco di tag di tipo, non simboli, ma si può abbastanza facilmente ottenere i simboli, o come questo :

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

Oppure nella stessa macro.

+0

Grazie ancora Travis! Li ho convertiti in 'TypeSymbol's nella macro. Ho fatto un 'Map' dei simboli ai loro campi' val' dichiarati nelle classi case, ma sto ricevendo questo errore mentre provavo ad accedervi: http://pastebin.com/8dHDRMYy Dovrei rilassare il inserisci i requisiti e vai per "Any" come hai fatto tu? Inoltre, come dovrei farlo per MethodSymbols? C'è un modo? – Emre