如何在 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
我目前正在挑战自己以提高 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
示例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