有趣的是没有人添加镜头,因为它们是为这种东西制造的。所以,这是一篇关于它的 CS 背景论文,这是一个简要介绍在 Scala 中使用镜头的博客,这是 Scalaz 的镜头实现,这是一些使用它的代码,这看起来很像你的问题。而且,为了减少样板,这是一个为案例类生成 Scalaz 镜头的插件。
对于奖励积分,这是另一个涉及镜头的 SO question,以及Tony Morris 的一篇论文。
镜头最重要的是它们是可组合的。所以一开始它们有点麻烦,但你使用它们的次数越多,它们就会越来越受欢迎。此外,它们非常适合可测试性,因为您只需要测试单个镜头,并且可以认为它们的组成是理所当然的。
因此,根据此答案末尾提供的实现,这是使用镜头的方法。首先,声明镜头以更改地址中的邮政编码和人员中的地址:
val addressZipCodeLens = Lens(
get = (_: Address).zipCode,
set = (addr: Address, zipCode: Int) => addr.copy(zipCode = zipCode))
val personAddressLens = Lens(
get = (_: Person).address,
set = (p: Person, addr: Address) => p.copy(address = addr))
现在,组合它们以获得一个可以改变人的邮政编码的镜头:
val personZipCodeLens = personAddressLens andThen addressZipCodeLens
最后,使用那个镜头来改变 raj:
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens.get(raj) + 1)
或者,使用一些语法糖:
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens(raj) + 1)
甚至:
val updatedRaj = personZipCodeLens.mod(raj, zip => zip + 1)
下面是这个例子中使用的简单实现,取自 Scalaz:
case class Lens[A,B](get: A => B, set: (A,B) => A) extends Function1[A,B] with Immutable {
def apply(whole: A): B = get(whole)
def updated(whole: A, part: B): A = set(whole, part) // like on immutable maps
def mod(a: A, f: B => B) = set(a, f(this(a)))
def compose[C](that: Lens[C,A]) = Lens[C,B](
c => this(that(c)),
(c, b) => that.mod(c, set(_, b))
)
def andThen[C](that: Lens[B,C]) = that compose this
}