Сокращенный способ назначения одного поля в записи при копировании остальных полей?


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
Кстати, во 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 08

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.