Supposons que nous voulions écrire une macro qui définit une classe anonyme avec un certain type de membres ou de ses méthodes et crée une instance de cette classe qui est statiquement typé comme un type de construction avec ces méthodes, etc. C'est possible avec le système de macro dans 2.10.0, et le type de membre partie est extrêmement facile:
object MacroExample extends ReflectionUtils {
import scala.language.experimental.macros
import scala.reflect.macros.Context
def foo(name: String): Any = macro foo_impl
def foo_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
}
(Où ReflectionUtils
est une commodité trait de caractère qui m' constructor
méthode.)
Cette macro permet de préciser le nom de la classe anonyme de type militaire, comme une chaîne de caractères littérale:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Notez qu'il est correctement saisi. Nous pouvons confirmer que tout fonctionne comme prévu:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Maintenant, supposons que nous essayons de faire la même chose avec une méthode:
def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(Flag.FINAL), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
DefDef(
Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
c.literal(42).tree
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
))
}
Mais quand nous essayons, nous ne sommes pas un type de structure:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Mais si nous nous en tenons un supplément de classe anonyme là:
def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(lit: String)) = name.tree
val anon = newTypeName(c.fresh)
val wrapper = newTypeName(c.fresh)
c.Expr(Block(
ClassDef(
Modifiers(), anon, Nil, Template(
Nil, emptyValDef, List(
constructor(c.universe),
DefDef(
Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
c.literal(42).tree
)
)
)
),
ClassDef(
Modifiers(Flag.FINAL), wrapper, Nil,
Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
),
Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
))
}
Il fonctionne:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
C'est très pratique,-il vous permet de faire des choses comme cela, par exemple-mais je ne comprends pas pourquoi il fonctionne, et le type de membre version fonctionne, mais pas bar
. Je sais que cela ne peut pas être définie comportement, mais sera-t-elle un sens? Est-il le moyen le plus propre à obtenir un type de structure (les méthodes) à partir d'une macro?