Supongamos que deseamos escribir una macro que defina una clase anónima con algunos miembros de tipo o métodos, y luego cree una instancia de esa clase que esté tipada estáticamente como un tipo estructural con esos métodos, etc. Esto es posible con el sistema de macros en 2.10. 0, y la parte del miembro tipo es extremadamente fácil:
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)
))
}
}
(¿Dónde ReflectionUtils
hay un rasgo de conveniencia que proporciona mi constructor
método?)
Esta macro nos permite especificar el nombre del miembro de tipo de la clase anónima como un literal de cadena:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Tenga en cuenta que está escrito correctamente. Podemos confirmar que todo funciona como se esperaba:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Ahora supongamos que intentamos hacer lo mismo con un método:
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)
))
}
Pero cuando lo probamos, no obtenemos un tipo estructural:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Pero si colocamos una clase anónima adicional allí:
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)
))
}
Funciona:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
Esto es extremadamente útil, te permite hacer cosas como esta , por ejemplo, pero no entiendo por qué funciona, y la versión del miembro tipo funciona, pero no bar
. Sé que esto puede no ser un comportamiento definido , pero ¿tiene algún sentido? ¿Hay una forma más limpia de obtener un tipo estructural (con los métodos en él) de una macro?
fuente
Respuestas:
Esta pregunta es respondida por duplicado por Travis aquí . Hay enlaces al tema en el rastreador y a la discusión de Eugene (en los comentarios y la lista de correo).
En la famosa sección "Skylla and Charybdis" del verificador de tipos, nuestro héroe decide qué escapará al oscuro anonimato y verá la luz como un miembro del tipo estructural.
Hay un par de formas de engañar al verificador de tipos (que no implican la estratagema de Odiseo de abrazar a una oveja). Lo más simple es insertar una declaración ficticia para que el bloque no se vea como una clase anónima seguida de su instanciación.
Si el usuario nota que usted es un término público al que no hace referencia el exterior, lo hará privado.
fuente
new $anon {}
. Mi otra conclusión es que en el futuro no loanon
usaré en macros con cuasiquotes o nombres especiales similares.shapeless.Generic
? A pesar de mis mejores intenciones de forzarAux
tipos de retorno de patrones, el compilador se niega a ver a través del tipo estructural.