我正在尝试熟悉 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)
我正在尝试熟悉 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)
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
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)
因此,通常最好避免使用这些伪造的透镜以防止意外事件发生。
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. AbrahamsonNothing
并尝试放入Just a
时,你无法发明一个b
来放入。反向情况可以很好地工作。这是我的尝试: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