Cách tốc ký để gán một trường duy nhất trong một bản ghi, trong khi sao chép các trường còn lại?


118

Giả sử tôi có bản ghi ADT sau:

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

Tôi muốn một hàm lấy một bản ghi và trả về một bản ghi (cùng loại) trong đó tất cả trừ một trong các trường có các giá trị giống hệt với một được truyền dưới dạng đối số, như vậy:

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

Các công việc trên, nhưng đối với một bản ghi có nhiều trường hơn (giả sử 10), việc tạo một hàm như vậy sẽ đòi hỏi rất nhiều cách gõ mà tôi cảm thấy là không cần thiết.

Có cách nào ít tẻ nhạt hơn để làm như vậy?


3
Ghi cú pháp để cập nhật tồn tại, nhưng nhanh chóng trở nên cồng kềnh. Hãy nhìn vào ống kính thay thế.
Cat Plus Plus

Câu trả lời:


154

Vâng, có một cách tốt đẹp để cập nhật các trường bản ghi. Trong GHCi bạn có thể làm -

> 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
Các RecordWildCardsphần mở rộng có thể được tốt đẹp là tốt, để các lĩnh vực “giải nén” trong một phạm vi. Đối với các bản cập nhật, nó không hoàn toàn đẹp như sau:incrementA x@Foo{..} = x { a = succ a }
Jon Purdy

2
BTW, trong Frege (một Haskell cho JVM), bạn sẽ định nghĩa hàm là updateFoo x = x.{ c = "Goodbye" }(lưu ý .toán tử).
0dB

Video tuyệt vời bằng cách youtube.com/watch?v=YScIPA8RbVE
Damián Rafael Lattenero

Cảm ơn. Đáng buồn là một thời gian dài kể từ khi tôi viết bất kỳ Haskell!
Chris Taylor

37

Đây là một công việc tốt cho ống kính :

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

test = Foo 1 2 "Hello"

Sau đó:

setL c "Goodbye" test

sẽ cập nhật trường 'c' của 'test' vào chuỗi của bạn.


5
Và các gói giống như ống kính thường xác định các toán tử ngoài các chức năng để nhận và thiết lập các trường. Ví dụ, test $ c .~ "Goodbye"là làm thế nào lensđể làm điều đó. Tôi không nói điều này là không tích cực, nhưng một khi bạn biết các nhà khai thác thì tôi hy vọng nó sẽ đến dễ dàng như vậy $.
Thomas M. DuBuisson

3
Bạn có biết setL đã đi đâu không? Tôi đang nhập Control.Lens , nhưng ghc đang báo cáo rằng setL không xác định.
dbanas

1
sử dụng set thay vì setL
Subhod I

16

Bạn không cần xác định chức năng phụ trợ hoặc sử dụng ống kính. Tiêu chuẩn Haskell đã có những gì bạn cần. Hãy lấy ví dụ của Don Stewart:

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

test = Foo 1 2 "Hello"

Sau đó, bạn chỉ có thể nói test { c = "Goodbye" }để có được một bản ghi cập nhật.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.