Геттер и сеттер, объединенные в первоклассное значение, называются линзами. Для этого существует довольно много пакетов; наиболее популярными являются data-lens и fclabels. Этот предыдущий вопрос SO является хорошее введение.
Обе эти библиотеки поддерживают получение объективов из определений записей с использованием Template Haskell (с data-lens он предоставляется как дополнительный пакет для переносимости). Ваш пример будет выражен как (с использованием синтаксиса линзы данных):
setL idxF_s (b ^. idL_s) a
(или эквивалентно: idxF_s ^= (b ^. idL_s) $ a
)
Вы можете, конечно, преобразовать линзы общим способом, преобразовав их геттер и сеттер вместе:
-- I don't know what swap_by_sign is supposed to do.
negateLens :: (Num b) => Lens a b -> Lens a b
negateLens l = lens get set
where
get = negate . getL l
set = setL l . negate
(или эквивалентно: negateLens l = iso negate negate . l
1)
В общем, я бы рекомендовал использовать линзы всякий раз, когда вам приходится иметь дело с какой-либо нетривиальной обработкой записей; они не только значительно упрощают чистое преобразование записей, но оба пакета содержат удобные функции для доступа и изменения состояния монады состояния с помощью линз, что невероятно полезно. (Для объектива данных вы можете использовать data-lens-fd для использования этих удобных функций в любом MonadState
; опять же, они находятся в отдельном пакете для переносимости.)
1 При использовании любого из пакетов вы должны начинать свои модули с:
import Prelude hiding (id, (.))
import Control.Category
Это связано с тем, что они используют обобщенные формы функций Prelude id
и (.)
— id
можно использовать в качестве линзы от любого значения к самой себе (правда, не так уж полезно), а (.)
используется для составления линз (например, getL (fieldA . fieldB) a
то же самое, что и getL fieldA . getL fieldB $ a
). Это используется в более коротком определении negateLens
.
person
ehird
schedule
04.02.2012