如何在 Scala 中自动对案例 class 的 all/some 字段应用修改?

How to automatically apply modifications to all/some fields of a case class in Scala?

我目前正在挑战自己以提高 Scala 和 FP 的技能。今天:


假设我在 Scala 3 中有以下情况 class:

type EmailAddress = String // I defined them like that to show I'm interested in
type PhoneNumber = String // ... attributes via their names, not via their types.
case class Person(name: String, emails: List[EmailAddress], phones: List[PhoneNumber])

我想要一个自动转换(几乎)所有字段的方法。 例如,我想使用 Ordering[String] 的 默认给定实例 指定的 phones 来订购 emails 43=]。 理想情况下,我应该能够排除 name 字段

所以我会得到类似的东西:

/* Below, I represented the kind of parametrization I would like to be able to do 
 * as parameters of the method orderValues,
 * but it could be annotations or meta-programming instead.
 * 
 * An `orderedPerson` can be directly an instance of Person
 * or something else like an OderedEntity[Person], I don't care so far.
 */
val orderedPerson =
  person.orderValues(
    excluded = Set("name"),
    explicitRules = Map(
      // Phones would have a special ordering (reverse is just a dummy value)
      "phones" -> Ordering.String.reverse
    )
  )

// -----

// So we would get:
Person(
  name = "Xiao",
  emails = List("a@a.a", "a@a.b", "a@b.a"),
  phones = List("+86 100 9000 1000", "+86 100 2000 1000")
)

我已经很长时间没有使用反射了,我还不熟悉元编程,但我愿意接受任何可以帮助我实现这一目标的解决方案。 这是一个学习的好机会!


[编辑]

我的意图是拥有一个可用于轻松匿名化任何数据的库。

Scala 中的type关键字只是一个类型别名。您应该使用像 https://github.com/estatico/scala-newtype(或 Scala 3 中的 opaque type)这样的 newtype 库,并从 String

中派生 Ordering 的隐式实例

示例estatico/scala-newtype

import io.estatico.newtype.macros.newtype
import io.estatico.newtype.ops._

@newtype case class Email(string: String)
object Email {
  implicit val ordering: Ordering[Email] = deriving
}
@newtype case class PhoneNumber(string: String)
object PhoneNumber {
  implicit val ordering: Ordering[PhoneNumber] = deriving[Ordering].reverse
}
case class Person(name: String, emails: List[Email], phones: List[PhoneNumber]) {
  lazy val orderValues: Person = this.copy(emails = emails.sorted, phones = phones.sorted)
}

Person(
  "Xiao",
  List(Email("a@a.a"), Email("a@a.b"), Email("a@b.a")),
  List(PhoneNumber("+86 100 9000 1000"), PhoneNumber("+86 100 2000 1000"))
).orderValues