Scala 3 宏引用:何时使用 apply 与 copy

问题描述 投票:0回答:1

我正在尝试学习如何使用新的 Scala 3 Quotes API 编写宏。在此 API 中,各种 AST 元素通常定义了两种方法来引入它们:

apply
copy
,其中
copy
采用非描述性
original: Tree
name: String
TypeTree
,而
apply
则采用非描述性
Symbol
似乎还包含一个名字和
Type

ValDef
为例:

在 QuotesImpl.scala 中

object ValDef extends ValDefModule:
      def apply(symbol: Symbol, rhs: Option[Term]): ValDef =
        xCheckMacroAssert(!symbol.flags.is(Flags.Method), "expected a symbol without `Method` flag set")
        withDefaultPos(tpd.ValDef(symbol.asTerm, xCheckedMacroOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree)))

      def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef =
        tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckedMacroOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree))
// tpd.
def ValDef(sym: TermSymbol, rhs: LazyTree = EmptyTree, inferred: Boolean = false)(using Context): ValDef =
   ta.assignType(untpd.ValDef(sym.name, TypeTree(sym.info, inferred), rhs), sym)
// tpd.cpy.
def ValDef(tree: Tree)(name: TermName, tpt: Tree, rhs: LazyTree)(using Context): ValDef = 
    tree match {
        case tree: ValDef if (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree
        case _ => finalize(tree, untpd.ValDef(name, tpt, rhs)(sourceFile(tree)))
      }

我什么时候应该使用其中一种而不是另一种?我认为

copy
方法似乎没有做太多事情;名称和类型仍然可以更改。将
copy
与新元素的现有父元素(不是
ValDef
)一起使用是否合适,或者仅当我想要更改已存在于元素中的
copy
时才使用
ValDef
树上的同一个地方?

Refined
是另一个有趣的案例,因为它甚至没有
apply
方法:

trait RefinedModule { this: Refined.type =>
      def copy(original: Tree)(tpt: TypeTree, refinements: List[Definition]): Refined
scala scala-macros scala-3
1个回答
0
投票

那么答案相当简单:你从不使用

QuotesImpl
,因为这是一个内部实现细节,可以随时修改而不会发出任何警告。

Quotes
是唯一的公共API,它只有
apply
unapply

不久前,ZIO 开发人员很恼火,因为他们从宏中调用内部/私有 API,并且在新的 Scala 版本发布时代码崩溃了,因为有人进行了重构,改变了内部组织方式。然后有人抱怨 Scala 承诺提供出色的兼容性保证,但一个小更新却破坏了他们的代码。

它很容易忽略了没有向后/前向兼容性涵盖这样的情况

val publicAPI: PublicAPI

publicAPI.asInstanceOf[internal.totallyprivate.APIImpl].internalMethod

据我所知,自从 Scala 3 团队开始测试是否有人依赖他们的 private 实现以避免更多戏剧性事件以来,但这仍然是制作脆弱代码的好方法,在任何更新时都可能会崩溃。

我可以想到一些有效的案例,为什么任何人都需要这样做,但使用

copy
而不是
apply
绝对不是其中之一。

© www.soinside.com 2019 - 2024. All rights reserved.