我一直在使用Scala Macros并在宏中使用以下代码:
val fieldMemberType = fieldMember.typeSignatureIn(objectType) match {
case NullaryMethodType(tpe) => tpe
case _ => doesntCompile(s"$propertyName isn't a field, it must be another thing")
}
reify{
new TypeBuilder() {
type fieldType = fieldMemberType.type
}
}
如您所见,我设法获得c.universe.Type fieldMemberType
。这表示对象中某个字段的类型。一旦我得到了,我想在reify中创建一个新的TypeBuilder
对象。 TypeBuilder
是一个带抽象参数的抽象类。此抽象参数为fieldType
。我希望这个fieldType
成为我之前找到的类型。
运行此处显示的代码会返回fieldMemberType not found
。有什么方法可以让fieldMemberType
在reify子句中工作吗?
答案 0 :(得分:24)
问题在于,传递给reify
的代码实际上是逐字放置在扩展宏的位置,而fieldMemberType
并不代表任何东西。< / p>
在某些情况下,您可以使用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"))
FINAL
与Flag.FINAL
,依此类推。我希望AST类型的toString
方法更准确地与构建它们所需的代码相对应,但是您很快就会了解需要更改的内容。你最终会得到这样的东西:
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
是一种方便的方法,我用来使这种事情变得不那么可怕(你可以找到)它在this complete working example)末尾的定义。
现在,如果我们将此表达式包含在this之类的内容中,我们可以编写以下内容:
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1@fb3f1f3
所以它有效。我们采用了c.universe.Type
(我从WeakTypeTag
上的类型参数的builderWithType
处获得了Type
,但它与旧的TypeBuilder
完全相同)并用它来定义{{1}}特征的类型成员。
答案 1 :(得分:6)
对于您的用例,有一种比树编写更简单的方法。事实上,我一直用它来遮挡树木,因为用树木编程真的很困难。我更喜欢计算类型并使用reify来生成树。这使得更加健壮和卫生&#34;宏和较少的编译时错误。使用树的IMO必须是最后的手段,仅适用于少数情况,例如树变换或类型系列的通用编程,如元组。
这里的技巧是定义一个函数作为类型参数,你想在reify体中使用的类型,以及WeakTypeTag上的上下文绑定。然后通过显式传递可以从Universe类型构建的WeakTypeTags来调用此函数,这要归功于WeakTypeTag方法的上下文。
因此,在您的情况下,这将提供以下内容。
val fieldMemberType: Type = fieldMember.typeSignatureIn(objectType) match {
case NullaryMethodType(tpe) => tpe
case _ => doesntCompile(s"$propertyName isn't a field, it must be another thing")
}
def genRes[T: WeakTypeTag] = reify{
new TypeBuilder() {
type fieldType = T
}
}
genRes(c.WeakTypeTag(fieldMemberType))