If I have the following types:
data RecA = RecA
{ aField1 :: Maybe Text
, aField2 :: Maybe RecB
}
data RecB = RecB
{ bField1 :: Maybe Text
, bField2 :: Maybe Text
}
... and I want to set the value of bField1 in the following, how do I do it?
let myVal = Nothing :: Maybe RecA
in set _whatComesHere (Just "ValueFor_bField1") myVal
I'm looking for a generalized solution that allows me to set a deeply nested Maybe a value irrespective of whether the "parent" data structures are Nothings or Justs. I'm fine with defining a Semigroup/Monoid instance where required.
To expand on @HTNW's comment, there's a bit of conceptual problem here. Your data types allow the following distinct representations:
but all of these representations probably have the same meaning -- a
RecAwith "no fields occupied", right? Similarly, the two representations:probably have the same meaning -- a
RecAwith only theaField1occupied.If so, you should first give consideration to modifying your data types so that multiple representations of the "same
RecA" aren't possible.Using alternative types:
and working directly with a
RecAinstead of aMaybe RecAgives you the same expressive power, except there's now a unique representation for each possibleRecA. ARecAwith no fields is:while a
RecAwith only theaField1occupied is:With these representations, the
lens-generated optics work great, and the desired optic for your example is justaField2.bfield1:Without a change in representation, the problem is much harder to solve.
The difficulty is that it's not possible to construct a lawful setter that works correctly over multiple representations of "
Nothing". If you require that allMaybe RecAvalues be "normalized" to push allNothingvalues to the "top" of the structure, then a lawful setter that works only on such normalized values is possible, but you would need to take all thelens-generated optics (i.e., those generated bymakeLensesabove) and write lots of boilerplate to convert them to the "normalizing" optics you need. You'd also need a special operator to compose them, because composition with(.)wouldn't give you the right optics.Note that, if your primary justification for having these
Maybe-filled structures was the convenience of writing:in place of:
then you might be interested in the
Defaulttype class in thedata-defaultpackage: