Есть ли в Haskell указатели/ссылки на членов записи?

Я могу создавать и ссылаться на относительные указатели на элементы структуры в C++, используя синтаксис ::*, .* и ->*, например:

char* fstab_t::*field = &fstab_t::fs_vfstype;
my_fstab.*field = ...

В Haskell я могу легко создавать временные метки для получения записей, например:

(idxF_s,idxL_s) = swap_by_sign sgn (idxF,idxL) ;

Афаик, однако я не могу обновлять записи, используя эти геттеры в качестве меток, например:

a { idxF_s = idxL_s b }

Есть ли простой способ сделать это без кодирования для каждого рекордсмена?


person Jeff Burdges    schedule 04.02.2012    source источник
comment
Указатель на операторы-члены не C, а C++. Сменить тег.   -  person Bo Persson    schedule 05.02.2012


Ответы (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 . l1)

В общем, я бы рекомендовал использовать линзы всякий раз, когда вам приходится иметь дело с какой-либо нетривиальной обработкой записей; они не только значительно упрощают чистое преобразование записей, но оба пакета содержат удобные функции для доступа и изменения состояния монады состояния с помощью линз, что невероятно полезно. (Для объектива данных вы можете использовать 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

Здесь вам нужны первоклассные звукозаписывающие компании, и хотя в языке их нет, на 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
person hammar    schedule 04.02.2012