2016-04-09 10 views
7

私は2枚のレンズがある場合:2枚のレンズを作る方法は?

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

か、それは不可能です:

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

製品レンズを構築する方法はありますか?

答えて

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戻っていくつかの値として、trueにすることはできません。すべてのpairのため(x, x)

@dfeuerは、lensProdの定義方法の例を示しています。興味深いことに、デュアルも壊れています。あなたが見る、私たちはRightに置くが、Leftを出すことができたよう

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

:一般的に、あなたは、プリズムの合法的 合計を持つことはできません。

+0

は本当に二重のことですか? – dfeuer

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

私が想像するcompdataのように、分離レベルの証明を得ることができます。そして、そのような証明を提供するコンディショナー – nicolas