Working on JSON with lenses and prisms
fpcomplete.com
fpcomplete.com
> str = '[{"someObject" : { "version" : [1, 0, 0] }}]'
> it = json.loads(str)
> it[0]["someObject"]["version"][1]
as the following Haskell '[{"someObject" : { "version" : [1, 0, 0] }}]' ^?
nth 0 . key "someObject" . key "version" . nth 1
while keeping type safety, composability, and purity. That's the wonderful part. The rest is explaining a lot of how the magic works via Maybe monad chaining and thinking about what Lenses and Prisms are.It's important to note that in order to interact with JSON Haskell uses an un typed microlanguage called aeson.Value which is the most direct translation to un typed JSON. The From/ToJSON code does a much more powerful lifting and lowering of Value into full Haskell types.
One thing I've found really useful is generating ADTs which have structure that's driven by the exact format to make the TH derive a correct To/FromJSON instance and then exporting it abstractly and having lots of Lenses which specify a more Haskell-friendly access pattern.
I think without a schema for the json data we cannot make this any more type safe.
The example written has a lot of type resolution done implicitly by the typeclass mechanism. It ensures that only valid lens-like combinators are applied to the traversal chain... which is only an issue due to how far we can flex the lens library to do other things.
A better example is the somewhat more explicit version of this code
it ^? _JSON . _Array . nth 0
. _Object . key "someObject"
. _Object . key "version"
. _Array . nth 1
Where the _JSON, _Array, nth, _Object, and key declarations must match otherwise its a type error. Furthermore, the `nth` and `key` calls are both just type-restricted aliases for `ix` which help to convey via types what kinds of very general tools should be applied here.We could also, for instance, throw another _JSON Prism into the chain to do a nested, schema-based validation for any types which have ToJSON/FromJSON instances.