假设我们要编写一个宏来定义一个具有一些类型成员或方法的匿名类,然后创建该类的一个实例,该类的静态类型为具有这些方法的结构类型,等等。这在 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在哪里。)
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。我知道这可能不是定义的行为,但它有什么意义吗?有没有一种更简洁的方法可以从宏中获取结构类型(及其上的方法)?
bar
在类型检查器著名的“Skylla and Charybdis”部分中,我们的英雄决定什么应该摆脱黑暗的匿名性并将光明视为结构类型的成员。
有几种方法可以欺骗类型检查器(这不涉及 Odysseus 的拥抱绵羊的策略)。最简单的方法是插入一个虚拟语句,以使该块看起来不像是一个匿名类,然后是它的实例化。
如果打字员注意到您是一个没有被外部引用的公共术语,它会将您设为私有。
object Mac { import scala.language.experimental.macros import scala.reflect.macros.Context /* Make an instance of a structural type with the named member. */ def bar(name: String): Any = macro bar_impl def bar_impl(c: Context)(name: c.Expr[String]) = { import c.universe._ val anon = TypeName(c.freshName) // next week, val q"${s: String}" = name.tree val Literal(Constant(s: String)) = name.tree val A = TermName(s) val dmmy = TermName(c.freshName) val tree = q""" class $anon { def $A(i: Int): Int = 2 * i } val $dmmy = 0 new $anon """ // other ploys //(new $anon).asInstanceOf[{ def $A(i: Int): Int }] // reference the member //val res = new $anon //val $dmmy = res.$A _ //res // the canonical ploy //new $anon { } // braces required c.Expr(tree) } }