提问人:Andrew 提问时间:12/22/2022 最后编辑:Dmytro MitinAndrew 更新时间:12/24/2022 访问量:121
在 Scala 3 中查找 lambda 捕获的值(或其类)
Finding lambda captured values (or their classes) in Scala 3
问:
我正在寻找一种方法来查找 Scala 3 中由 lambda 捕获的值(或其类)(用于序列化 - 类似于 Spark)(我不需要 Scala 2 支持):
val a = "abc"
val f = () => a + "xyz"
serialize(f) // Should detect a / String as captured value
在运行时执行此操作有点容易(迭代),但我想在编译时执行此操作。f.getClass.getDeclaredFields
我试图在 Macro 中检查 lambda 的时间,但它被检测为没有任何有趣信息。scala.Function0
我想知道我是否可以做一些树检查,但我真的很想避免这种情况 - 我觉得我必须复制编译器内部结构才能捕获所有边缘情况。
答:
1赞
Dmytro Mitin
12/24/2022
#1
请尝试以下宏
import scala.quoted.*
inline def serialize(x: Any): Unit = ${serializeImpl('x)}
def serializeImpl(x: Expr[Any])(using Quotes): Expr[Unit] = {
import quotes.reflect.*
def owners(s: Symbol): List[Symbol] = s :: List.unfold(s)(s1 => Option.when(s1.maybeOwner != Symbol.noSymbol)((s1.maybeOwner, s1.maybeOwner)))
val symbol = x.asTerm.underlying.symbol
val rhs = symbol.tree match {
case ValDef(_, _, Some(rhs)) => rhs
}
val traverser = new TreeTraverser {
override def traverseTree(tree: Tree)(owner: Symbol): Unit = {
tree match {
case Ident(name) =>
val symbol1 = tree.symbol
val pos1 = symbol1.pos.get
println(s"identifier: $name, defined inside lambda: ${owners(symbol1).contains(symbol)}, defined in current file: ${pos1.sourceFile == SourceFile.current}")
case _ =>
}
super.traverseTree(tree)(owner)
}
}
traverser.traverseTree(rhs)(rhs.symbol)
'{()}
}
用法:
object App1 {
val b = "bbb"
}
import App1.b
object App {
val a = "abc"
val f = () => { val x = "uvw"; a + b + x + "xyz"}
serialize(f)
}
//scalac: identifier: a, defined inside lambda: false, defined in current file: true
//scalac: identifier: b, defined inside lambda: false, defined in current file: false
//scalac: identifier: x, defined inside lambda: true, defined in current file: true
//scalac: identifier: $anonfun, defined inside lambda: true, defined in current file: true
评论
0赞
Andrew
12/27/2022
非常感谢您的回答,不幸的是,由于某种原因,它没有在我的项目中编译,并且似乎绑定到对象范围内。但是,多亏了 method 和 traverseTree,我目前正在研究符合我的项目要求的解决方案。val
owners
0赞
Dmytro Mitin
12/28/2022
@Andrew 为了方便起见,我在 github.com/DmytroMitin/scala3-macro-closure-demo 发布了该项目 如果您的编译错误是因为定义右侧的 AST 为空,那么很可能您丢失了 。如果您仍然遇到编译错误并需要帮助,请提供复制(在此问题或新问题中)。MatchError
val
scalacOptions += "-Yretain-trees"
评论
f.getClass.getDeclaredFields
a
a
a
val
a
x
val f = () => { val x = "uvw"; a + x + "xyz"}