I have the following bit of code, and I'm not happy with it:
import Data.Aeson.Lens
import Data.Aeson.Types
import Data.Text hiding (foldl, map)
path :: (Applicative f, AsValue t) => [Text] -> (Value -> f Value) -> t -> f t
path [] = error "No path provided"
path (i:is) = foldl (.) (key i) (map key is)
path
is meant to be a lens into a nested JSON object. For instance:
"{ \"a\": { \"b\": 8} }" ^? path ["a", "b"] == Just (Number 8.0)
Currently this works, but the somewhat obvious shortcoming is that the function is not total.
I tried the following definitions:
path is = foldl (.) id (map key is)
path [] = id
path (i:is) = foldl (.) (key i) (map key is)
-- This one doesn't compile
path [] = _Object
path (i:is) = foldl (.) (key i) (map key is)
Running the same code block as above with these definitions yields Nothing
, instead of the expected 8
.
I'm almost sure this is a case of type juggling that I'm just missing, but I can't figure out how to make this work with a total function. The expected behavior is for path []
to be a Lens
which focused on the whole json object.
Use _Value
for the empty case:
> ("null" :: String) ^? _Value
Just Null
> ("null" :: String) ^? (foldl (.) _Value $ map key ["foo", "bar"])
Nothing
> ("{ \"a\": { \"b\": 8} }" :: String) ^? (foldl (.) _Value $ map key ["a", "b"])
Just (Number 8.0)
Also path
as you describe cannot be a Lens
as it's partial (as you mention!), at best it is Traversal
. Yet if you are really sure what you are doing, than you can use signular
to convert Traversal
into Lens
.