Search code examples
haskelllenseshaskell-lens

Getting multiple results from map with "lens"


Having these imports:

> import Control.Lens
Control.Lens> import qualified Data.Map as Map

and a map value defined as follows:

Control.Lens Map> let m = Map.fromList [('a', 1), ('c', 3), ('b', 2)]

I can get it's elements one by one like so:

Control.Lens Map> view (at 'b') m
Just 2

What I want to know is, having a set of keys such as this:

Control.Lens Map> import qualified Data.Set as Set
Control.Lens Map Set> let keys = Set.fromList ['d', 'c', 'b']

how to construct such a getter (I guess), using which I'll be able to get a set (or a list) of matching elements:

Control.Lens Map Set> view (**???**) m
[3, 2]

Notice that the result contains only 2 elements, because there's no match for a key 'd'.


Solution

  • The following will work if you only want a getter over multiple fields.

    First, you need to make Accessor from lens an instance of Monoid (that instance is in in HEAD, but not released yet already defined in lens >= 4, so you only need to define the instance if you're working with an old version of the library).

    import Data.Monoid
    import Control.Lens
    
    instance Monoid r => Monoid (Accessor r a) where
      mempty = Accessor mempty
      mappend (Accessor a) (Accessor b) = Accessor $ a <> b
    

    You can then use that instance to combine multiple lenses/traversals into a single traversal:

    >>> import qualified Data.Set as S
    >>> import qualified Data.Map as M
    >>> import Data.Foldable (foldMap)
    >>> import Control.Lens
    >>> let m = M.fromList [('a',1), ('b',2), ('c',3)]
    >>> let k = S.fromList ['b','c','e']
    >>> m ^.. foldMap at k
    [Just 2,Just 3,Nothing]
    >>> m ^.. foldMap ix k
    [2,3]
    

    foldMap uses the Monoid instance for Accessor and the Monoid instance for functions.