如何创建一个宏来在案例 class 中创建 val 列表?
How to create a Macro to create List of val in a case class?
我正在尝试创建一个宏来为我提供特定情况下的 val 列表 class。
object CaseClass {
def valList[T]: List[String] = macro implValList[T]
def implValList[T](c: whitebox.Context): c.Expr[List[String]] = {
import c.universe._
val listApply = Select(reify(List).tree, TermName("apply"))
val vals = weakTypeOf[T].decls.collect {
case m: TermSymbol if m.isVal => q"${m.name}"
}
c.Expr[List[String]](Apply(listApply, vals.toList))
}
}
如此给出
case class AClass(
val a: String,
val b: Int
)
我想要 CaseClass.valList[AClass] = List("a", "b")
的列表
不是宏方面的专家,所以对它持保留态度。但是我用 Intellij 测试了它。
首先,要使用 weakTypeOf
,您需要将 WeakTypeTag
作为隐含在您的宏实现中,如下所示:
def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]) ...
其次,要创建文字,您可以使用此构造而不是您的准引用(我相信,它实际上什么都不做):
Literal(Constant(m.name.toString))
最后,我建议使用这个守卫而不是 isVal
:
m.isCaseAccessor && m.isGetter
正确检查 case class 参数并且也是 getter(case class 参数重复,一个为 isGetter
,另一个为 isParam
).原因是 isVal
案例 classes 的名称令人惊讶地产生了一个以空格结尾的名称。
对我有用的最终实现如下:
object CaseClass {
def valList[T]: List[String] = macro implValList[T]
def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]): c.Expr[List[String]] = {
import c.universe._
val listApply = Select(reify(List).tree, TermName("apply"))
val vals = weakTypeOf[T].decls.collect {
case m: TermSymbol if m.isCaseAccessor && m.isGetter => Literal(Constant(m.name.toString))
}
c.Expr[List[String]](Apply(listApply, vals.toList))
}
}
作为替代方案(因为设置宏有点麻烦 - 你不能在定义它的同一个子项目中使用宏),而且你并不经常需要它,你也许可以逃脱使用 shapeless 一行:
import shapeless._
import shapeless.ops.record.Keys
case class Foo(a: Int, b: String)
Keys[the.`LabelledGeneric[Foo]`.Repr].apply().toList.map(_.name) // List("a", "b")
我正在尝试创建一个宏来为我提供特定情况下的 val 列表 class。
object CaseClass {
def valList[T]: List[String] = macro implValList[T]
def implValList[T](c: whitebox.Context): c.Expr[List[String]] = {
import c.universe._
val listApply = Select(reify(List).tree, TermName("apply"))
val vals = weakTypeOf[T].decls.collect {
case m: TermSymbol if m.isVal => q"${m.name}"
}
c.Expr[List[String]](Apply(listApply, vals.toList))
}
}
如此给出
case class AClass(
val a: String,
val b: Int
)
我想要 CaseClass.valList[AClass] = List("a", "b")
不是宏方面的专家,所以对它持保留态度。但是我用 Intellij 测试了它。
首先,要使用 weakTypeOf
,您需要将 WeakTypeTag
作为隐含在您的宏实现中,如下所示:
def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]) ...
其次,要创建文字,您可以使用此构造而不是您的准引用(我相信,它实际上什么都不做):
Literal(Constant(m.name.toString))
最后,我建议使用这个守卫而不是 isVal
:
m.isCaseAccessor && m.isGetter
正确检查 case class 参数并且也是 getter(case class 参数重复,一个为 isGetter
,另一个为 isParam
).原因是 isVal
案例 classes 的名称令人惊讶地产生了一个以空格结尾的名称。
对我有用的最终实现如下:
object CaseClass {
def valList[T]: List[String] = macro implValList[T]
def implValList[T](c: whitebox.Context)(implicit wt: c.WeakTypeTag[T]): c.Expr[List[String]] = {
import c.universe._
val listApply = Select(reify(List).tree, TermName("apply"))
val vals = weakTypeOf[T].decls.collect {
case m: TermSymbol if m.isCaseAccessor && m.isGetter => Literal(Constant(m.name.toString))
}
c.Expr[List[String]](Apply(listApply, vals.toList))
}
}
作为替代方案(因为设置宏有点麻烦 - 你不能在定义它的同一个子项目中使用宏),而且你并不经常需要它,你也许可以逃脱使用 shapeless 一行:
import shapeless._
import shapeless.ops.record.Keys
case class Foo(a: Int, b: String)
Keys[the.`LabelledGeneric[Foo]`.Repr].apply().toList.map(_.name) // List("a", "b")