Скорочений спосіб призначення одного поля в записі, а копіювання решти полів?


119

Скажімо, у мене є такий запис ADT:

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

Я хочу функцію, яка бере запис і повертає запис (того ж типу), де всі поля, окрім одного, мають однакові значення з тим, що передано як аргумент, наприклад:

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

Вищезазначене працює, але для запису з більшою кількістю полів (скажімо 10), створення такої функції спричинило б за собою багато введення тексту, що мені здається зовсім непотрібним.

Чи є якісь менш виснажливі способи зробити те саме?


3
Синтаксис запису для оновлення існує, але швидко стає громіздким. Замість цього подивіться на лінзи .
Cat Plus Plus

Відповіді:


155

Так, є приємний спосіб оновлення полів запису. У GHCi ви можете -

> 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
RecordWildCardsРозширення може бути добре , як добре, щоб «Видобути» полів в області. Для оновлень це не так приємно:incrementA x@Foo{..} = x { a = succ a }
Джон Перді,

2
BTW, у Frege (Haskell для JVM) ви б визначили функцію як updateFoo x = x.{ c = "Goodbye" }(зверніть увагу на .оператора).
0dB


Дякую. На жаль, давно минуло, як я написав будь-який Haskell!
Кріс Тейлор

37

Це хороша робота для лінз :

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

test = Foo 1 2 "Hello"

Тоді:

setL c "Goodbye" test

буде оновлено поле "c" "test" для вашої рядка.


5
А об'єкти-подібні пакети часто визначають операторів на додаток до функцій отримання та налаштування полів. Наприклад, test $ c .~ "Goodbye"як lensце зробить iirc. Я не кажу, що це нав'язливо, але, як тільки ти знаєш операторів, тоді я очікую, що це стане так просто $.
Thomas M. DuBuisson

3
Ви знаєте, куди пішов setL ? Я імпортую Control.Lens , але ghc повідомляє, що setL не визначено.
dbanas

1
використовувати set замість setL
Subhod I

16

Вам не потрібно визначати допоміжні функції або використовувати об’єктиви. Стандартний Haskell вже має те, що вам потрібно. Візьмемо для прикладу Дон Стюарт:

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

test = Foo 1 2 "Hello"

Тоді ви можете просто сказати, test { c = "Goodbye" }щоб отримати оновлений запис.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.