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

能否用Scala 3实现dsinfo?Scala 3宏能否获取上下文信息?

Scala 3宏实现自动获取变量名的方案

可以用Scala 3宏实现类似dsinfo库的功能,即在赋值语句中让函数自动获取左侧变量名称,无需显式传递字符串。

实现思路

通过Scala 3的inline def配合宏工具,在宏实现中遍历当前调用位置的语法树,找到包含函数调用的val(或var)定义节点,提取其变量名称,再将名称与原参数一起返回。

具体代码实现

1. 定义入口函数与宏实现

import scala.quoted.*

// 入口inline函数,接收两个参数,返回包含变量名和参数的元组
inline def myFunction[A, B](x: A, y: B): (String, A, B) = ${ myFunctionImpl('x, 'y) }

// 宏实现逻辑
def myFunctionImpl[A: Type, B: Type](x: Expr[A], y: Expr[B])(using Quotes): Expr[(String, A, B)] = {
  import quotes.reflect.*

  // 递归遍历语法树,向上查找包含当前调用的ValDef节点
  def findEnclosingValDef(tree: Tree): Option[ValDef] = tree match {
    case valDef: ValDef => Some(valDef)
    case _ => tree.parent.flatMap(findEnclosingValDef)
  }

  // 获取当前宏调用的语法树节点
  val currentCall = Position.ofMacroExpansion.tree match {
    case apply: Apply => apply
    case _ => report.errorAndAbort("myFunction仅支持在val赋值语句右侧调用")
  }

  // 提取变量名,若找不到则报错
  val valName = findEnclosingValDef(currentCall)
    .map(_.name.toString)
    .getOrElse(report.errorAndAbort("myFunction必须用于val赋值场景"))

  // 构造最终返回的表达式
  '{ ($valName, $x, $y) }
}

2. 使用示例

val userAge = myFunction(25, "years old")
// 实际编译后等价于:val userAge = ("userAge", 25, "years old")

语法树遍历说明

  1. 获取当前调用节点:通过Position.ofMacroExpansion.tree获取宏调用位置的语法树节点,这里匹配Apply类型(函数调用节点)。
  2. 向上遍历父节点:定义递归函数findEnclosingValDef,从当前调用节点开始,不断访问tree.parent向上遍历,直到找到ValDef类型节点(即赋值语句左侧的变量定义)。
  3. 提取变量名:从ValDef节点的name属性中获取变量名称,转换为字符串后传入最终表达式。

扩展说明

如果需要支持var赋值,只需修改递归函数,同时匹配VarDef节点:

def findEnclosingVarOrValDef(tree: Tree): Option[Tree] = tree match {
  case valDef: ValDef => Some(valDef)
  case varDef: VarDef => Some(varDef)
  case _ => tree.parent.flatMap(findEnclosingVarOrValDef)
}

内容的提问来源于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.08.19 00:50:36