Смішно, що ніхто не додавав лінзи, оскільки вони зроблені для подібних матеріалів. Отже, ось довідковий документ CS на цьому сайті, ось блог, який коротко торкається використання лінз у Scala, ось реалізація лінз для Scalaz, і ось якийсь код, який використовує його, виглядає напрочуд як ваше запитання. І, щоб скоротити на плиті котла, ось плагін, який генерує лінзи Scalaz для класів корпусів.
Щодо бонусних балів, ось ще одне питання, яке стосується об’єктивів, та документ Тоні Морріса.
Велика справа щодо лінз полягає в тому, що вони є композиційними. Тож вони спочатку трохи громіздкі, але вони все більше набирають сили, чим більше ви їх використовуєте. Крім того, вони чудово підходять для перевірки, оскільки вам потрібно лише протестувати окремі лінзи, і ви можете прийняти як належне їх склад.
Отже, виходячи з реалізації, наданої наприкінці цієї відповіді, ось як би ви це зробили за допомогою лінз. Спочатку оголосіть лінзи, щоб змінити поштовий індекс у адресу та адресу особи:
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
}