Skrótowy sposób przypisania pojedynczego pola w rekordzie, podczas kopiowania pozostałych pól?

119

Powiedzmy, że mam następujący rekord ADT:

data Foo = Bar { a :: Integer, b :: String, c :: String }

Chcę funkcji, która pobiera rekord i zwraca rekord (tego samego typu), w którym wszystkie pola oprócz jednego mają identyczne wartości, jak te przekazane jako argument, na przykład:

walkDuck x = Bar { a = a x, b = b x, c = lemonadeStand (a x) (b x) }

Powyższe działa, ale dla rekordu z większą liczbą pól (powiedzmy 10), stworzenie takiej funkcji wymagałoby dużo pisania, które uważam za zupełnie niepotrzebne.

Czy są jakieś mniej żmudne sposoby na zrobienie tego samego?

jaymmer - Przywróć Monikę
źródło
3
Rekordowa składnia aktualizacji istnieje, ale szybko staje się uciążliwa. Zamiast tego spójrz na soczewki .
Cat Plus Plus

Odpowiedzi:

155

Tak, istnieje fajny sposób aktualizowania pól rekordów. W GHCi możesz -

> data Foo = Foo { a :: Int, b :: Int, c :: String }  -- define a Foo
> let foo = Foo { a = 1, b = 2, c = "Hello" }         -- create a Foo
> let updateFoo x = x { c = "Goodbye" }               -- function to update Foos
> updateFoo foo                                       -- update the Foo
Foo {a = 1, b = 2, c = "Goodbye" }
Chris Taylor
źródło
9
RecordWildCardsRozszerzenie może być miłe, jak również, aby „rozpakować” pól w zakresie. Jeśli chodzi o aktualizacje, nie jest to jednak tak przyjemne:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy,
2
BTW, w Frege (Haskell dla JVM) można zdefiniować funkcję jako updateFoo x = x.{ c = "Goodbye" }(zwróć uwagę na .operator).
0dB
Przy okazji
fajny
Dzięki. Niestety dawno nie napisałem żadnego Haskella!
Chris Taylor,
37

To dobra praca dla soczewek :

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

Następnie:

setL c "Goodbye" test

zaktualizowałoby pole „c” z „test” do twojego ciągu.

Don Stewart
źródło
5
A pakiety podobne do soczewek często definiują operatory oprócz funkcji pobierania i ustawiania pól. Na przykład, test $ c .~ "Goodbye"jak lensby to zrobić iirc. Nie mówię, że jest to nietrwałe, ale kiedy już znasz operatorów, spodziewam się, że przyjdzie to równie łatwo, jak $.
Thomas M. DuBuisson
3
Czy wiesz, dokąd poszedł setL ? Importuję Control.Lens , ale ghc zgłasza, że setL jest niezdefiniowane.
dbanas
1
użyj set zamiast setL
Subhod I
16

Nie musisz definiować funkcji pomocniczych ani używać soczewek. Standardowy Haskell ma już to, czego potrzebujesz. Weźmy przykład Dona Stewarta:

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

Następnie możesz po prostu powiedzieć, test { c = "Goodbye" }aby uzyskać zaktualizowany rekord.

Wolfgang Jeltsch
źródło