2013-02-19 5 views
88

Sagen wir, ich den folgenden Datensatz ADT haben:Kurzform für die Zuweisung eines einzelnen Feldes in einem Datensatz, während die restlichen Felder kopiert werden?

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

Ich möchte eine Funktion, die einen Datensatz nimmt und einen Datensatz (vom gleichen Typ), wobei alle bis auf einen der Felder der einen identischen Werten bestanden haben, als Argument, etwa so:

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

die oben genannten Arbeiten, aber für einen Datensatz mit mehreren Feldern (zB 10), würde eine solche Funktion zu schaffen eine Menge Tipparbeit mit sich bringen, die ich fühle mich ganz unnötig ist.

Gibt es weniger mühsame Möglichkeiten, das Gleiche zu tun?

+2

Record-Syntax für die Aktualisierung existiert, wird aber schnell umständlich. Sehen Sie sich stattdessen [Linsen] (http://stackoverflow.com/questions/5767129/lenses-fclabels-data-accessor-which-library-for-structure-access-and-mutatio) an. –

Antwort

115

Ja, es gibt eine nette Möglichkeit, Datensatzfelder zu aktualisieren. In GHCi können Sie tun -

> 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" } 
+4

Die 'RecordWildCards' Erweiterung kann auch gut sein, um Felder in einem Bereich zu" entpacken ". Für Updates ist es allerdings nicht ganz so schön: 'incrementA x @ Foo {..} = x {a = succ a}' –

+1

Übrigens, in Frege (ein Haskell für die JVM) würden Sie die Funktion '' 'updateFoo x definieren = x. {c = "Auf Wiedersehen"} '' '(beachten Sie den' '' .''' Operator). – 0dB

28

Dies ist ein guter Job für lenses:

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

test = Foo 1 2 "Hello" 

Dann:

setL c "Goodbye" test 

aktualisieren Feld 'c' würde von 'Test' auf Ihre Zeichenfolge.

+4

Und Objektive-ähnliche Pakete definieren oft Operatoren zusätzlich zu Funktionen zum Abrufen und Einstellen von Feldern. Zum Beispiel, 'test $ c. ~" Auf Wiedersehen "' ist, wie 'Linse' es in der Regel tun würde. Ich sage nicht, dass dies intuitiv ist, aber sobald Sie die Operatoren kennen, dann erwarte ich, dass es so einfach wie '$' kommen würde. –

+2

Wissen Sie wo _setL_ gegangen ist? Ich importiere _Control.Lens_, aber ghc meldet, dass _setL_ nicht definiert ist. – dbanas