问题 如何组合镜片和仿函数?


我正试着习惯 lens Haskell的库,发现自己在一些简单的问题上苦苦挣扎。例如,让我们说(为方便起见) at 和 _1 有以下类型(这至少是我理解它们的方式):

at :: Ord k => k -> Lens' (Map k v) (Maybe v)

_1 :: Lens' (a, b) a

如何将这些镜头组合成以下类型的镜头:

maybeFst :: Ord k => k -> Lens' (Map k (a, b)) (Maybe a)

11397
2018-03-12 20:34


起源



答案:


你喜欢像镜头一样

Lens' (Maybe (a, b)) (Maybe a)

但这不可能是一个 Lens 自从放回去 Nothing 影响了 b 同样。它可以是一个 Getter

getA :: Getter (Maybe (a, b)) (Maybe a)
getA = to (fmap fst)

但是当你构成它时,你最终会结束 Getter 同样,不是一个完整的 Lens

maybeFst :: Ord k => k -> Getter (Map k (a, b)) (Maybe a)
maybeFst k = at k . getA

可能比这更好的是使用a Traversal 代替

maybeFstT :: Ord k => k -> Traversal' (Map k (a, b)) a
maybeFstT k = at k . _Just . _1

这将允许你得到(使用 preview 要么 toListOf)并设置值 fst 地图中的值,但您无法在地图中修改它的存在:如果该值不存在,则无法添加它,如果它存在,则无法将其删除。


最后,我们可以陪审团伪造 Lens 它有适当的类型,但我们必须给它一个默认值 b

getA :: b -> Lens' (Maybe (a, b)) (Maybe a)
getA b inj Nothing       = (\x -> (,b) <$> x) <$> inj Nothing
getA _ inj (Just (a, b)) = (\x -> (,b) <$> x) <$> inj (Just a)

但请注意它有一些不太 - Lens喜欢的行为。

>>> Just (1, 2) & getA 0 .~ Nothing & preview (_Just . _2)
Nothing

>>> Nothing & getA 0 .~ Just 1
Just (1,0)

所以经常最好避免这些假象来防止意外事故。


9
2018-03-12 20:49



谢谢,我现在看到我要求的类型永远不会是一个镜头! :) - wen
最后一点,如果我们有一个 Iso' (a, b) a (这显然是不可能的,那么我们可以使用 mapping ourIso :: Iso' (Maybe (a, b)) (Maybe a) 代替 getA。好吧,试试吧 fl (a, b) = (b, a) 同 flipP = iso fl fl 接着 maybeFlip k = at k . mapping flipP :: Ord k => k -> Lens' (Map k (a, b)) (Maybe (b, a))。 - J. Abrahamson
只是一个小小的澄清:你的第一个例子不能成为一个镜头的原因是你不能发明一个 b 有时投入 Nothing 并试图投入 Just a。相反的情况很好。这是我的尝试: impossible :: Lens' (Maybe (a,b)) (Maybe a);  impossible k (Just (a,b)) = fmap (,b) <$> k (Just a);  impossible k Nothing = fmap (,undefined) <$> k Nothing。我先写了 impossible k Nothing = Nothing <$ k Nothing,但这不符合镜头规律。 - Hjulle


答案:


你喜欢像镜头一样

Lens' (Maybe (a, b)) (Maybe a)

但这不可能是一个 Lens 自从放回去 Nothing 影响了 b 同样。它可以是一个 Getter

getA :: Getter (Maybe (a, b)) (Maybe a)
getA = to (fmap fst)

但是当你构成它时,你最终会结束 Getter 同样,不是一个完整的 Lens

maybeFst :: Ord k => k -> Getter (Map k (a, b)) (Maybe a)
maybeFst k = at k . getA

可能比这更好的是使用a Traversal 代替

maybeFstT :: Ord k => k -> Traversal' (Map k (a, b)) a
maybeFstT k = at k . _Just . _1

这将允许你得到(使用 preview 要么 toListOf)并设置值 fst 地图中的值,但您无法在地图中修改它的存在:如果该值不存在,则无法添加它,如果它存在,则无法将其删除。


最后,我们可以陪审团伪造 Lens 它有适当的类型,但我们必须给它一个默认值 b

getA :: b -> Lens' (Maybe (a, b)) (Maybe a)
getA b inj Nothing       = (\x -> (,b) <$> x) <$> inj Nothing
getA _ inj (Just (a, b)) = (\x -> (,b) <$> x) <$> inj (Just a)

但请注意它有一些不太 - Lens喜欢的行为。

>>> Just (1, 2) & getA 0 .~ Nothing & preview (_Just . _2)
Nothing

>>> Nothing & getA 0 .~ Just 1
Just (1,0)

所以经常最好避免这些假象来防止意外事故。


9
2018-03-12 20:49



谢谢,我现在看到我要求的类型永远不会是一个镜头! :) - wen
最后一点,如果我们有一个 Iso' (a, b) a (这显然是不可能的,那么我们可以使用 mapping ourIso :: Iso' (Maybe (a, b)) (Maybe a) 代替 getA。好吧,试试吧 fl (a, b) = (b, a) 同 flipP = iso fl fl 接着 maybeFlip k = at k . mapping flipP :: Ord k => k -> Lens' (Map k (a, b)) (Maybe (b, a))。 - J. Abrahamson
只是一个小小的澄清:你的第一个例子不能成为一个镜头的原因是你不能发明一个 b 有时投入 Nothing 并试图投入 Just a。相反的情况很好。这是我的尝试: impossible :: Lens' (Maybe (a,b)) (Maybe a);  impossible k (Just (a,b)) = fmap (,b) <$> k (Just a);  impossible k Nothing = fmap (,undefined) <$> k Nothing。我先写了 impossible k Nothing = Nothing <$ k Nothing,但这不符合镜头规律。 - Hjulle