Angenommen, ich habe zwei folgende case class
:
case class Address(street: String, city: String, state: String, zipCode: Int)
case class Person(firstName: String, lastName: String, address: Address)
und die folgende Instanz der Person
Klasse:
val raj = Person("Raj", "Shekhar", Address("M Gandhi Marg",
"Mumbai",
"Maharashtra",
411342))
Nun , wenn ich zu aktualisierenden zipCode
von raj
dann werde ich zu tun haben:
val updatedRaj = raj.copy(address = raj.address.copy(zipCode = raj.address.zipCode + 1))
Mit mehr Verschachtelungsebenen wird dies noch hässlicher. Gibt es eine sauberere Möglichkeit (so etwas wie die von Clojure update-in
), solche verschachtelten Strukturen zu aktualisieren?
scala
case-class
zipper
fehlender Faktor
quelle
quelle
Antworten:
Reißverschlüsse
Huets Reißverschluss bietet eine bequeme Durchquerung und "Mutation" einer unveränderlichen Datenstruktur. Scalaz bietet Reißverschlüsse für
Stream
( scalaz.Zipper ) undTree
( scalaz.TreeLoc ). Es stellt sich heraus, dass die Struktur des Reißverschlusses automatisch von der ursprünglichen Datenstruktur abgeleitet werden kann, und zwar auf eine Weise, die der symbolischen Differenzierung eines algebraischen Ausdrucks ähnelt.Aber wie hilft Ihnen das bei Ihren Scala-Fallklassen? Nun, Lukas Rytz hat kürzlich einen Prototyp für eine Erweiterung von Scalac entwickelt, mit der automatisch Reißverschlüsse für kommentierte Fallklassen erstellt werden. Ich werde sein Beispiel hier wiedergeben:
Daher muss die Community das Scala-Team davon überzeugen, dass diese Bemühungen fortgesetzt und in den Compiler integriert werden sollten.
Übrigens Lukas vor kurzem veröffentlichte eine Version von Pacman, benutzerprogrammierbare über einen DSL. Sieht aber nicht so aus, als hätte er den modifizierten Compiler verwendet, da ich keine
@zip
Anmerkungen sehen kann .Umschreiben von Bäumen
Unter anderen Umständen möchten Sie möglicherweise eine Transformation auf die gesamte Datenstruktur anwenden, die einer bestimmten Strategie (von oben nach unten, von unten nach oben) entspricht und auf Regeln basiert, die an einem bestimmten Punkt in der Struktur mit dem Wert übereinstimmen. Das klassische Beispiel ist die Transformation eines AST für eine Sprache, um möglicherweise Informationen auszuwerten, zu vereinfachen oder zu sammeln. Kiama unterstützt das Umschreiben , sehen Sie sich die Beispiele in RewriterTests an und sehen Sie sich dieses Video an . Hier ist ein Ausschnitt, der Appetit macht:
Beachten Sie, dass Kiama außerhalb Schritte vom Typ System dies zu erreichen.
quelle
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:
Stellen Sie sie nun zusammen, um ein Objektiv zu erhalten, das die Postleitzahl einer Person ändert:
Verwenden Sie zum Schluss dieses Objektiv, um Raj zu wechseln:
Oder mit syntaktischem Zucker:
Oder auch:
Hier ist die einfache Implementierung von Scalaz, die für dieses Beispiel verwendet wird:
quelle
personZipCodeLens.set(raj, personZipCodeLens.get(raj) + 1)
ist die gleiche wiepersonZipCodeLens mod (raj, _ + 1)
mod
ist jedoch kein Grundelement für Objektive.Nützliche Werkzeuge zur Verwendung von Objektiven:
Ich möchte nur hinzufügen, dass die auf Scala 2.10-Makros basierenden Projekte Macrocosm und Rillit die Erstellung dynamischer Objektive ermöglichen.
Verwenden von Rillit:
Verwenden von Macrocosm:
quelle
Ich habe mich nach einer Scala-Bibliothek umgesehen, die die schönste Syntax und die beste Funktionalität hat, und eine Bibliothek, die hier nicht erwähnt wird, ist Monokel, was für mich wirklich gut war. Ein Beispiel folgt:
Diese sind sehr schön und es gibt viele Möglichkeiten, die Linsen zu kombinieren. Scalaz zum Beispiel erfordert viel Boilerplate und dies kompiliert schnell und läuft großartig.
Um sie in Ihrem Projekt zu verwenden, fügen Sie dies einfach Ihren Abhängigkeiten hinzu:
quelle
Formlos macht den Trick:
mit:
Beachten Sie, dass Sie mit einigen anderen Antworten hier Objektive zusammenstellen können, um tiefer in eine bestimmte Struktur einzudringen. Mit diesen shapless-Objektiven (und anderen Bibliotheken / Makros) können Sie zwei nicht verwandte Objektive kombinieren, sodass Sie ein Objektiv erstellen können, das eine beliebige Anzahl von Parametern in beliebige Positionen setzt in Ihrer Struktur. Für komplexe Datenstrukturen ist diese zusätzliche Komposition sehr hilfreich.
quelle
Lens
Code in Daniel C. Sobrals Antwort verwendet habe und so vermieden habe, eine externe Abhängigkeit hinzuzufügen.Linsen bieten aufgrund ihrer Zusammensetzbarkeit eine sehr gute Lösung für das Problem stark verschachtelter Strukturen. Bei einem geringen Verschachtelungsgrad habe ich jedoch manchmal das Gefühl, dass Objektive etwas zu viel sind, und ich möchte nicht den gesamten Objektivansatz einführen, wenn es nur wenige Stellen mit verschachtelten Aktualisierungen gibt. Der Vollständigkeit halber ist hier eine sehr einfache / pragmatische Lösung für diesen Fall:
Ich schreibe einfach ein paar
modify...
Hilfsfunktionen in die Struktur der obersten Ebene, die sich mit der hässlichen verschachtelten Kopie befassen. Zum Beispiel:Mein Hauptziel (Vereinfachung des Updates auf Client-Seite) wird erreicht:
Das Erstellen des vollständigen Satzes von Änderungshilfen ist offensichtlich ärgerlich. Für interne Inhalte ist es jedoch häufig in Ordnung, sie nur zu erstellen, wenn Sie zum ersten Mal versuchen, ein bestimmtes verschachteltes Feld zu ändern.
quelle
Vielleicht passt QuickLens besser zu Ihrer Frage. QuickLens verwendet Makros, um einen IDE-freundlichen Ausdruck in etwas zu konvertieren, das der ursprünglichen Kopieranweisung nahe kommt.
Angesichts der beiden Beispielfallklassen:
und die Instanz der Personenklasse:
Sie können die Postleitzahl von Raj aktualisieren mit:
quelle