У Haskell есть указатели / ссылки на участников записи?
Я могу создавать и ссылаться на относительные указатели для структурирования членов в C++, используя ::*
, .*
, а также ->*
синтаксис вроде:
char* fstab_t::*field = &fstab_t::fs_vfstype;
my_fstab.*field = ...
В Haskell я могу легко создавать временные метки для получателей записей, такие как:
(idxF_s,idxL_s) = swap_by_sign sgn (idxF,idxL) ;
Afaik, я не могу, однако, затем обновлять записи, используя эти геттеры как метки:
a { idxF_s = idxL_s b }
Есть ли простой способ сделать это без кодирования для каждого установщика записей?
2 ответа
Геттер и сеттер, объединенные в первоклассное значение, называются линзой. Есть довольно много пакетов для этого; наиболее популярными являются 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 вы захотите использовать пакет data-lens-fd, чтобы использовать эти удобные функции в любом MonadState
; опять же, они в отдельном пакете для переносимости.)
1 При использовании любого из пакетов вы должны запустить свои модули с:
import Prelude hiding (id, (.))
import Control.Category
Это потому, что они используют обобщенные формы прелюдии id
а также (.)
функции - id
может быть использован в качестве объектива от любого значения для себя (не все, что полезно, по общему признанию), и (.)
используется для составления линз (например, getL (fieldA . fieldB) a
такой же как getL fieldA . getL fieldB $ a
). Короче negateLens
определение использует это.
Здесь вам нужны первоклассные лейблы звукозаписи, и хотя этого нет в языке, в Hackage есть несколько пакетов, которые реализуют этот шаблон. Одним из них является fclabels, который может использовать Template Haskell для создания необходимого шаблона для вас. Вот пример:
{-# LANGUAGE TemplateHaskell #-}
import Control.Category
import Data.Label
import Prelude hiding ((.))
data Foo = Foo { _fieldA :: Int, _fieldB :: Int }
deriving (Show)
$(mkLabels [''Foo])
main = do
let foo = Foo 2 3
putStrLn "Pick a field, A or B"
line <- getLine
let field = (if line == "A" then fieldA else fieldB)
print $ modify field (*10) foo