¿Una forma abreviada de asignar un solo campo en un registro, mientras se copia el resto de los campos?

119

Digamos que tengo el siguiente registro ADT:

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

Quiero una función que tome un registro y devuelva un registro (del mismo tipo) donde todos menos uno de los campos tienen valores idénticos al que se pasó como argumento, así:

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

Lo anterior funciona, pero para un registro con más campos (digamos 10), crear una función de este tipo implicaría una gran cantidad de escritura que creo que es bastante innecesaria.

¿Hay formas menos tediosas de hacer lo mismo?

jaymmer - Restablecer a Monica
fuente
3
Existe sintaxis de registro para la actualización, pero rápidamente se vuelve engorrosa. En su lugar, eche un vistazo a las lentes .
Cat Plus Plus

Respuestas:

155

Sí, existe una buena forma de actualizar los campos de registro. En GHCi puedes hacer -

> 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
fuente
9
La RecordWildCardsextensión también puede ser buena para "descomprimir" campos en un ámbito. Sin embargo, para las actualizaciones no es tan agradable:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy
2
Por cierto, en Frege (un Haskell para la JVM) definiría la función como updateFoo x = x.{ c = "Goodbye" }(observe el .operador).
0dB
Buen video por cierto youtube.com/watch?v=YScIPA8RbVE
Damián Rafael Lattenero 11/11/19
Gracias. Lamentablemente, ¡ha pasado mucho tiempo desde que escribí Haskell!
Chris Taylor
37

Este es un buen trabajo para lentes :

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

test = Foo 1 2 "Hello"

Luego:

setL c "Goodbye" test

actualizaría el campo 'c' de 'prueba' a su cadena.

Don Stewart
fuente
5
Y los paquetes tipo lentes a menudo definen operadores además de funciones para obtener y configurar campos. Por ejemplo, test $ c .~ "Goodbye"es cómo lenslo haría iirc. No estoy diciendo que esto sea intuitivo, pero una vez que conozca los operadores, espero que sea tan fácil como $.
Thomas M. DuBuisson
3
¿Sabes adónde se ha ido setL ? Estoy importando Control.Lens , pero ghc informa que setL no está definido.
Dbanas
1
use set en lugar de setL
Subhod I
16

No es necesario definir funciones auxiliares ni emplear lentes. Haskell estándar ya tiene lo que necesitas. Tomemos el ejemplo de Don Stewart:

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

test = Foo 1 2 "Hello"

Entonces puede simplemente decir test { c = "Goodbye" }que obtenga un registro actualizado.

Wolfgang Jeltsch
fuente