Komisch, dass niemand Objektive hinzufügte, da sie für solche Sachen gemacht wurden. Also, hier ist ein CS-Hintergrundpapier dazu, hier ist ein Blog, der kurz auf die Verwendung von Objektiven in Scala eingeht , hier ist eine Objektivimplementierung für Scalaz und hier ist ein Code, der es verwendet, was überraschend wie Ihre Frage aussieht. Und um die Kesselplatte zu reduzieren, hier ein Plugin, das Scalaz-Objektive für Fallklassen generiert.
Für Bonuspunkte gibt es hier eine weitere SO-Frage, die Linsen berührt, und ein Papier von Tony Morris.
Die große Sache bei Objektiven ist, dass sie zusammensetzbar sind. Sie sind anfangs etwas umständlich, aber sie gewinnen immer mehr an Boden, je mehr Sie sie verwenden. Sie eignen sich auch hervorragend für die Testbarkeit, da Sie nur einzelne Objektive testen müssen und deren Zusammensetzung als selbstverständlich vorausgesetzt werden kann.
Basierend auf einer Implementierung am Ende dieser Antwort erfahren Sie hier, wie Sie dies mit Objektiven tun würden. Deklarieren Sie zunächst Objektive, um eine Postleitzahl in einer Adresse und eine Adresse in einer Person zu ändern:
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))
Stellen Sie sie nun zusammen, um ein Objektiv zu erhalten, das die Postleitzahl einer Person ändert:
val personZipCodeLens = personAddressLens andThen addressZipCodeLens
Verwenden Sie zum Schluss dieses Objektiv, um Raj zu wechseln:
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens.get(raj) + 1)
Oder mit syntaktischem Zucker:
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens(raj) + 1)
Oder auch:
val updatedRaj = personZipCodeLens.mod(raj, zip => zip + 1)
Hier ist die einfache Implementierung von Scalaz, die für dieses Beispiel verwendet wird:
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
}