Modo abbreviato per assegnare un singolo campo in un record, mentre si copia il resto dei campi?


119

Diciamo che ho il seguente record ADT:

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

Voglio una funzione che prende un record e restituisce un record (dello stesso tipo) in cui tutti i campi tranne uno hanno valori identici a quello passato come argomento, in questo modo:

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

Quanto sopra funziona, ma per un record con più campi (diciamo 10), la creazione di una tale funzione comporterebbe un sacco di digitazione che ritengo sia abbastanza inutile.

Esistono modi meno noiosi per fare lo stesso?


3
La sintassi dei record per l'aggiornamento esiste, ma diventa rapidamente complicata. Dai un'occhiata invece alle lenti .
Cat Plus Plus

Risposte:


155

Sì, c'è un bel modo per aggiornare i campi dei record. In GHCi puoi fare -

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

9
Anche l' RecordWildCardsestensione può essere utile, per "decomprimere" i campi in uno scope. Per gli aggiornamenti però non è così bello:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy

2
A proposito, in Frege (un Haskell per la JVM) definiresti la funzione come updateFoo x = x.{ c = "Goodbye" }(nota l' .operatore).
0dB


Grazie. Purtroppo è passato molto tempo da quando ho scritto Haskell!
Chris Taylor,

37

Questo è un buon lavoro per le lenti :

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

test = Foo 1 2 "Hello"

Poi:

setL c "Goodbye" test

aggiorna il campo "c" di "test" alla tua stringa.


5
E i pacchetti simili a lenti spesso definiscono gli operatori oltre alle funzioni per ottenere e impostare i campi. Ad esempio, test $ c .~ "Goodbye"è come lenslo farei iirc. Non sto dicendo che sia intuitivo, ma una volta che conosci gli operatori, mi aspetto che venga facilmente come $.
Thomas M. DuBuisson

3
Sai dove è andato setL ? Sto importando Control.Lens , ma ghc segnala che setL non è definito.
dbanas

1
usa set invece di setL
Subhod I

16

Non è necessario definire funzioni ausiliarie o utilizzare obiettivi. Standard Haskell ha già ciò di cui hai bisogno. Prendiamo l'esempio di Don Stewart:

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

test = Foo 1 2 "Hello"

Quindi puoi semplicemente dire test { c = "Goodbye" }di ottenere un record aggiornato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.