2016-04-09 14 views
7

Wenn ich zwei Linsen haben:Wie mache ich das Produkt aus zwei Linsen?

foo :: Lens' X Foo 
bar :: Lens' X Bar 

Gibt es eine Möglichkeit, ein Produkt Linse zu konstruieren:

foobar :: Lens' X (Foo, Bar) 
foobar = ... foo bar 

oder ist es unmöglich?

Antwort

9

Im Allgemeinen ist dies unmöglich. Wahrscheinlich der häufigste Fall, wenn Sie Linsen zu verschiedenen Bereichen der Aufzeichnung haben, sind die Linsen disjunkt, so dass Sie eine rechtmäßige Linse machen können. Aber im Allgemeinen ist es nicht wahr. Aus diesem Grund ist der Kombinator nicht in den Bibliotheken enthalten, selbst wenn er einfach zu schreiben wäre.

Angenommen lensProd existiert. Es ist genug, um zweimal die gleiche Linse zu nehmen:

_1 :: Lens' (a, b) a -- Simpler type 

badLens :: Lens' (a, b) (a, a) 
badLens = lensProd _1 _1 

Dann wird die „Man bekommt zurück, was Sie setzen in“ Gesetz nicht gilt. Es sollte sein:

view badLens (set badLens (1, 2) (3, 4)) ≡ (1, 2) 

Aber es kann nicht wahr sein, wie view badLens pair kehrt einigen Wert zweimal: (x, x) für all pair s.

@dfeuer gibt ein Beispiel, wie man lensProd definiert.


Interessanterweise ist das Dual auch gebrochen. Im Allgemeinen kann man nicht rechtmäßig Summe des Prismas haben:

{-# 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 

Wie Sie sehen können, haben wir in Right, aber Left raus.

+0

Ist das wirklich Dual? – dfeuer

7

Wie phadej erklärte, gibt es keinen gesetzestreuen Weg, dies im Allgemeinen zu tun. Sie können dies jedoch trotzdem tun und Ihre Benutzer warnen, dass sie vorsichtig sein sollten, um sie nur auf orthogonale Linsen anzuwenden.

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) 

Zum Beispiel:

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) 
+2

Wir könnten einen Typ-Level-Beweis der Disjunktion wie in compdata, die ich mir vorstelle, und einen Kombinator erhalten, der auf der Bereitstellung eines solchen Beweises beruht – nicolas