能否用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")
语法树遍历说明
- 获取当前调用节点:通过
Position.ofMacroExpansion.tree获取宏调用位置的语法树节点,这里匹配Apply类型(函数调用节点)。 - 向上遍历父节点:定义递归函数
findEnclosingValDef,从当前调用节点开始,不断访问tree.parent向上遍历,直到找到ValDef类型节点(即赋值语句左侧的变量定义)。 - 提取变量名:从
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
相关产品推荐
相关产品推荐

