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?
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]
.