我试图习惯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)
答案 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
这样您就可以获得(使用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)
通常情况下,最好避免这些假象以防止意外发生。