如何组合镜片和仿函数?

时间:2014-03-12 20:34:13

标签: haskell lenses lens

我试图习惯Haskell的lens库,发现自己在一些简单的问题上苦苦挣扎。例如,让我们说(为方便起见)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)

1 个答案:

答案 0 :(得分:9)

你喜欢像

这样的镜头
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

可能更好的是使用Traversal代替

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

这样您就可以获得(使用previewtoListOf)并在地图中的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)

通常情况下,最好避免这些假象以防止意外发生。