问题是你传递给的代码reify
本质上将逐字放置在宏展开的位置,并且fieldMemberType
在那里没有任何意义。
在某些情况下你可以使用splice
将宏扩展时的表达式隐藏到正在具体化的代码中。例如,如果我们尝试创建此特征的实例:
trait Foo { def i: Int }
并且在宏展开时有这个变量:
val myInt = 10
我们可以这样写:
reify { new Foo { def i = c.literal(myInt).splice } }
这在这里不起作用,这意味着你将不得不忘记漂亮的小东西reify
并手写出 AST。不幸的是,你会发现这种情况经常发生。我的标准方法是启动一个新的 REPL 并输入如下内容:
import scala.reflect.runtime.universe._
trait TypeBuilder { type fieldType }
showRaw(reify(new TypeBuilder { type fieldType = String }))
这将输出几行 AST,然后您可以将其剪切并粘贴到宏定义中作为起点。然后你摆弄它,替换如下内容:
Ident(TypeBuilder)
有了这个:
Ident(newTypeName("TypeBuilder"))
And FINAL
with Flag.FINAL
, 等等。我希望toString
AST 类型的方法与构建它们所需的代码更准确地对应,但您很快就会明白需要更改哪些内容。你最终会得到这样的结果:
c.Expr(
Block(
ClassDef(
Modifiers(Flag.FINAL),
anon,
Nil,
Template(
Ident(newTypeName("TypeBuilder")) :: Nil,
emptyValDef,
List(
constructor(c),
TypeDef(
Modifiers(),
newTypeName("fieldType"),
Nil,
TypeTree(fieldMemberType)
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
)
)
Where anon
是您预先为匿名类创建的类型名称,并且constructor
是一种方便的方法,我用它来使这种事情不那么可怕(你可以在末尾找到它的定义)这个完整的工作示例 https://gist.github.com/4249936).
现在如果我们把这个表达式包装成类似的东西this https://gist.github.com/4249936,我们可以写如下:
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1@fb3f1f3
所以它有效。我们采取了一个c.universe.Type
(我从这里得到的WeakTypeTag
类型参数的builderWithType
,但它的工作方式与任何旧的完全相同Type
)并用它来定义我们的类型成员TypeBuilder
trait.