我正在努力适应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)
我正在努力适应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)
你想要一个像这样的镜头
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
可能比那更好的是使用 aTraversal
代替
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)
所以通常最好避免使用这些伪透镜以防止发生意外。