Scala 3宏:获取类属性

66bbxpm5  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(196)

我想编写一个宏来获取类的属性名称。但不能在引用的语句中使用Symbol模块。我收到吹气错误..。

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.
cfh9epnr

cfh9epnr1#

宏有编译时和运行时。还有主代码的编译时和运行时。宏的运行时是主代码的编译时间。

def getPropsImpl... = 
  '{ Symbol.classSymbol($className).fieldMembers.map(_.name) }
  ...

是不正确的,因为Scala 3宏所做的是将树转换为树(例如,Expr转换为ExprExpr是树上的 Package 器)(*)。那棵树

Symbol.classSymbol($className).fieldMembers.map(_.name)

在应用站点的范围内是没有意义的。SymbolSymbol.classSymbol等在这里是有意义的,在宏的范围内。

def getPropsImpl... = 
  Symbol.classSymbol(className).fieldMembers.map(_.name)
  ...

也是不正确的,因为className作为一个值还不存在,它现在只是一棵树。
我想正确的是使用.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]
    )
  )
}

用途:

// in other file
getProps("mypackage.App.A") //ArraySeq(s, i)

// in other subproject
package mypackage
object App {
  case class A(i: Int, s: String)
}

(*)使用c.eval,Scala 2宏可以执行更多操作。在Scala3中也有类似的thingstaging.run,但在宏中是被禁止的。
实际上,c.eval(或禁用的staging.run)也可以在Scala3中模拟
get annotations from class in scala 3 macros

相关问题