i want to writing a macro to get property names of a class.
but can not use Symbol
module in quoted statement. i receive blow error...
inline def getProps(inline className: String): Iterable[String] = ${ getPropsImpl('className) }
private def getPropsImpl(className: Expr[String])(using Quotes): Expr[Iterable[String]] = {
import quotes.reflect.*
val props = '{
Symbol.classSymbol($className).fieldMembers.map(_.name) // error access to parameter x$2 from
} wrong staging level:
props - the definition is at level 0,
} - but the access is at level 1.
There are compile time and runtime of macros. And there are compile time and runtime of main code. The runtime of macros is the compile time of main code.
def getPropsImpl... =
'{ Symbol.classSymbol($className).fieldMembers.map(_.name) }
...
is incorrect because what Scala 3 macros do is transforming trees into trees (i.e. Expr
s into Expr
s, Expr
is a wrapper over a tree) (*). The tree
Symbol.classSymbol($className).fieldMembers.map(_.name)
will make no sense inside the scope of application site. Symbol
, Symbol.classSymbol
etc. make sense here, inside the scope of macro.
def getPropsImpl... =
Symbol.classSymbol(className).fieldMembers.map(_.name)
...
would be also incorrect because className
as a value doesn't exist yet, it's just a tree now.
I guess correct is with .valueOrAbort
import scala.quoted.*
inline def getProps(inline className: String): Iterable[String] = ${getPropsImpl('className)}
def getPropsImpl(className: Expr[String])(using Quotes): Expr[Iterable[String]] = {
import quotes.reflect.*
Expr.ofSeq(
Symbol.classSymbol(className.valueOrAbort).fieldMembers.map(s =>
Literal(StringConstant(s.name)).asExprOf[String]
)
)
}
Usage:
// in other file
getProps("mypackage.App.A") //ArraySeq(s, i)
// in other subproject
package mypackage
object App {
case class A(i: Int, s: String)
}
(*) Scala 2 macros can do more with c.eval
. In Scala 3 there is similar thing staging.run
but it's forbidden in macros.
Actually, c.eval
(or forbidden staging.run
) can be emulated in Scala 3 too