Divertente che nessuno abbia aggiunto obiettivi, dal momento che sono stati realizzati per questo tipo di cose. Quindi, ecco un documento di approfondimento su CS, ecco un blog che tocca brevemente l'uso degli obiettivi in Scala, ecco un'implementazione degli obiettivi per Scalaz e qui c'è un po 'di codice che lo utilizza, che assomiglia sorprendentemente alla tua domanda. E, per ridurre la piastra della caldaia, ecco un plugin che genera obiettivi Scalaz per le classi di custodie.
Per i punti bonus, ecco un'altra domanda SO che tocca gli obiettivi e un articolo di Tony Morris.
Il grosso problema delle lenti è che sono componibili. Quindi all'inizio sono un po 'ingombranti, ma continuano a guadagnare terreno più li usi. Inoltre, sono ottimi per la testabilità, dal momento che devi solo testare i singoli obiettivi e puoi dare per scontata la loro composizione.
Quindi, sulla base di un'implementazione fornita alla fine di questa risposta, ecco come lo faresti con gli obiettivi. Innanzitutto, dichiarare agli obiettivi di modificare un codice postale in un indirizzo e un indirizzo in una persona:
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))
Ora componili per ottenere un obiettivo che cambi il codice postale in una persona:
val personZipCodeLens = personAddressLens andThen addressZipCodeLens
Infine, usa quell'obiettivo per cambiare raj:
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens.get(raj) + 1)
Oppure, usando un po 'di zucchero sintattico:
val updatedRaj = personZipCodeLens.set(raj, personZipCodeLens(raj) + 1)
O anche:
val updatedRaj = personZipCodeLens.mod(raj, zip => zip + 1)
Ecco la semplice implementazione, presa da Scalaz, utilizzata per questo esempio:
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
}