You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Scala 3宏中调用内部类的构造方法?

解决Scala宏中内部类的"stale symbol"错误

问题根源

你碰到的stale symbol错误,是因为内部类Inner的符号在宏展开阶段和类定义阶段处于不同编译周期,直接通过$outer.Inner引用内部类构造器会导致符号失效。Scala 3宏处理内部类时,不能简单通过外部实例直接访问内部类构造器,必须正确处理内部类的符号上下文。

可行解决方案

方案1:通过反射API精准获取内部类构造器

修改宏实现,利用quotes的反射能力找到Inner类的符号,再生成合法的构造调用表达式:

inline def makeInnerMacro[X, Y, Z](
  inline outer: Outer[X],
  inline l: List[Y],
  inline f: Y => Z,
): Outer[X]#Inner[Y, Z] = ${ makeInnerImpl('{outer}, '{l}, '{f}) }

def makeInnerImpl[X: Type, Y: Type, Z: Type](
  outer: Expr[Outer[X]],
  l: Expr[List[Y]],
  f: Expr[Y => Z],
)(using qCtx: Quotes): Expr[Outer[X]#Inner[Y, Z]] =
  import qCtx.reflect.*

  // 获取外部类Outer的符号
  val outerClassSymbol = TypeRepr.of[Outer[X]].typeSymbol.asClass
  // 查找内部类Inner的符号
  val innerClassSymbol = outerClassSymbol.declaredType("Inner").typeSymbol.asClass
  // 获取Inner的主构造器
  val innerConstructor = innerClassSymbol.primaryConstructor

  // 准备构造参数,用show替代toString获取更准确的Lambda源码
  val fString = Expr(f.show)
  val constructorArgs = List(outer, l, f, fString).map(_.asTerm)

  // 生成构造调用并转换为目标类型的Expr
  val innerInstance = Apply(
    Select.unique(outer.asTerm, innerClassSymbol.name),
    constructorArgs
  )
  innerInstance.asExprOf[Outer[X]#Inner[Y, Z]]

方案2:调整构造调用的写法,明确路径依赖

通过引入临时变量持有外部实例,避免直接在宏引号中通过外部实例引用内部类,确保符号上下文正确:

inline def makeInnerMacro[X, Y, Z](
  inline outer: Outer[X],
  inline l: List[Y],
  inline f: Y => Z,
): Outer[X]#Inner[Y, Z] = ${ makeInnerImpl('{outer}, '{l}, '{f}) }

def makeInnerImpl[X: Type, Y: Type, Z: Type](
  outer: Expr[Outer[X]],
  l: Expr[List[Y]],
  f: Expr[Y => Z],
)(using qCtx: Quotes): Expr[Outer[X]#Inner[Y, Z]] =
  import qCtx.reflect.*
  val fString = Expr(f.show)
  // 通过临时变量持有外部实例,再调用内部类构造器
  '{
    val outerInst = $outer
    new outerInst.Inner($l, $f, $fString)
  }

关键注意点

  • 用f.show替代f.toString:show方法能精准获取Lambda的源码字符串,而toString可能返回编译器生成的内部标识,不符合需求。
  • 内部类符号的正确获取:必须通过外部类的符号上下文查找内部类,不能直接通过实例访问,否则会触发符号过期错误。
  • 路径依赖类型:Outer[X]#Inner[Y,Z]是标准的类型投影写法,确保宏返回类型与实际内部类实例类型匹配。

修改后,MyOuter.inner会正确生成为Inner(List(0, 1, 2), <func>, "num => num.toString"),编译错误彻底消除。

内容的提问来源于stack exchange,提问作者Todd O'Bryan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 10:17:10