2013-08-24 5 views
8

Mentre l'immutabilità è stata elogiata da molti, ho trovato difficile mantenerla nella programmazione mainstream. Nella mia esperienza, i programmatori prima di allora renderanno i campi mutabili di nuovo per evitare di refactoring una grande parte di codice che dovrebbe passare oggetto aggiornato insieme al valore di ritorno.Quali lingue supportano le lenti o un modo simile per aggiornare strutture nidificate immutabili?

Scala ha qualche supporto con i costruttori di copie ma non conosco soluzioni soddisfacenti per l'aggiornamento di strutture di oggetti complesse. Potrei aver perso qualcosa.

La migliore implementazione che ho sperimentato è data-lens in Haskell. Tuttavia, Haskell è difficile da imparare. Quali opzioni ci sono per i linguaggi di programmazione multipiattaforma come Java o Scala?

+0

vedi [puramente funzionali strutture dati] (http://www.amazon.com/Purely- Functional-Structures-Chris-Okasaki/dp/0521663504) di Okasaki. –

+0

@RobertHarvey Ho letto solo la tesi, ma non è che "solo" su efficienti strutture di dati puramente funzionali, non su modi convenienti di (citando il titolo della domanda, poiché è probabilmente il fraseggio più accurato) che aggiorna le strutture immutabili annidate? – delnan

+0

Che dire del clojure?È in grado di gestire strutture di dati nidificate immutabili abbastanza bene e funziona sulla JVM. – SpiderPig

risposta

7

Non c'è davvero alcun bisogno di supporto a livello di linguaggio per gli obiettivi, anche se ovviamente possono essere più o meno utili a seconda delle proprietà della lingua e la chiarezza della sintassi dipenderà dalle caratteristiche della lingua.

Come menzionato in un commento sopra, ci sono buone librerie di obiettivi per Scala anche se il linguaggio stesso non (e probabilmente non dovrebbe) fornire loro. Ad esempio, supponiamo di avere le seguenti classi:

case class Email(user: String, domain: String) 
case class Contact(email: Email, web: String) 
case class Person(name: String, contact: Contact) 

E un'istanza:

val foo = Person(
    "Foo McBar", 
    Contact(Email("foo", "mcbar.com"), "http://mcbar.com/foo") 
) 

Usando Shapeless si può scrivere il seguente (si noti che nella prossima versione 2.0 del boilerplate isomorfismo non sarà necessario):

import shapeless._, Nat._ 

implicit val emailIso = Iso.hlist(Email.apply _, Email.unapply _) 
implicit val contactIso = Iso.hlist(Contact.apply _, Contact.unapply _) 
implicit val personIso = Iso.hlist(Person.apply _, Person.unapply _) 

E poi:

val emailDomainLens = Lens[Contact] >> _1 >> _1 

E ora Foo McBar può facilmente cambiare il proprio dominio di posta elettronica:

scala> println(emailHostLens.set(foo)("mcbar.org")) 
Person(Foo McBar,Contact(Email(foo,mcbar.com),mcbar.org)) 

Questo è tutto vaniglia Scala-la versione corrente di informe (1.2.4) non utilizza le macro o plugin compilatore, ecc e funzionerà su Scala 2.9. Se siamo disposti a usare le macro Scala 2.10 di, possiamo ottenere la sintassi ancora più bello e meno boilerplate:

scala> import rillit._ 
import rillit._ 

scala> println(Lenser[Person].contact.email.domain.set(foo)("mcbar.org")) 
Person(Foo McBar,Contact(Email(foo,mcbar.org),http://mcbar.com/foo)) 

Questo utilizza Rillit, una biblioteca proof-of-concept sviluppato da lenti Aki Saarinen (e più tardi adapted by me) .

Tutto ciò potrebbe essere stato fatto in Java, sebbene la sintassi non sia verosimile. In effetti sono sicuro che ci sono librerie di obiettivi per Java, anche se non ne ho mai viste o usate, e la relativa mancanza di enfasi su tipi di dati immutabili significa che la maggior parte degli sviluppatori Java non avrà mai bisogno o non vorranno obiettivi.

+0

"Sono sicuro che ci sono librerie di obiettivi per Java" - affermazione senza fondamento. Non ne ho trovato nessuno. – leventov

+2

@leventov: esegui una ricerca su Google per 'obiettivo funzionale java'-avrai almeno un paio di candidati nella prima pagina dei risultati. Non ho idea se siano buoni o usati da nessuno, motivo per cui non ho espresso la frase in modo più positivo. –

+0

Hm, ho pensato che "lens" fosse un termine specifico per haskell e ho provato solo "java haskell lens". Grazie :) – leventov

0

ci sono lenti in Octarine, che vengono utilizzati per comporre le chiavi di registrazione per creare percorsi in strutture dati:

Record testRecord = $$(
     name.of("Arthur Putey"), 
     age.of(43), 
     address.of(
      addressLines.of("23 Acacia Avenue", "Sunderland", "VB6 5UX") 
     ) 
); 

Lens<Record, String> secondLineOfAddress = address.assertPresent() 
    .join(addressLines.assertPresent()) 
    .join(Lens.intoPVector(1)); 

assertThat(secondLineOfAddress.get(testRecord), equalTo("Sunderland")); 
assertThat(secondLineOfAddress.set(testRecord, "Cirencester"), equalTo($$(
    name.of("Arthur Putey"), 
    age.of(43), 
    address.of(Record.of(
     addressLines.of("23 Acacia Avenue", "Cirencester", "VB6 5UX") 
    )) 
)));