我一直在使用Scala宏并在宏中包含以下代码:
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子句中工作吗?
发布于 2012-12-10 19:09:34
问题是,您传递给reify的代码本质上是逐字放置在宏要展开的位置,而fieldMemberType在那里没有任何意义。
在某些情况下,可以使用splice将宏展开时的表达式偷偷放入要具体化的代码中。例如,如果我们试图创建此特征的一个实例:
trait Foo { def i: Int }并且在宏展开时有这个变量:
val myInt = 10我们可以编写以下代码:
reify { new Foo { def i = c.literal(myInt).splice } }这在这里是行不通的,这意味着你将不得不忘记漂亮的小reify,并手动写出它。不幸的是,你会发现这种情况经常发生。我的标准方法是启动一个新的REPL,并键入如下内容:
import scala.reflect.runtime.universe._
trait TypeBuilder { type fieldType }
showRaw(reify(new TypeBuilder { type fieldType = String }))这将输出几行AST,然后您可以将其剪切并粘贴到宏定义中作为起点。然后你摆弄它,替换成这样的东西:
Ident(TypeBuilder)有了这个:
Ident(newTypeName("TypeBuilder"))以及使用Flag.FINAL的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 (我在这里从builderWithType上的类型参数的WeakTypeTag中获得,但它的工作方式与任何旧的Type完全相同),并使用它来定义TypeBuilder特征的类型成员。
发布于 2012-12-11 07:03:48
对于您的用例,有一种比树编写更简单的方法。事实上,我一直在使用它来阻止树木,因为使用树木进行编程真的很困难。我更喜欢计算类型并使用reify来生成树。这使得宏更加健壮和“卫生”,并且减少了编译时的错误。使用树的IMO必须是最后的手段,仅在少数情况下,例如树转换或对元组等类型族的泛型编程。
这里的技巧是定义一个函数,将其作为类型参数,您想要在reify主体中使用的类型,并在WeakTypeTag上绑定上下文。然后,通过显式传递可以通过上下文WeakTypeTag方法从语义层类型构建的WeakTypeTags来调用此函数。
因此,在您的情况下,这将提供以下内容。
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))https://stackoverflow.com/questions/13795490
复制相似问题