I'm trying to get used to the lens
library for Haskell, and find myself struggling at some simple problems. For instance, let's say (for convenience) that at
and _1
have the following types (this is how I understand them, at least):
at :: Ord k => k -> Lens' (Map k v) (Maybe v)
_1 :: Lens' (a, b) a
How do I combine these lenses into a lens with the following type:
maybeFst :: Ord k => k -> Lens' (Map k (a, b)) (Maybe a)
You'd like a lens like
Lens' (Maybe (a, b)) (Maybe a)
but that can't quite be a Lens
since putting back Nothing
affects the b
as well. It can be a Getter
getA :: Getter (Maybe (a, b)) (Maybe a)
getA = to (fmap fst)
but then when you compose it you'll just wind up with a Getter
as well, not a full Lens
maybeFst :: Ord k => k -> Getter (Map k (a, b)) (Maybe a)
maybeFst k = at k . getA
Probably better than that is to use a Traversal
instead
maybeFstT :: Ord k => k -> Traversal' (Map k (a, b)) a
maybeFstT k = at k . _Just . _1
This will allow you to both get (using preview
or toListOf
) and set values at the fst
of the values in your map, but you won't be able to modify its existence in the map: if the value does not exist you cannot add it and if it does exist you cannot remove it.
Finally, we can jury-rig a fake Lens
which has the appropriate type, though we have to give it a default value for 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)
but notice that it has some not-very-Lens
like behavior.
>>> Just (1, 2) & getA 0 .~ Nothing & preview (_Just . _2)
Nothing
>>> Nothing & getA 0 .~ Just 1
Just (1,0)
so often it's better to avoid these pseudolenses to prevent mishaps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With