17

レンズライブラリを使用して、次のように個々のターゲットに変更関数を適用できます。

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)
4

2 に答える 2

19

の型クラスuntaintedから使用すると、2 つのセッターを組み合わせることができますが、結果もセッターのみでゲッターではありません。SettableControl.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)

編集

内部関数を実際に使用する必要はありません。Mutator がモナドであるという事実を利用できます:

{-# 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)

2 つのレンズを取り、それらを 1 つのレンズに組み合わせて 1 組のフィールドにします。これはもう少し一般的で、異なるタイプのフィールドを指すレンズを組み合わせる場合に機能します。ただし、2 つのフィールドを別々に変更する必要があります。

人々がこのようなものを探している場合に備えて、このソリューションを公開したかっただけです。

于 2013-07-08T18:00:29.563 に答える