17

Using a lens library I can apply a modification function to individual targets, like so:

Prelude Control.Lens> (1, 'a', 2) & _1 %~ (*3)
(3,'a',2)
Prelude Control.Lens> (1, 'a', 2) & _3 %~ (*3)
(1,'a',6)

How can I combine those individual lenses (_1 and _3) to be able to perform this update to both of the targets at once? I expect something in the spirit of the following:

Prelude Control.Lens> (1, 'a', 2) & ??? %~ (*3)
(3,'a',6)
4

2 回答 2

19

使用in 中untaintedSettable类型类Control.Lens.Internal.Setter,可以组合两个 setter,但结果也只会是 setter 而不是 getter。

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)

编辑

您实际上不需要使用内部函数。您可以使用 Mutator 是一个 monad 的事实:

{-# 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
于 2013-07-08T14:40:46.573 回答
7

您所要求的内容有所不同,后者更普遍:

(/\)
    :: (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)

它需要两个镜头并将它们组合成一个镜头到一对视场。这稍微更通用,适用于组合指向不同类型场的镜头。但是,您必须分别改变这两个字段。

我只是想把这个解决方案扔在那里,以防人们正在寻找这样的东西。

于 2013-07-08T18:00:29.563 回答