2015-02-10 61 views
2

我可以手動定義所需的Lens'如何將鏡頭定義爲和類型的Map類容器?

type Key = String 
type Val = Int 
type Foo = Map Key (Either Val Bool) 

ll :: String -> Lens' Foo (Maybe Int) 
ll k f m = f mv <&> \r -> case r of 
    Nothing -> maybe m (const (Map.delete k m)) mv 
    Just v' -> Map.insert k (Left v') m 
    where mv = Map.lookup k m >>= maybeLeft 
     maybeLeft (Left v') = Just v' 
     maybeLeft (Right _) = Nothing 

它的工作原理是:

x, y :: Foo 
x = Map.empty 
y = Map.fromList [("foo", Right True)] 

>>> x ^. ll "foo" 
Nothing 

>>> x & ll "foo" ?~ 1 
fromList [("foo",Left 1)] 

>>> (x & ll "foo" ?~ 1) ^. ll "foo" 
Just 1 

>>> (x & ll "foo" ?~ 1) ^. ll "bar" 
Nothing 

>>> x & ll "foo" ?~ 1 & ll "foo" .~ Nothing 
fromList [] 

>>> y ^. ll "foo" 
Nothing 

>>> y & ll "foo" ?~ 1 
fromList [("foo",Left 1)] 

>>> y & ll "foo" .~ Nothing 
fromList [("foo",Right True)] 

我覈實,定義是合法的:

-- Orphan instance is ok-ish in this case :) 
instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map k v) where 
    arbitrary = Map.fromList <$> arbitrary 

-- 1) You get back what you put in: 
lensLaw1 :: Foo -> Key -> Maybe Val -> Property 
lensLaw1 s k v = view (ll k) (set (ll k) v s) === v 

-- 2) Putting back what you got doesn't change anything: 
lensLaw2 :: Foo -> Key -> Property 
lensLaw2 s k = set (ll k) (view (ll k) s) s === s 

-- 3) Setting twice is the same as setting once: 
lensLaw3 :: Foo -> Key -> Maybe Val -> Maybe Val -> Property 
lensLaw3 s k v v' = set (ll k) v' (set (ll k) v s) === set (ll k) v' s 

所以問題可以使用來定義和_Left

也許某種prismToLens :: Prism' a b -> Lens' (Maybe a) (Maybe b),你可以做at k . prismToLens _Left。但我不確定prismToLens是否有意義? Hoogle不符合lens :(

編輯有益看來,第三定律並不總是成立。很容易找到反例,如果你改變KeyBool。然而,在我的應用程序Map實際上是依賴,即總和分支取決於密鑰,所以Lens法律應保持(如果我訪問foo,我知道這應該是Left或根本不存在)

+0

伊克,不守法的事情是噁心。你能以某種方式包裝這個「Map」來表達你後來依賴的不變量嗎? – dfeuer 2015-02-10 19:34:05

+0

@dfeuer,在實際應用程序中隱藏了實現。然而,例如['non'](http://hackage.haskell.org/package/lens-4.7/docs/Control-Lens-Iso.html#v:non)正在延伸現實:) – phadej 2015-02-10 19:55:20

回答

1

現在我一起去。

prismToLens :: Prism' a b -> Lens' (Maybe a) (Maybe b) 
prismToLens p = lens getter setter 
    where getter s = s >>= (^? p) 
     setter _ b = (p#) <$> b 

所以我可以定義ll像:

ll' :: Key -> Lens' Foo (Maybe Val) 
ll' k = at k . prismToLens _Left 

Controrary在問題中定義的「鏡頭」,這一個第二定律不成立:

-- 2) Putting back what you got doesn't change anything: 
-- Doesn't hold 
-- >>> quickCheck $ lensLaw2' (Map.fromList [(True,Right False)]) True 
-- fromList [] /= fromList [(True,Right False)] 
lensLaw2' :: Foo -> Key -> Property 
lensLaw2' s k = set (ll' k) (view (ll' k) s) s === s 

但與原來的第三定律未持有:

-- 3) Setting twice is the same as setting once: 
-- Doesn't hold 
-- >>> quickCheck $ lensLaw3 (Map.fromList [(False, Right False)]) False (Just 0) Nothing 
-- fromList [] /= fromList [(True,Right False)] 
lensLaw3 :: Foo -> Key -> Maybe Val -> Maybe Val -> Property 
lensLaw3 s k v v' = set (ll k) v' (set (ll k) v s) === set (ll k) v' s 

如問題說,我公頃ve依賴的地圖,這沒關係。當訪問一些密鑰k時,應該不會有Right的值,如果我預計有Left。考慮到賬戶,使用prismToLens實際上更好。仍在尋找一個更好的名字。


記住non後,我改變了答案使用:

prismToIso :: Prism' a b -> Iso' (Maybe a) (Maybe b) 
prismToIso p = iso t f 
    where t a = a >>= (^? p) 
     f b = (p#) <$> b -- no unused param as in `prismToLens`! 

這類似於mapping。法律性質與prismToLens表現相同。這引起了新的問題:哪一個更好或更差,prismToIsoprismToLens。爲什麼?


完整的可運行的例子:

{-# LANGUAGE RankNTypes #-} 
module Lens where 

import Control.Applicative 
import Control.Lens 
import Data.Map as Map 
import Test.QuickCheck 

type Key = Bool 
type Val = Int 
type Foo = Map Key (Either Val Bool) 

ll :: Key -> Lens' Foo (Maybe Val) 
ll k f m = f mv <&> \r -> case r of 
    Nothing -> maybe m (const (Map.delete k m)) mv 
    Just v' -> Map.insert k (Left v') m 
    where mv = Map.lookup k m >>= maybeLeft 
     maybeLeft (Left v') = Just v' 
     maybeLeft (Right _) = Nothing 

prismToLens :: Prism' a b -> Lens' (Maybe a) (Maybe b) 
prismToLens p = lens getter setter 
    where getter s = s >>= (^? p) 
     setter _ b = (p#) <$> b 

ll' :: Key -> Lens' Foo (Maybe Val) 
ll' k = at k . prismToLens _Left 

x, y :: Foo 
x = Map.empty 
y = Map.fromList [(True, Right True)] 

{- 
>>> x ^. ll "foo" 
Nothing 

>>> x & ll "foo" ?~ 1 
fromList [("foo",Left 1)] 

>>> (x & ll "foo" ?~ 1) ^. ll "foo" 
Just 1 

>>> (x & ll "foo" ?~ 1) ^. ll "bar" 
Nothing 

>>> x & ll "foo" ?~ 1 & ll "foo" .~ Nothing 
fromList [] 

>>> y ^. ll "foo" 
Nothing 

>>> y & ll "foo" ?~ 1 
fromList [("foo",Left 1)] 

>>> y & ll "foo" .~ Nothing 
fromList [("foo",Right True)] 
-} 

-- Orphan instance is ok-ish in this case :) 
instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map k v) where 
    arbitrary = Map.fromList <$> arbitrary 
    shrink = Prelude.map Map.fromList . shrink . Map.toList 

-- 1) You get back what you put in: 
lensLaw1 :: Foo -> Key -> Maybe Val -> Property 
lensLaw1 s k v = view (ll k) (set (ll k) v s) === v 

-- 2) Putting back what you got doesn't change anything: 
lensLaw2 :: Foo -> Key -> Property 
lensLaw2 s k = set (ll k) (view (ll k) s) s === s 

-- 3) Setting twice is the same as setting once: 
-- Doesn't hold 
-- >>> quickCheck $ lensLaw3 (Map.fromList [(False, Right False)]) False (Just 0) Nothing 
-- fromList [] /= fromList [(True,Right False)] 
lensLaw3 :: Foo -> Key -> Maybe Val -> Maybe Val -> Property 
lensLaw3 s k v v' = set (ll k) v' (set (ll k) v s) === set (ll k) v' s 

-- Using prismToLens defined "lens" 

-- 1) You get back what you put in: 
lensLaw1' :: Foo -> Key -> Maybe Val -> Property 
lensLaw1' s k v = view (ll' k) (set (ll' k) v s) === v 

-- 2) Putting back what you got doesn't change anything: 
-- Doesn't hold 
-- >>> quickCheck $ lensLaw2' (Map.fromList [(True,Right False)]) True 
-- fromList [] /= fromList [(True,Right False)] 
lensLaw2' :: Foo -> Key -> Property 
lensLaw2' s k = set (ll' k) (view (ll' k) s) s === s 

-- 3) Setting twice is the same as setting once: 
lensLaw3' :: Foo -> Key -> Maybe Val -> Maybe Val -> Property 
lensLaw3' s k v v' = set (ll' k) v' (set (ll' k) v s) === set (ll' k) v' s 
相關問題