Nếu tôi có hai ống kính:Làm thế nào để tạo ra sản phẩm của hai ống kính?
foo :: Lens' X Foo
bar :: Lens' X Bar
Có cách nào để xây dựng một ống kính sản phẩm:
foobar :: Lens' X (Foo, Bar)
foobar = ... foo bar
hoặc là nó không thể?
Nếu tôi có hai ống kính:Làm thế nào để tạo ra sản phẩm của hai ống kính?
foo :: Lens' X Foo
bar :: Lens' X Bar
Có cách nào để xây dựng một ống kính sản phẩm:
foobar :: Lens' X (Foo, Bar)
foobar = ... foo bar
hoặc là nó không thể?
Trong trường hợp chung, điều này là không thể. Có lẽ trường hợp phổ biến nhất khi bạn có ống kính đến các trường khác nhau của bản ghi, các ống kính rời nhau, vì vậy bạn có thể tạo ra một ống kính hợp pháp. Nhưng nói chung nó không đúng. Đây là lý do tại sao các combinator không được cung cấp trong các thư viện, thậm chí nó sẽ dễ dàng để viết.
Giả sử lensProd
tồn tại. Đủ để lấy cùng một ống kính hai lần:
_1 :: Lens' (a, b) a -- Simpler type
badLens :: Lens' (a, b) (a, a)
badLens = lensProd _1 _1
Sau đó, luật "Bạn lấy lại những gì bạn đưa vào" không giữ. Nó nên là:
view badLens (set badLens (1, 2) (3, 4)) ≡ (1, 2)
Nhưng nó không thể là sự thật, như view badLens pair
lợi nhuận một số giá trị gấp đôi: (x, x)
cho tất cả pair
s.
@dfeuer cung cấp ví dụ về cách xác định lensProd
.
Điều thú vị là đôi cũng bị hỏng. Nói chung bạn không thể có số tiền hợp pháp của lăng kính:
{-# 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
Như bạn có thể thấy, chúng tôi đưa vào Right
, nhưng nhận ra Left
.
Như phadej giải thích, không có cách tuân thủ pháp luật nào để làm điều này nói chung. Tuy nhiên, bạn có thể làm điều đó anyway và cảnh báo người dùng của bạn rằng họ nên cẩn thận hơn chỉ áp dụng nó cho ống kính trực giao.
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)
Ví dụ:
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)
Chúng tôi có thể nhận được một loại bằng chứng về sự phân tách như trong compdata tôi tưởng tượng, và một combinator có điều kiện khi cung cấp bằng chứng như vậy – nicolas
Đó là thực sự kép? – dfeuer