最佳答案
假设我们想编写一个宏来定义一个具有某些类型成员或方法的匿名类,然后创建一个具有这些方法的静态类型作为结构类型的类的实例,等等。这在2.10.0的宏系统中是可行的,而且类型成员部分非常简单:
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)
))
}
}
(其中 ReflectionUtils
是提供我的 constructor
方法的 方便的特点。)
此宏允许我们将匿名类的类型成员的名称指定为字符串文本:
scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6
注意,它的类型是正确的。我们可以确认一切正常:
scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>
现在假设我们尝试用一个方法做同样的事情:
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)
))
}
但是当我们尝试的时候,我们得不到一个结构类型:
scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492
但如果我们再加一个匿名类:
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)
))
}
它是有效的:
scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834
scala> res0.test
res1: Int = 42
这非常方便ーー例如,它允许您执行 这个之类的操作ーー但是我不明白它为什么可以工作,而且类型成员版本可以工作,但是 bar
不可以。我知道这个 可能不是定义好的行为,但它有意义吗?有没有一种更简洁的方法从宏获得结构类型(上面有方法) ?