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-Lenslike 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