Search code examples
haskelldictionaryhaskell-lens

Default value if it doesn't exist in map


For my purposes, I need a map-like container with 'properties' of some kind. It is possible that my objects can have different properties. To access the properties, I decided to use Control.Lens, it's very interesting. But I can't find a lens-like way for such logic: on access to property, if it doesn't exist, add default value instead, and return the new one. But if it's there, just use it.

In other words, the prop_test should return True:

type PropertyMap = Map.Map Int String
data Properties = Properties { _propertyMap :: PropertyMap }
  deriving (Eq)

makeLenses ''Properties

emptyProperties = Properties Map.empty

propertyLens pIndex = propertyMap . at pIndex . traverse

property1 = propertyLens 1
property2 = propertyLens 2
property3 = propertyLens 3

obj1Properties :: State Properties ()
obj1Properties = do
    property1 .= "Property1 value"
    property2 .= "Property2 value"

obj2Properties :: State Properties ()
obj2Properties = do
    property1 .= "Property1 value"
    property3 .= "Property3 value"

prop_test = op1 /= emptyProperties
    where
        op1 = execState obj1Properties emptyProperties

But now, op1 is equal to emptyProperties. For default value, I could use Data.Default. How can I handle this? Or should I use another method maybe? For example, a wrapper function in State monad which unpacks and checks the properties existence for me.

Also, could you give links to real world examples of Control.Lens (or another lens package), please?


Solution

  • If you make Properties and instance of 'Monoid' with mempty equal to emptyProperties and mappend something sensible like the union of the two maps then the Lens library will just do the right thing.

    The alternative is to use one of the combinators specifically designed for traversals (lenses that can return 0 or more results) and deal with the missing case then. See the documentation for (^?) which would produce a Maybe t or (^..) which will give you [t].