我已经努力了几天,试图创建一个宏或使用 shapeless 创建一个方法/函数来提取字段名称和值作为 Tuple[String, String]。
让我们想象一下以下案例类:
case class Person(name: String, age: Int)
我想要这样的东西(实际上不需要是案例类中的方法)。
case class Person(name: String, age: Int) {
def fields: List[(String, String)] = ???
}
// or
def fields[T](caseClass: T): List[(String, String)] = ???
我在这里看到了很多类似的解决方案,但我无法让它与我的 (String, String) 用例一起工作
我也很欣赏一些文献来学习和扩展我关于宏的知识,我有《Scala 编程》(Martin 的第三版)和《Scala 编程》(O'REILLY - Dean Wampler 和 Alex Payne),并且只有 O'REILLY 拥有非常好的关于宏的小章节,说实话,它非常缺乏。
PD:我使用的是 Scala 2.12.12,所以我没有那些用于案例类 productElementNames 等奇特的新方法:(
基于
LabelledGeneric
和 Keys
类型类
import shapeless.LabelledGeneric
import shapeless.HList
import shapeless.ops.hlist.ToTraversable
import shapeless.ops.record.Keys
case class Person(name: String, age: Int)
def fields[P <: Product, L <: HList, R <: HList](a: P)(
implicit
gen: LabelledGeneric.Aux[P, L],
keys: Keys.Aux[L, R],
ts: ToTraversable.Aux[R, List, Symbol]
): List[(String, String)] = {
val fieldNames = keys().toList.map(_.name)
val values = a.productIterator.toList.map(_.toString)
fieldNames zip values
}
fields(Person("Jean-Luc, Picard", 70))
// : List[(String, String)] = List((name,Jean-Luc, Picard), (age,70))
对于类型级代码和宏,IDEA ...显示错误...没有隐式参数
IntelliJ 编辑器内错误突出显示有时不是 100% 准确。最好的办法是将其视为指导,并适当信任 Scala 编译器,因此,如果编译器满意而 IJ 不满意,那么就使用该编译器。另一种选择是尝试 Scala Metals,它应该在编译器诊断和编辑器内错误突出显示之间具有一对一的映射。
为什么使用 LabelledGeneric.Aux、Keys.Aux、ToTraversable.Aux
这使用了一种称为类型类的设计模式。我的建议是阅读类型宇航员的无形状指南,特别是关于链接依赖函数
的部分依赖类型函数提供了一种计算一种类型的方法 来自另一个。我们可以链接依赖类型的函数来执行 涉及多个步骤的计算。
考虑类型之间的以下依赖关系
input type
|
gen: LabelledGeneric.Aux[P, L],
|
output type
input type
|
keys: Keys.Aux[L, R]
|
output type
请注意,例如
L
的输出类型 LabelledGeneric
如何成为 Keys
的输入类型。通过这种方式,您向编译器展示了类型之间的关系,作为回报,编译器能够为您提供一个代表字段名称的 HList
,来自代表特定案例类的 Product
,而这一切都在程序运行之前完成。
需要 ToTraversable
,以便您可以从 List
返回常规 Scala HList
,从而启用以下位
.toList.map(_.name)
希望这至少能给您一点指导。需要搜索的一些关键字有:类型类、依赖类型、隐式解析、类型别名辅助模式、类型成员与类型参数、类型细化等。Typelevel 社区有一个新的 Discord channel,您可以在其中获得更多信息方向。