8

我是新手Control.Lens,我正在尝试将 2 个镜头“平行”(而不是按顺序)组合,就像我对 `Control.Arrow.&&& 所做的那样。

如果我从lens文档中获取示例:

`data Foo a = Foo { _baz :: Int, _bar :: Int, a }

我希望能够做类似的事情:

>> let foo = (bar &&& baz) .~ (1, 20) $ Foo undefined undefined "FOO"
>> foo ^. (bar &&& baz) 
(1, 20)

我到处寻找,我找不到这样做的方法。那是因为:

  • (&&&) 以另一个名字存在,我错过了。
  • 没用的。我不应该需要它,因此没有人打扰实现它。
  • both以另一种方式(使用or <*>)做起来很简单

更新

&&&可以这样实现:

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

但是,它需要一个有点烦人的类型签名。

4

3 回答 3

7

这个组合器可能无法实现。考虑:

> (baz &&& baz) .~ (1,5)

这应该怎么做?

甚至更弱的组合器:

(/|\) :: Lens' s a -> Lens' s a -> Traversal' s a
a /|\ b = (a &&& b) . both

会违反法律:

例如,让我们看看baz /|\ baz. 既然 aTraversal也是 a Setter,它也必须满足Setter规律。现在,采用第二个二传手法则:

over (baz /|\ baz) (f . g) = over (baz /|\ baz) f . over (baz /|\ baz) g

现在,我们得到:

over (baz /|\ baz) (f . g) 
= \(Foo _baz _bar) -> Foo (f . g . f . g $ _baz) _bar

和:

over (baz /|\ baz) f . over (baz /|\ baz) g
= \(Foo _baz _bar) -> Foo (f . f . g . g $ _baz) _bar

这两个显然是不同的。当两个镜头“重叠”时就会出现问题,而这并没有在类型中编码。

于 2014-04-27T10:26:22.723 回答
3

我刚刚偶然发现了这个实际功能,因为它是在定义的lens(在模块名称中带有一个关于不健全的胖评论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
于 2018-01-28T20:47:30.097 回答
1

作为对另一个问题的回答,我提出了一个比镜头更通用的库。在附加功能中,参考可以“并行”组合。

当然,它会引发有趣的问题。例如twice = self &+& self,一个访问自身两次的引用:

 a ^* twice == [a,a]
 (twice *= x) a == x
 (twice *- f) a == f (f a)

(不要被不同的算子打扰。^*类似于Control.Lens.Operators^..*=.~*-%~

于 2014-08-26T08:02:49.227 回答