Control.Lens объектив для общей карты
У меня есть общая карта от типа A до типа B.
import qualified Data.Map as M
import Data.Maybe
tmGet k m = fromJust $ M.lookup k m
tmSet k v = M.insert k v
я использовал Data.Map
в качестве примера реализации, но это может быть что угодно, например Array
или даже Bool
Индексированный кортеж:
tmGet True = fst
tmGet False = snd
Я хочу иметь tmAt
Функция для построения объектива:
(42, 665) ^. tmAt True == 42
(42, 665) & tmAt False +~ 1 == (42, 666)
Вопрос в том, как мне построить tmAt
снаружи tmGet
а также tmSet
(или же tmModify
)?
2 ответа
Если вы посмотрите документацию к модулю Control.Lens, вы увидите очень удобное изображение различных частей упаковки объектива. Так как вы хотите построить Lens
, вы можете посмотреть на Lens
часть диаграммы. Самая верхняя показанная функция
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b
Это создает объектив из геттера и сеттера.
Функция s -> a
является получателем - подпись типа означает "Если вы дадите мне структуру данных s
Я выберу a
ценность из этого." s -> b -> t
Функция является установщиком, а подпись типа означает: "Если вы дадите мне s
и новое значение b
Я создам для вас новую структуру t
." (Типы разные, потому что линзы действительно могут менять типы вещей.)
Если ваш геттер tmGet
и ваш сеттер tmSet
поэтому вы можете построить объектив с
tmAt :: Boolean -> Lens s t a b
tmAt b = lens (tmGet b) (tmSet b)
за то, что вы на самом деле s
, t
, a
, а также b
параметры есть. В примере кортежа это будет
tmAt :: Bool -> Lens (a, a) (a, a) a a
(Другими словами, если вы даете объективу функцию a -> a
, это может преобразовать (a, a)
-тупить в другое (a, a)
-кратный.)
Если вы хотите быть модным, вы также можете переписать tmAt
как
tmAt = lens <$> tmGet <*> tmSet
Как только вы определили tmGet
а также tmSet
быть типом
tmGet :: k -> f v -> v
tmSet :: k -> f v -> v -> f v
Вы можете определить семейство линз, подавая конструктор линз lens
с вашими параметризованными геттерами и сеттерами. Вот как бы вы сделали это для пары, проиндексированной Bool
tmGet True = fst
tmGet False = snd
tmSet True (_,b) a = (a,b)
tmSet False (a,_) b = (a,b)
tmAt b = lens (tmGet b) (tmSet b)
Сейчас в GHCI
>>> (42,665) ^. tmAt True
42
>>> (42,665) & tmAt False +~ 1
(42,666)