In working with lenses, I occasionally have the need for some basic text parsing in the chain of optics. In one API I'm dealing with, there is a JSON field like this:
"timespent": "0.25",
Since it is incorrectly encoded as a string instead of a number, I can't just do the typical lens-aeson
solution:
v ^? key "timespent" . _Double -- this doesn't work
So, I need this instead:
v ^? key "timespent" . _String . mystery
where the mystery
optic needs to turn Text
into a Double
. I know that mystery
could be typed as follows:
mystery :: Prism' Text Double
And I could built this as follows:
mystery = prism' (pack . show) (readMaybe . unpack)
And technically, this function could have an even more general type signature with Read
and Show
constraints, but that's not really what my question is about. What I don't like is the Prism
is really too strong for what I'm doing. Most of the time, I'm interested in parsing but not is rendering it back to a string. So, I need either a Traversal
or a Fold
but I'm not sure which one, and I'm not sure how to build them. When I look at the type signature of ^?
to find the minimum optic required, I see:
(^?) :: s -> Getting (First a) s a -> Maybe a
I can sort of understand what this means but not very well. So, to make my question clear, it is:
If I have a function f :: Text -> Maybe a
, how can I turn this into a Traversal
or a Fold
? Thanks, and let me know if there is anything I can clarify.
I think what you want is a Getter:
_Read :: Read a => Getter Text (Maybe a)
_Read f = contramap g . f . g where g = readMaybe . unpack
In general, if you have x :: a -> b
then \f -> contramap x . f . x
is a Getter a b
.
Then to get the a
value out of the Maybe a
, use traverse
, so combined you have _Read . traverse
which is a Read a => Fold Text a
.