haskellhaskell-lenslenses

Shorter way to traverse nested record with Maybe


Is there a shorter/cleaner way of writing the following snippet of code:

fromMaybe "" $ fmap (^. fullName) (bi ^. bookerContact)

Here bi ^. bookerContact may result in a Maybe Contact record, which is why ^. fullName needs to be fmapped. After the nested traversal, if we end up with a Nothing we use fromMaybe "" to default it to a blank string.


Solution

  • A simple way of tidying it is using maybe instead of the fromMaybe/fmap combo:

    maybe "" (^. fullName) (bi ^. bookerContact)
    

    You can also introduce the _Just prism to express all of the drilling down as a single optic:

    fromMaybe "" (bi ^? bookerContact . _Just . fullName)
    

    Note that we have switched from (^.) to (^?). That reflects how adding _Just to the chain changes what used to be a lens (and reach exactly one target) into a traversal (which might reach no targets).


    It is also possible to take advantage of Text being a monoid and use fold from Data.Foldable...

    fold (bi ^? bookerContact . _Just . fullName)
    

    ... or foldOf, if you prefer the lens spelling:

    foldOf (bookerContact . _Just . fullName) bi
    

    As the docs point out, foldOf is equivalent to (^.) (which is essentially view specialised to (->)), so this will also work, by folding the Maybe Text:

    bi ^. bookerContact . _Just . fullName