Maneira abreviada de atribuir um único campo em um registro, enquanto copia o restante dos campos?


119

Digamos que eu tenha o seguinte registro ADT:

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

Eu quero uma função que leva um registro e retorna um registro (do mesmo tipo) onde todos, exceto um dos campos têm valores idênticos ao passado como argumento, assim:

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

O procedimento acima funciona, mas para um registro com mais campos (digamos 10), a criação de tal função envolveria muita digitação que considero desnecessária.

Existem maneiras menos tediosas de fazer o mesmo?


3
A sintaxe de registro para atualização existe, mas fica rapidamente complicada. Em vez disso, uma olhada nas lentes .
Cat Plus Plus

Respostas:


155

Sim, existe uma boa maneira de atualizar os campos de registro. No GHCi você pode fazer -

> 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
A RecordWildCardsextensão também pode ser boa, para “desempacotar” campos em um escopo. Porém, para atualizações não é tão bom:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy

2
BTW, em Frege (um Haskell para o JVM) você definiria a função como updateFoo x = x.{ c = "Goodbye" }(observe o .operador).
0dB de


Obrigado. Infelizmente, já faz muito tempo que não escrevo Haskell!
Chris Taylor

37

Este é um bom trabalho para lentes :

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

test = Foo 1 2 "Hello"

Então:

setL c "Goodbye" test

atualizaria o campo 'c' de 'teste' para sua string.


5
E pacotes semelhantes a lentes geralmente definem operadores, além de funções para obter e definir campos. Por exemplo, test $ c .~ "Goodbye"é como lensfaria isso iirc. Não estou dizendo que seja intuitivo, mas uma vez que você conhece os operadores, espero que seja tão fácil quanto $.
Thomas M. DuBuisson

3
Você sabe para onde foi setL ? Estou importando Control.Lens , mas ghc está relatando que setL é indefinido.
dbanas de

1
use set em vez de setL
Subhod I

16

Você não precisa definir funções auxiliares ou empregar lentes. O Haskell padrão já tem o que você precisa. Vejamos o exemplo de Don Stewart:

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

test = Foo 1 2 "Hello"

Então você pode simplesmente dizer test { c = "Goodbye" }para obter um registro atualizado.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.