Search code examples
jsonhaskellhaskell-lens

Prism, Traversal, or Fold from readMaybe


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.


Solution

  • 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.