Сочетание линз
Используя библиотеку объектив, я могу применить функцию модификации к отдельным целям, например:
Prelude Control.Lens> (1, 'a', 2) & _1 %~ (*3)
(3,'a',2)
Prelude Control.Lens> (1, 'a', 2) & _3 %~ (*3)
(1,'a',6)
Как я могу объединить эти отдельные линзы (_1
и _3
), чтобы иметь возможность выполнить это обновление для обеих целей одновременно? Я ожидаю чего-то в духе следующего:
Prelude Control.Lens> (1, 'a', 2) & ??? %~ (*3)
(3,'a',6)
Ответы
Ответ 1
Используя untainted
из класса типа Settable
в Control.Lens.Internal.Setter
, можно объединить два сеттера, но результатом будет также только сеттер, а не получатель.
import Control.Lens.Internal.Setter
-- (&&&) is already taken by Control.Arrow
(~&~) :: (Settable f) => (c -> d -> f a) -> (c -> a -> t) -> c -> d -> t
(~&~) a b f = b f . untainted . a f
Вы можете проверить это:
>>> import Control.Lens
>>> (1, 'a', 2) & (_1 ~&~ _3) %~ (*3)
(3,'a',6)
ИЗМЕНИТЬ
Фактически вам не нужно использовать внутренние функции. Вы можете использовать тот факт, что Мутатор является монадой:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Monad
import Control.Applicative
(~&~) = liftA2 (>=>)
-- This works too, and is maybe easier to understand:
(~&~) a b f x = a f x >>= b f
Ответ 2
Существует вариация того, о чем вы спрашиваете, что более общее:
(/\)
:: (Functor f)
=> ((a -> (a, a)) -> (c -> (a, c)))
-- ^ Lens' c a
-> ((b -> (b, b)) -> (c -> (b, c)))
-- ^ Lens' c b
-> (((a, b) -> f (a, b)) -> (c -> f c))
-- ^ Lens' c (a, b)
(lens1 /\ lens2) f c0 =
let (a, _) = lens1 (\a_ -> (a_, a_)) c0
(b, _) = lens2 (\b_ -> (b_, b_)) c0
fab = f (a, b)
in fmap (\(a, b) ->
let (_, c1) = lens1 (\a_ -> (a_, a)) c0
(_, c2) = lens2 (\b_ -> (b_, b)) c1
in c2
) fab
infixl 7 /\
Просто сосредоточьтесь на сигнатуре типа с синонимами типа объектива:
Lens' c a -> Lens' c b -> Lens' c (a, b)
Он принимает две линзы и объединяет их в объектив с парой полей. Это немного более общий и работает для объединения линз, которые указывают на поля разных типов. Тем не менее, вам придется мутировать два поля отдельно.
Я просто хотел бросить это решение там, если люди искали что-то вроде этого.