我可以手動定義所需的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
:(
編輯有益看來,第三定律並不總是成立。很容易找到反例,如果你改變Key
是Bool
。然而,在我的應用程序Map
實際上是依賴,即總和分支取決於密鑰,所以Lens
法律應保持(如果我訪問foo
,我知道這應該是Left
或根本不存在)
伊克,不守法的事情是噁心。你能以某種方式包裝這個「Map」來表達你後來依賴的不變量嗎? – dfeuer 2015-02-10 19:34:05
@dfeuer,在實際應用程序中隱藏了實現。然而,例如['non'](http://hackage.haskell.org/package/lens-4.7/docs/Control-Lens-Iso.html#v:non)正在延伸現實:) – phadej 2015-02-10 19:55:20