La domanda non ha menzionato modifica i dati, ma quando è necessario fare questo si scopre rapidamente che la libreria Scala non ha gli strumenti per semplificare (quando i dati sono immutabili). Se non l'hai ancora provato, prova a scrivere una funzione che sostituirà o modificherà lo value
dello Cash
tenuto da un Person
, utilizzando i tipi definiti nella domanda.
Come descritto in Asymmetric Lenses in Scala di Tony Morris, gli obiettivi sono una soluzione appropriata a questo problema.
Ecco un esempio di come si può accedere e aggiornare la value
di Cash
utilizzando le lenti (parziale) implementazioni di una persona Lens
e PLens
dal ramo scalaz-seven di Scalaz.
In primo luogo, un numero di piastre: definire l'istanza di Lente per ciascun campo delle classi di casi. A @[email protected] B
significa lo stesso di Lens[A, B]
.
val pants: Person @[email protected] Option[Pants] =
lensG(_.pants, p => ps => p.copy(pants = ps))
val pocket: Pants @[email protected] Option[Pocket] =
lensG(_.pocket, ps => p => ps.copy(pocket = p))
val cash: Pocket @[email protected] Option[Cash] =
lensG(_.cash, p => c => p.copy(cash = c))
val value: Cash @[email protected] String =
lensG(_.value, c => v => c.copy(value = v))
Non possiamo comporre tutti questi obiettivi, però, perché la maggior parte dei campi sono avvolti in Option
tipi.
Lenti parziali salvato: questi consentono di accedere e aggiornare le distinte di una struttura che non può esistere, come il valore di un Some
Option
, o head
di un List
.
Possiamo usare la funzione somePLens
da Scalaz 7 per creare un obiettivo parziale che visualizza ciascun campo facoltativo.Per comporre un obiettivo parziale con uno dei nostri obiettivi regolari, tuttavia, è necessario accedere all'istanza dell'obiettivo parziale equivalente per l'obiettivo normale, utilizzando il metodo partial
esistente su ogni Lens
.
// @-? is an infix type alias for PLens
val someCash: Pocket @-? Cash = cash.partial andThen somePLens
scala> someCash.get(Pocket(Some(Cash("zilch"))))
res1: Option[Cash] = Some(Cash(zilch))
Allo stesso modo, possiamo creare il nostro obiettivo parziale la visualizzazione della liquidità detenuta da un Person
componendo tutte le istanze partial
nostre lenti, e sandwich istanze di somePLens
. Qui, ho utilizzato l'operatore <=<
, un alias per andThen
(che equivale a compose
con gli operandi attivati).
val someCashValue: Person @-? String =
pants.partial <=< somePLens <=<
pocket.partial <=< somePLens <=<
cash.partial <=< somePLens <=<
value.partial
Creazione di un'istanza Person
con cui giocare:
val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch")))))))
Utilizzando la lente parziale accedere al valore del denaro che ho:
scala> someCashValue.get(ben)
res2: Option[String] = Some(zilch)
Utilizzando la lente parziale modificare il valore :
scala> someCashValue.mod(_ + ", zero, nada", ben)
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada)))))))
(!) 0
Ora, se io sono non indossare i pantaloni, possiamo vedere come un tentativo di modificare il valore dei miei soldi non avrà alcun effetto:
scala> val ben = Person(None)
ben: Person = Person(None)
scala> someCashValue.mod(_ + ", zero, nada", ben)
res4: Person = Person(None)
Impressionante, grazie! L'approccio per la comprensione è in realtà esattamente quello che stavo cercando di fare, ma la struttura con cui sto lavorando non è pulita come nell'esempio che ho fornito sopra. Almeno questo conferma che sono sulla strada giusta. –