Powiedzmy, że mam następujące dwa case class
es:
case class Address(street: String, city: String, state: String, zipCode: Int)
case class Person(firstName: String, lastName: String, address: Address)
i następujące wystąpienie Person
klasy:
val raj = Person("Raj", "Shekhar", Address("M Gandhi Marg",
"Mumbai",
"Maharashtra",
411342))
Teraz, jeśli chcę zaktualizować zipCode
z raj
czym będę miał do zrobienia:
val updatedRaj = raj.copy(address = raj.address.copy(zipCode = raj.address.zipCode + 1))
Im więcej poziomów zagnieżdżenia, tym brzydszy. Czy istnieje czystszy sposób (podobny do Clojure update-in
) na aktualizację takich zagnieżdżonych struktur?
scala
case-class
zipper
missingfaktor
źródło
źródło
Odpowiedzi:
Zamki błyskawiczne
Huet's Zipper zapewnia wygodne przechodzenie i „mutację” niezmiennej struktury danych. Scalaz dostarcza Zippers dla
Stream
( scalaz.Zipper ) iTree
( scalaz.TreeLoc ). Okazuje się, że struktura zamka błyskawicznego jest automatycznie wyprowadzana z pierwotnej struktury danych, w sposób przypominający symboliczne zróżnicowanie wyrażenia algebraicznego.Ale w jaki sposób pomoże Ci to w Twoich klasach przypadków Scala? Cóż, Lukas Rytz niedawno opracował prototyp rozszerzenia do scalaca, które automatycznie tworzy suwaki dla klas przypadków z adnotacjami. Przedstawię tutaj jego przykład:
Dlatego społeczność musi przekonać zespół Scala, że ten wysiłek powinien być kontynuowany i zintegrowany z kompilatorem.
Nawiasem mówiąc, Lukas niedawno opublikował wersję Pacmana, programowalną przez użytkownika przez DSL. Wygląda jednak na to, że nie korzystał ze zmodyfikowanego kompilatora, ponieważ żadnego nie widzę
@zip
adnotacji.Przepisywanie drzewa
W innych okolicznościach możesz chcieć zastosować pewną transformację w całej strukturze danych, zgodnie z pewną strategią (odgórną, oddolną) i na podstawie reguł dopasowanych do wartości w pewnym punkcie struktury. Klasycznym przykładem jest przekształcenie AST dla języka, być może w celu oceny, uproszczenia lub zebrania informacji. Kiama obsługuje Rewriting , zobacz przykłady w RewriterTests i obejrzyj ten film . Oto fragment, który zaostrzy apetyt:
Zauważ, że Kiama wychodzi poza system czcionek, aby to osiągnąć.
źródło
Zabawne, że nikt nie dodał soczewek, ponieważ zostały stworzone do tego typu rzeczy. Więc tutaj jest tło papieru CS na nim, tutaj jest blog, który na krótko dotykowy obiektywów używać w Scala, tutaj jest realizacja soczewki dla Scalaz i tutaj jest jakiś kod używając go, co wygląda zaskakująco podobne pytanie. I, aby zmniejszyć liczbę kotłów, oto wtyczka, która generuje soczewki Scalaz dla klas przypadków.
Jeśli chodzi o punkty dodatkowe, oto kolejne pytanie SO, które dotyczy soczewek i artykuł Tony'ego Morrisa.
Najważniejsze w soczewkach jest to, że można je komponować. Na początku są trochę uciążliwe, ale im częściej ich używasz, zyskują na popularności. Ponadto są świetne do testowania, ponieważ wystarczy przetestować pojedyncze soczewki i można przyjąć ich skład za pewnik.
Tak więc, w oparciu o implementację podaną na końcu tej odpowiedzi, oto jak to zrobić z soczewkami. Najpierw zadeklaruj soczewki do zmiany kodu pocztowego w adresie i adresu w osobie:
Teraz utwórz je, aby uzyskać soczewkę, która zmienia kod pocztowy w osobie:
Na koniec użyj tego obiektywu, aby zmienić raj:
Lub używając cukru syntaktycznego:
Lub nawet:
Oto prosta implementacja, zaczerpnięta ze Scalaz, użyta w tym przykładzie:
źródło
personZipCodeLens.set(raj, personZipCodeLens.get(raj) + 1)
jest taki sam jakpersonZipCodeLens mod (raj, _ + 1)
mod
nie jest prymitywem dla soczewek.Przydatne narzędzia do korzystania z soczewek:
Chcę tylko dodać, że projekty Macrocosm i Rillit , oparte na makrach Scala 2.10, zapewniają dynamiczne tworzenie soczewek.
Korzystanie z Rillit:
Korzystanie z Macrocosm:
źródło
Rozglądałem się za biblioteką Scala, która ma najładniejszą składnię i najlepszą funkcjonalność, a jedna biblioteka nie wymieniona tutaj to monokl, który dla mnie był naprawdę dobry. Oto przykład:
Są bardzo ładne i istnieje wiele sposobów łączenia soczewek. Na przykład Scalaz wymaga dużo gotowych rozwiązań, a to szybko się kompiluje i działa świetnie.
Aby użyć ich w swoim projekcie, po prostu dodaj to do swoich zależności:
źródło
Shapeless załatwia sprawę:
z:
Zauważ, że podczas gdy niektóre inne odpowiedzi tutaj pozwalają ci komponować soczewki, aby zagłębić się w daną strukturę, te bezkształtne soczewki (i inne biblioteki / makra) pozwalają ci połączyć dwie niepowiązane soczewki tak, że możesz zrobić soczewkę, która ustawia dowolną liczbę parametrów w dowolnych pozycjach w twojej strukturze. W przypadku złożonych struktur danych dodatkowa kompozycja jest bardzo pomocna.
źródło
Lens
kodu w odpowiedzi Daniela C. Sobrala i uniknąłem dodania zewnętrznej zależności.Soczewki ze względu na swoją kompozycyjną naturę stanowią bardzo dobre rozwiązanie problemu mocno zagnieżdżonych struktur. Jednak przy niskim poziomie zagnieżdżenia czasami wydaje mi się, że soczewki są trochę za duże i nie chcę wprowadzać całego podejścia do soczewek, jeśli jest tylko kilka miejsc z zagnieżdżonymi aktualizacjami. W trosce o kompletność, oto bardzo proste / pragmatyczne rozwiązanie dla tego przypadku:
To, co robię, to po prostu napisanie kilku
modify...
funkcji pomocniczych w strukturze najwyższego poziomu, które zajmują się brzydką zagnieżdżoną kopią. Na przykład:Mój główny cel (uproszczenie aktualizacji po stronie klienta) został osiągnięty:
Tworzenie pełnego zestawu pomocników modyfikacji jest oczywiście denerwujące. Jednak w przypadku elementów wewnętrznych często wystarczy je po prostu utworzyć przy pierwszej próbie zmodyfikowania określonego pola zagnieżdżonego.
źródło
Być może QuickLens lepiej pasuje do Twojego pytania. QuickLens używa makr do konwersji przyjaznego dla IDE wyrażenia na coś, co jest zbliżone do oryginalnej instrukcji copy.
Biorąc pod uwagę dwie przykładowe klasy przypadków:
oraz instancja klasy Person:
możesz zaktualizować kod pocztowy raj z:
źródło