Забавно, что никто не добавил линз, так как они были СДЕЛАНЫ для такого рода вещей.Итак, здесь - это справочный документ по CS, здесь - блог, в котором кратко рассматриваются вопросы использования линз в Scala, здесь - реализация линз для Scalaz и здесь - это некоторый код, использующий его, который удивительно похож на ваш вопрос.И, чтобы сократить котельную, вот плагин, который генерирует линзы Scalaz для классов дел.
Для бонусных баллов вот еще один SO вопрос, касающийся линзи бумага Тони Морриса.
Главное в линзах то, что они сочетаются.Так что поначалу они немного громоздки, но они продолжают набирать силу, чем больше вы их используете.Кроме того, они отлично подходят для тестируемости, поскольку вам нужно только тестировать отдельные линзы и принимать как должное их состав.
Итак, на основе реализации, представленной в конце этого ответа, вот каксделай это с линзами.Сначала объявите линзы для изменения почтового индекса в адресе и адреса в человеке:
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
}