Lens'
必要なものを手動で定義できます:
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
たぶん、ある種のprismToLens :: Prism' a b -> Lens' (Maybe a) (Maybe b)
があれば、できるでしょうat k . prismToLens _Left
。それでもprismToLens
、意味があるかどうかはわかりませんか?Hoogle はlens
:(
EDITは、第三法則が常に成り立つとは限らないようです。Key
に変更すると、反例を簡単に見つけることができますBool
。しかし、私のアプリケーションでは、Map
は実際には依存しています。つまり、 sum 分岐はキーに依存しているため、Lens
法則が成り立つはずです ( にアクセスするfoo
と、それが存在するかどうかはまったくわかりLeft
ません)。