首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在reify子句中使用Scala Macro中计算的类型?

如何在reify子句中使用Scala Macro中计算的类型?
EN

Stack Overflow用户
提问于 2012-12-10 13:27:12
回答 2查看 3.5K关注 0票数 13

我一直在使用Scala宏并在宏中包含以下代码:

代码语言:javascript
复制
    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子句中工作吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-12-10 19:09:34

问题是,您传递给reify的代码本质上是逐字放置在宏要展开的位置,而fieldMemberType在那里没有任何意义。

在某些情况下,可以使用splice将宏展开时的表达式偷偷放入要具体化的代码中。例如,如果我们试图创建此特征的一个实例:

代码语言:javascript
复制
trait Foo { def i: Int }

并且在宏展开时有这个变量:

代码语言:javascript
复制
val myInt = 10

我们可以编写以下代码:

代码语言:javascript
复制
reify { new Foo { def i = c.literal(myInt).splice } }

这在这里是行不通的,这意味着你将不得不忘记漂亮的小reify,并手动写出它。不幸的是,你会发现这种情况经常发生。我的标准方法是启动一个新的REPL,并键入如下内容:

代码语言:javascript
复制
import scala.reflect.runtime.universe._

trait TypeBuilder { type fieldType }

showRaw(reify(new TypeBuilder { type fieldType = String }))

这将输出几行AST,然后您可以将其剪切并粘贴到宏定义中作为起点。然后你摆弄它,替换成这样的东西:

代码语言:javascript
复制
Ident(TypeBuilder)

有了这个:

代码语言:javascript
复制
Ident(newTypeName("TypeBuilder"))

以及使用Flag.FINALFINAL,等等。我希望AST类型的toString方法能更准确地对应于构建它们所需的代码,但您很快就会明白需要更改什么。你最终会得到类似这样的结果:

代码语言:javascript
复制
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的东西中,我们可以编写以下代码:

代码语言:javascript
复制
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1@fb3f1f3

所以它是有效的。我们获取了一个c.universe.Type (我在这里从builderWithType上的类型参数的WeakTypeTag中获得,但它的工作方式与任何旧的Type完全相同),并使用它来定义TypeBuilder特征的类型成员。

票数 25
EN

Stack Overflow用户

发布于 2012-12-11 07:03:48

对于您的用例,有一种比树编写更简单的方法。事实上,我一直在使用它来阻止树木,因为使用树木进行编程真的很困难。我更喜欢计算类型并使用reify来生成树。这使得宏更加健壮和“卫生”,并且减少了编译时的错误。使用树的IMO必须是最后的手段,仅在少数情况下,例如树转换或对元组等类型族的泛型编程。

这里的技巧是定义一个函数,将其作为类型参数,您想要在reify主体中使用的类型,并在WeakTypeTag上绑定上下文。然后,通过显式传递可以通过上下文WeakTypeTag方法从语义层类型构建的WeakTypeTags来调用此函数。

因此,在您的情况下,这将提供以下内容。

代码语言:javascript
复制
  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))
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13795490

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档