You can use over (inner.traverse.foo)
to write to the nested field. This will not report failure the way you want though, because it succeeded in mapping over all 0 targets.
traverse
(from Data.Traversable
, re-exported by Control.Lens
) here gives you a Traversal
for walking over the Maybe
.
You can read from it with (^?)
to see if the target of the lens exists.
We can solve this in several ways by using existing lens combinators to read and write separately, but we could just build such a combinator directly:
import Data.Monoid (Any(..))
import Control.Monad (guard)
overish :: LensLike ((,) Any) s t a b -> (a -> b) -> s -> Maybe t
overish l f s = case l (\a -> (Any True, f a)) s of
(Any r, t) -> t <$ guard r
You could write this with l %%~ \a -> (Any True, f a)
as well.
You can easily check if a Traversal
has no targets by using nullOf
, but this will require two passes and a higher rank type:
overish :: Traversal s t a b -> (a -> b) -> s -> Maybe t
overish l f s = over l f s <$ guard (not (nullOf l s))
This is effectively just checking to see that there is a target and then applying the setter to it if there are targets.
Then you can just use
overish (inner.traverse.foo) succ