Objektiv ist kein fertiger Kombinator dafür, vermutlich weil man illegale Setzer (oder Linsen) bekommen kann, wenn sich die Fokusse der Komponenten überlappen.
data Trio a = Trio a a a
deriving (Show)
oneTwo :: Setter' (Trio a) (a, a)
oneTwo = sets $ \f (Trio x y z) -> let (x', y') = f (x, y) in Trio x' y' z
twoThree :: Setter' (Trio a) (a, a)
twoThree = sets $ \f (Trio x y z) -> let (y', z') = f (y, z) in Trio x y' z'
cheating :: Setter' (Trio a) (a, a)
cheating = sets $ \f x -> x & oneTwo %~ f & twoThree %~ f
GHCi> Trio 1 1 1 & cheating %~ bimap (2*) (2*) & cheating %~ bimap (3+) (3+)
Trio 5 10 5
GHCi> Trio 1 1 1 & cheating %~ (bimap (2*) (2*) <&> bimap (3+) (3+))
Trio 5 13 5
In Ihrem Fall die schönste Alternative der Setter/Traversal von Hand zu bauen (wie Sie and Cristoph Hegemann tun werden) scheint liftA2 (>=>) :: ASetter' s a -> ASetter' s a -> ASetter' s a
zu sein, wie bennofs (dank Shersh für den Link an anderer Stelle vorgeschlagen). Wenn Sie ein Objektiv auf ein homogenes Paar haben zufällig (oder eine andere Bitraversable
) herumliegen, können Sie die Traversal aus ihm heraus mit both
erhalten:
data Foo = Foo
{ _bar, _baz :: Int
} deriving (Show)
makeLenses ''Foo
barBaz :: Iso' Foo (Int, Int)
barBaz = iso ((,) <$> view bar <*> view baz) (Foo <$> fst <*> snd)
GHCi> Foo 1 2 & barBaz . both %~ (2*)
Foo {_bar = 2, _baz = 4}
Eine weitere Möglichkeit ist die Nutzung Data.Data.Lens
zu erhalten eine Traversal aller Felder eines bestimmten Typs:
{-# LANGUAGE DeriveDataTypeable #-}
import Control.Lens
import Data.Data.Lens
import Data.Data
data Foo = Foo
{ _bar, _baz :: Int
} deriving (Show, Data, Typeable)
makeLenses ''Foo
barBaz :: Traversal' Foo Int
barBaz = template
GHCi> Foo 1 2 & barBaz %~ (2*)
Foo {_bar = 2, _baz = 4}
Dies scheint Ihre Frage zu beantworten: http://stackoverflow.com/questions/17528119/combining-lenses – Shersh
Wenn Sie ein Tupel anstelle von 'Foo' verwenden, könnten Sie etwas wie [' (1,2) und beide tun. ~ 10'] (https://hackage.haskell.org/package/lens-4.15.1/docs/Control-Lens-Traversal.html#v:both) um beide Elemente des Tupels auf '10' zu setzen. – Alec