如果我有两个镜头:
foo :: Lens' X Foo
bar :: Lens' X Bar
有没有办法构建产品镜头:
foobar :: Lens' X (Foo, Bar)
foobar = ... foo bar
还是不可能?
答案 0 :(得分:9)
一般情况下,这是不可能的。可能是最常见的情况,当你有镜头到不同的记录领域,镜头是不相交的,所以你可以制作一个合法的镜头。但总的来说,事实并非如此。这就是为什么库中没有提供组合子的原因,即使它很容易编写。
假设lensProd
存在。只需两次拍摄相同的镜头就足够了:
_1 :: Lens' (a, b) a -- Simpler type
badLens :: Lens' (a, b) (a, a)
badLens = lensProd _1 _1
然后,“你得到你放入的东西”法律不成立。它应该是:
view badLens (set badLens (1, 2) (3, 4)) ≡ (1, 2)
但事实并非如此,因为view badLens pair
会返回两次值(x, x)
,所有pair
。
@dfeuer举例说明如何定义lensProd
。
有趣的是双重也被打破了。一般来说,你不能合法 棱镜之和:
{-# LANGUAGE RankNTypes #-}
import Control.Applicative
import Control.Lens
-- |
-- >>> :t sumPrism _Just _Nothing :: Prism' (Maybe a) (Either a ())
-- sumPrism _Just _Nothing :: Prism' (Maybe a) (Either a ())
-- :: (Applicative f, Choice p) =>
-- p (Either a ()) (f (Either a ())) -> p (Maybe a) (f (Maybe a))
--
sumPrism :: Prism' a b -> Prism' a c -> Prism' a (Either b c)
sumPrism ab ac = prism' build match where
build (Left b) = ab # b
build (Right c) = ac # c
match x = Left <$> x ^? ab <|> Right <$> x ^? ac
-- The law
--
-- @
-- preview l (review l b) ≡ Just b
-- @
--
-- breaks with
--
-- >>> preview badPrism (review badPrism (Right 'x'))
-- Just (Left 'x')
--
badPrism :: Prism' a (Either a a)
badPrism = sumPrism id id
如您所见,我们放入了Right
,但是Left
了。
答案 1 :(得分:7)
正如phadej所解释的那样,一般来说,没有守法的方法。但是,无论如何,您都可以这样做,并警告您的用户,他们最好只注意将其应用于正交镜头。
import Control.Lens
import Control.Arrow ((&&&))
fakeIt :: Lens' s x -> Lens' s y -> Lens' s (x,y)
fakeIt l1 l2 =
lens (view l1 &&& view l2)
(\s (x,y) -> set l1 x . set l2 y $ s)
例如:
Prelude Control.Lens A> set (fake _1 _2) (7,8) (1,2,3)
(7,8,3)
Prelude Control.Lens A> view (fake _1 _2) (1,2,3)
(1,2)