Suponha que desejemos escrever uma macro que defina uma classe anônima com alguns membros ou métodos de tipo e, em seguida, crie uma instância dessa classe que seja estaticamente digitada como um tipo estrutural com esses métodos, etc. Isso é possível com o sistema de macro na versão 2.10. 0 e a parte do membro de tipo é extremamente 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)
))
}
}
(Onde ReflectionUtils
está um traço de conveniência que fornece meu constructor
método.)
Essa macro nos permite especificar o nome do membro do tipo da classe anônima como uma literal de cadeia de caracteres:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
Observe que foi digitado adequadamente. Podemos confirmar que tudo está funcionando como esperado:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
Agora, suponha que tentemos fazer a mesma coisa com um 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)
))
}
Mas quando tentamos, não temos um tipo estrutural:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
Mas se colocarmos uma classe anônima extra 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)
))
}
Funciona:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
Isso é extremamente útil - permite fazer coisas como essa , por exemplo - mas não entendo por que funciona e a versão do membro de tipo funciona, mas não bar
. Eu sei que isso pode não ser um comportamento definido , mas isso faz algum sentido? Existe uma maneira mais limpa de obter um tipo estrutural (com os métodos nele) a partir de uma macro?
fonte
Respostas:
Esta pergunta é respondida em duplicado por Travis aqui . Existem links para o problema no rastreador e na discussão de Eugene (nos comentários e na lista de discussão).
Na famosa seção "Skylla e Charybdis" do verificador de tipos, nosso herói decide o que deve escapar do anonimato sombrio e vê a luz como um membro do tipo estrutural.
Existem algumas maneiras de enganar o verificador de tipos (que não implica a estratégia de Odisseu de abraçar uma ovelha). O mais simples é inserir uma instrução fictícia para que o bloco não pareça uma classe anônima seguida por sua instanciação.
Se o digitador perceber que você é um termo público que não é referenciado por fora, ele o tornará privado.
fonte
new $anon {}
. Minha outraanon
sugestão é que, no futuro, não usarei macros com quasiquotes ou nomes especiais semelhantes.shapeless.Generic
? Apesar das minhas melhores intenções de forçar osAux
tipos de retorno de padrão, o compilador se recusa a ver através do tipo estrutural.