问题是您传递给的代码reify
基本上将逐字放置在宏被扩展的位置,并且在fieldMemberType
那里没有任何意义。
在某些情况下,您可以使用splice
将宏扩展时的表达式偷偷带入您正在具体化的代码中。例如,如果我们试图创建这个 trait 的一个实例:
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"))
和FINAL
,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)
)
)
anon
您预先为匿名类创建的类型名称在哪里,并且constructor
是我用来使这种事情不那么可怕的一种方便方法(您可以在这个完整的工作示例的末尾找到它的定义)。
现在,如果我们将这个表达式包装成这样的东西,我们可以编写以下内容:
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1@fb3f1f3
所以它有效。我们采用了一个c.universe.Type
(我从WeakTypeTag
on 的类型参数中得到builderWithType
它,但它与任何 old 的工作方式完全相同)并用它来定义我们特征Type
的类型成员。TypeBuilder