Cara singkat untuk menetapkan satu bidang dalam catatan, sambil menyalin bidang lainnya?

119

Katakanlah saya memiliki catatan ADT berikut:

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

Saya ingin fungsi yang mengambil record dan mengembalikan record (dengan tipe yang sama) di mana semua kecuali satu bidang memiliki nilai yang identik dengan yang diteruskan sebagai argumen, seperti:

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

Cara di atas berfungsi, tetapi untuk rekaman dengan lebih banyak bidang (katakanlah 10), membuat fungsi seperti itu akan memerlukan banyak pengetikan yang menurut saya tidak terlalu diperlukan.

Apakah ada cara yang tidak terlalu membosankan untuk melakukan hal yang sama?

jaymmer - Kembalikan Monica
sumber
3
Rekam sintaks untuk memperbarui ada, tetapi dengan cepat menjadi rumit. Lihatlah lensa sebagai gantinya.
Cat Plus Plus

Jawaban:

155

Ya, ada cara yang bagus untuk memperbarui bidang rekaman. Di GHCi Anda dapat melakukan -

> 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
sumber
9
The RecordWildCardsekstensi bisa bagus juga, untuk “membongkar” bidang dalam lingkup. Untuk pembaruan, itu tidak terlalu bagus:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy
2
BTW, di Frege (Haskell untuk JVM) Anda akan mendefinisikan fungsi sebagai updateFoo x = x.{ c = "Goodbye" }(perhatikan .operatornya).
0dB
Video yang bagus dengan cara youtube.com/watch?v=YScIPA8RbVE
Damián Rafael Lattenero
Terima kasih. Sayangnya sudah lama sekali saya tidak menulis Haskell!
Chris Taylor
37

Ini adalah pekerjaan yang bagus untuk lensa :

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

test = Foo 1 2 "Hello"

Kemudian:

setL c "Goodbye" test

akan memperbarui bidang 'c' dari 'test' ke string Anda.

Don Stewart
sumber
5
Dan paket seperti lensa sering kali menentukan operator selain fungsi untuk mendapatkan dan menyetel bidang. Misalnya, test $ c .~ "Goodbye"adalah bagaimana lensmelakukannya iirc. Saya tidak mengatakan ini intutitif, tetapi begitu Anda mengetahui operatornya maka saya berharap itu akan datang semudah $.
Thomas M. DuBuisson
3
Apakah Anda tahu kemana perginya setL ? Saya mengimpor Control.Lens , tetapi ghc melaporkan bahwa setL tidak ditentukan.
dbanas
1
gunakan set sebagai ganti setL
Subhod I
16

Anda tidak perlu menentukan fungsi tambahan atau menggunakan lensa. Standar Haskell sudah memiliki apa yang Anda butuhkan. Mari kita ambil contoh oleh Don Stewart:

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

test = Foo 1 2 "Hello"

Kemudian Anda bisa mengatakan test { c = "Goodbye" }untuk mendapatkan catatan yang diperbarui.

Wolfgang Jeltsch
sumber