2013-07-08 25 views
15

Sử dụng một ống kính thư viện tôi có thể áp dụng một chức năng sửa đổi các mục tiêu cá nhân, như vậy:Kết hợp ống kính

Prelude Control.Lens> (1, 'a', 2) & _1 %~ (*3) 
(3,'a',2) 
Prelude Control.Lens> (1, 'a', 2) & _3 %~ (*3) 
(1,'a',6) 

Làm thế nào tôi có thể kết hợp những thấu kính của cá nhân (_1_3) để có thể thực hiện cập nhật này cho cả hai mục tiêu cùng một lúc? Tôi hy vọng điều gì đó trong tinh thần của những điều sau đây:

Prelude Control.Lens> (1, 'a', 2) & ??? %~ (*3) 
(3,'a',6) 
+0

Tôi không chắc chắn rằng có một sự thực thi hợp lý về một hoạt động như vậy. Giả sử toán tử kết hợp hai ống kính của bạn là '(&&&)'. Nó phải có một cái gì đó kiểu như '(&&&) :: Lens a b -> Lens a b -> Lens a b' để bạn có thể sử dụng nó giống như hai ống kính mà bạn kết hợp để tạo ra nó. Cho rằng 'view _1 (1,2) = 1' và' view _2 (1,2) = 2', bạn mong đợi kết quả của 'view (_1 &&& _2) (1,2)' là gì? –

+0

@ChrisTaylor Tôi không thực sự cần chức năng "getter". Mặc dù AFAIU trong thư viện này thông thường là tiếp cận các trường hợp như vậy với monoid, ví dụ: [Traversal] (http://hackage.haskell.org/packages/archive/lens/latest/doc/html/Control-Lens-Traversal.html). –

+2

Tôi liên kết điều này trong các ý kiến ​​dưới đây, nhưng trong trường hợp ai đó bỏ lỡ nó, có một [vấn đề ống kính] (https://github.com/ekmett/lens/issues/109) về kết hợp các traversals và rắc rối với nó. – shachaf

Trả lời

19

Sử dụng untainted từ lớp SettableControl.Lens.Internal.Setter, nó có thể kết hợp hai setters, nhưng kết quả cũng sẽ chỉ là một setter và không phải là một getter.

import Control.Lens.Internal.Setter 

-- (&&&) is already taken by Control.Arrow 
(~&~) :: (Settable f) => (c -> d -> f a) -> (c -> a -> t) -> c -> d -> t 
(~&~) a b f = b f . untainted . a f 

Bạn có thể kiểm tra điều này:

>>> import Control.Lens 
>>> (1, 'a', 2) & (_1 ~&~ _3) %~ (*3) 
(3,'a',6) 

EDIT

Bạn không thực sự cần phải sử dụng các chức năng bên trong. Bạn có thể sử dụng thực tế là mutator là một đơn nguyên:

{-# LANGUAGE NoMonomorphismRestriction #-} 

import Control.Monad 
import Control.Applicative 

(~&~) = liftA2 (>=>) 

-- This works too, and is maybe easier to understand: 
(~&~) a b f x = a f x >>= b f 
+0

Cảm ơn! Có cách nào để có thể xây dựng một 'Traversal'? –

+3

No. Thật không may [không thực sự làm việc] (https://github.com/ekmett/lens/issues/109) nói chung. – shachaf

+1

(_1 ~ & ~ _1) không thỏa mãn luật Setter (aka SEC). –

7

Có sự khác biệt về những gì bạn đang yêu cầu mà là tổng quát hơn:

(/\) 
    :: (Functor f) 
    => ((a -> (a, a)) -> (c -> (a, c))) 
    --^Lens' c a 
    -> ((b -> (b, b)) -> (c -> (b, c))) 
    --^Lens' c b 
    -> (((a, b) -> f (a, b)) -> (c -> f c)) 
    --^Lens' c (a, b) 
(lens1 /\ lens2) f c0 = 
    let (a, _) = lens1 (\a_ -> (a_, a_)) c0 
     (b, _) = lens2 (\b_ -> (b_, b_)) c0 
     fab = f (a, b) 
    in fmap (\(a, b) -> 
      let (_, c1) = lens1 (\a_ -> (a_, a)) c0 
       (_, c2) = lens2 (\b_ -> (b_, b)) c1 
      in c2 
      ) fab 

infixl 7 /\ 

Chỉ cần tập trung vào các loại chữ ký với loại ống kính từ đồng nghĩa :

Lens' c a -> Lens' c b -> Lens' c (a, b) 

Phải mất hai ống kính và kết hợp chúng thành ống kính với một cặp trường. Điều này hơi chung chung hơn và hoạt động để kết hợp các thấu kính trỏ đến các trường thuộc nhiều loại khác nhau. Tuy nhiên, sau đó bạn phải thay đổi riêng hai trường.

Tôi chỉ muốn ném giải pháp này ra khỏi đó trong trường hợp mọi người đang tìm kiếm thứ như thế này.

+1

Một loại pithier, cho những người như họ '(/ \) :: LensLike '((,) a) c a -> LensLike' ((,) b) c b -> Lens 'c (a, b)'. Ngoài ra, hạn chế rõ ràng, '(/ | \) :: LensLike '((,) a) ca -> LensLike' ((,) a) ca -> Traversal 'ca' với' a/| \ b = (a/\ b). cả hai'. Tại sao những thứ này không nằm trong 'ống kính'? –

+0

Câu trả lời ngay lập tức: https://github.com/ekmett/lens/issues/315 –

Các vấn đề liên quan