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)
Другие вопросы по тегам