问题
I'm new to the excelent Control.Lens
and I'm trying to combine 2 lens in "parallel" (not in sequence) like I would do with `Control.Arrow.&&&).
If I take the example from the lens
documentation:
`data Foo a = Foo { _baz :: Int, _bar :: Int, a }
I would like to be able to do stuff like :
>> let foo = (bar &&& baz) .~ (1, 20) $ Foo undefined undefined "FOO"
>> foo ^. (bar &&& baz)
(1, 20)
I've looked everywhere and I could not find a way to do so. Is that because :
- (&&&) exist with another name and I missed it.
- It's useless. I should not need it, therefore nobody bothered implementing it.
- It's trivial to do in another way (using
both
or<*>
)
Update
&&&
can be implemented that way :
(/|\) :: Lens' f a -> Lens' f b -> Lens' f (a, b)
a /|\ b = lens getBoth setBoth where
getBoth f = (f ^. a, f ^. b)
setBoth f (v, w) = a .~ v $ f' where
f' = b .~ w $ f
barz :: Lens' Foo (Int, Int)
barz = bar /|\ baz
However, it needs a type signature which is a bit annoying.
回答1:
I've just stumbled over this actual function, as it is defined in lens (with a fat comment about the unsoundness right in the module name: Control.Lens.Unsound
)
-- | A lens product. There is no law-abiding way to do this in general.
-- Result is only a valid 'Lens' if the input lenses project disjoint parts of
-- the structure @s@. Otherwise "you get what you put in" law
--
-- @
-- 'Control.Lens.Getter.view' l ('Control.Lens.Setter.set' l v s) ≡ v
-- @
--
-- is violated by
--
-- >>> let badLens :: Lens' (Int, Char) (Int, Int); badLens = lensProduct _1 _1
-- >>> view badLens (set badLens (1,2) (3,'x'))
-- (2,2)
--
-- but we should get @(1,2)@.
--
-- Are you looking for 'Control.Lens.Lens.alongside'?
--
lensProduct :: ALens' s a -> ALens' s b -> Lens' s (a, b)
lensProduct l1 l2 f s =
f (s ^# l1, s ^# l2) <&> \(a, b) -> s & l1 #~ a & l2 #~ b
回答2:
This combinator is probably impossible to implement. Consider:
> (baz &&& baz) .~ (1,5)
What should this do?
Even a weaker for of the combinator:
(/|\) :: Lens' s a -> Lens' s a -> Traversal' s a
a /|\ b = (a &&& b) . both
would break the laws:
For example, let's look at baz /|\ baz
. Since a Traversal
is also a Setter
, it must also satisfy the Setter laws. Now, take the second setter law:
over (baz /|\ baz) (f . g) = over (baz /|\ baz) f . over (baz /|\ baz) g
Now, we get:
over (baz /|\ baz) (f . g)
= \(Foo _baz _bar) -> Foo (f . g . f . g $ _baz) _bar
and:
over (baz /|\ baz) f . over (baz /|\ baz) g
= \(Foo _baz _bar) -> Foo (f . f . g . g $ _baz) _bar
Those two are obviously different. The problem arises when the two lenses "overlap", and this is not encoded in the type.
回答3:
As an answer to another question, I propose a library that is more general than the lens library. Among additional features, references can be combined "in parallel".
Of course it can raise interesting issues. For example twice = self &+& self
is a reference that accesses itself twice:
a ^* twice == [a,a]
(twice *= x) a == x
(twice *- f) a == f (f a)
(Do not be disturbed by the different operators. ^*
is similar to ^..
, *=
is similar to .~
and *-
is similar to %~
in Control.Lens.Operators)
来源:https://stackoverflow.com/questions/23321844/how-to-combine-lenses-in-parallel