Un raccourci pour affecter un seul champ dans un enregistrement, tout en copiant le reste des champs?

119

Disons que j'ai l'enregistrement ADT suivant:

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

Je veux une fonction qui prend un enregistrement et renvoie un enregistrement (du même type) où tous les champs sauf un ont des valeurs identiques à celui passé en argument, comme ceci:

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

Ce qui précède fonctionne, mais pour un enregistrement avec plus de champs (par exemple 10), la création d'une telle fonction impliquerait beaucoup de frappe qui, à mon avis, est tout à fait inutile.

Existe-t-il des moyens moins fastidieux de faire de même?

jaymmer - Réintégrer Monica
la source
3
La syntaxe d'enregistrement pour la mise à jour existe, mais devient rapidement fastidieuse. Jetez plutôt un œil aux lentilles .
Cat Plus Plus

Réponses:

155

Oui, il existe un bon moyen de mettre à jour les champs d'enregistrement. Dans GHCi, vous pouvez faire -

> 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
la source
9
L' RecordWildCardsextension peut également être intéressante pour «décompresser» les champs d'un scope. Pour les mises à jour, ce n'est pas aussi bien cependant:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy
2
BTW, dans Frege (un Haskell pour la JVM), vous définiriez la fonction comme updateFoo x = x.{ c = "Goodbye" }(notez l' .opérateur).
0 dB le
Belle vidéo d'ailleurs youtube.com/watch?v=YScIPA8RbVE
Damián Rafael Lattenero
Merci. Malheureusement, il y a longtemps que je n'ai pas écrit de Haskell!
Chris Taylor
37

C'est un bon travail pour les objectifs :

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

test = Foo 1 2 "Hello"

Ensuite:

setL c "Goodbye" test

mettrait à jour le champ «c» de «test» avec votre chaîne.

Don Stewart
la source
5
Et les packages de type lentille définissent souvent des opérateurs en plus des fonctions d'obtention et de définition de champs. Par exemple, test $ c .~ "Goodbye"comment le lensferait iirc. Je ne dis pas que c'est intuitif, mais une fois que vous connaissez les opérateurs, je pense que cela viendra aussi facilement que $.
Thomas M. DuBuisson
3
Savez-vous où setL est allé? Je l' importation Control.Lens , mais GHC rapporte que SETL est indéfini.
dbanas
1
utiliser set au lieu de setL
Subhod I
16

Vous n'avez pas besoin de définir des fonctions auxiliaires ou d'utiliser des objectifs. Standard Haskell a déjà ce dont vous avez besoin. Prenons l'exemple de Don Stewart:

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

test = Foo 1 2 "Hello"

Ensuite, vous pouvez simplement dire test { c = "Goodbye" }pour obtenir un enregistrement mis à jour.

Wolfgang Jeltsch
la source