17 ms·
Working on JSON with lenses and prisms
- Peaker 13y agoI love examples that show that with a little effort (at least when standing on the shoulders of giants), we can have our cake (nice abstractions) and eat it too (not lose type safety).
- tel 13y agoFor the non-Haskeller audience, let me just copy the money quotes. This tutorial eventually shows how we can write the following Python > 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.
- hershel 13y agoAs some who doesn't speak haskell, how do you add type safety to this example, but using more precise types than string/object/array ?
- tel 13y agoThere are already more precise types being inferred by the compiler and erased. Broadly, the "right" way to use typed JSON in Haskell is to write a JSON "schema" via FromJSON and ToJSON instances. This is fairly easy due to generic recursion in Haskell, but much more work than the python or lens examples. 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.
- Peaker 13y agoThere's Template Haskell support to auto-generate FromJSON/ToJSON instances, so if you don't care exactly how the JSON looks like, you can get type-safe JSON serialization/schema validation for free.
- tel 13y agoI personally find those wonderful in prototyping but it's rare that I control both ends of a JSON pipeline so it's important for me to write custom schema. 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.
- peq 13y agoI don't see how the Haskell example is more type safe than the Python example. In Haskell you will get None or Just a String. In Python you get an exception or a String. For me that is equivalent, but maybe you have a different definition of type safety? I think without a schema for the json data we cannot make this any more type safe.
- tel 13y agoI agree completely that this example is not fully type safe---there is an implicit, partial schema where either Python exceptions or Haskell Nothings imply validation failure, but that's beside the point. 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.