在一个带有类型参数T
的Scala 3宏中,可以使用TypeRepr.of[T]
和新的Scala 3 reflection API来探索T
的companionClass
,并找到该伴随类上任意方法的Symbol
(例如companionClass.declarations.find(_.name == "list")
查找list()
方法)。
给定伴随对象方法的Symbol
,您将如何在quoted code block中调用该方法?
我猜我需要将Symbol
转换为Expr[T]
,但我不知道如何做到这一点!
在Scala 2宏中,在q"..."
quasiquote中调用c.universe.Symbol
类型的listMethod
看起来非常简单--只需要输入$listMethod
,然后就可以开始Map结果列表,例如:
q"""
$listMethod.map(_.toString)
"""
尝试在Scala 3宏中执行类似的操作会得到如下错误:
[error] 27 | ${listMethod}.map(_.toString)
[error] | ^^^^^^^^^^
[error] | Found: (listMethod : x$1.reflect.Symbol)
[error] | Required: quoted.Expr[Any]
在Scala 3中使用正确的代码是什么?
您可以在这里查看AvroSerialisableMacro
类中的更多代码上下文(Scala 2编译,Scala 3目前还远未完成!):https://github.com/guardian/marley/pull/77/files
2条答案
按热度按时间eivgtgni1#
首先,让我们讨论一下如何使用符号名调用一个方法。
您可能需要
Select
。您可以通过几种不同的方式调用获得它,例如:选择方法后,可以提供参数:
还有其他方法,如
appliedToType
、appliedToTypeTrees
,但如果您有一个方法名为Symbol
,并希望使用它来调用某些内容,这应该是一个很好的起点。请记住,
Quotes
的源代码是您的朋友,因此即使IDE没有给予您任何建议,它也可以为您指出一些解决方案。理论上,这些方法是在
Term
上定义的,而不是在Select
(<: Term
)上定义的,但是你的用例很可能是选择一个表达式,然后用一些参数调用它的方法。显然,证明
expression
可以调用method
,并确保args
中Term
的类型与传递给方法的值的允许类型相匹配,这是你的责任。这比Scala 2中的麻烦一些,因为引号只允许你使用Type[T]
和Expr[T]
。因此,任何不属于该类别的内容都必须使用宏/Tasty ADT来实现,直到可以在${}
中返回Expr
为止。也就是说,您链接的示例显示这些调用是相当硬编码的,因此您不必查找
Symbol
并调用它们。您的代码很可能会消除:6ojccjat2#
Scala 2 quasiquotes和Scala 3 quotations的区别在于前者必须在主代码的编译时使用宏进行编译(即,在宏扩展、宏运行时期间)而后者必须更早地编译,所以Scala 3的引号
'{...}
/${...}
更像Scala 2的引号reify{...}
/.splice
,而不是Scala 2的准引号q"..."
/${...}
。tq
equivalent in Scala 3 macros你必须重新创建AST,让我们看看AST应该是什么形状:
因此,为了重新创建AST,请尝试使用
Apply(...)
和Select.unique(..., "list")
:测试(在不同文件中):
使用方法符号而不是名称,使用方便的方法而不是直接使用AST节点,可以将
fooImpl
重写为这只是一个如何创建AST的例子,在
.asExprOf[Unit]
中应该使用实际的返回类型def list()
而不是Unit
。How to get the list of default fields values for typed case class?
scala 3 macro how to implement generic trait