Sunday, February 5, 2017

Program JSON and YAML with Dhall

This is short post to announce a new dhall-json library which lets you compile the Dhall configuration language to both JSON and YAML. This in turn means that you can now program JSON and YAML using a non-Turing-complete programming language.

Here's a quick example of the library in action:

$ dhall-to-json <<< "{ foo = 1, bar = [1, 2, 3] }"
$ dhall-to-yaml <<< "{ foo = 1, bar = [1, 2, 3] }"
foo: 1
- 1
- 2
- 3
$ cat example
    let indexed
in  let map
in  let words
        =   [ "Lorem"
            , "ipsum"
            , "dolor"
            , "sit"
            , "amet"
            , "consectetur"
            , "adipiscing"
            , "elit"
in  let makeRow
        =   λ(r : { index : Natural, value : Text }){ index      = r.index
            , background = if Natural/even r.index then "Green" else "Red"
            , contents   = r.value
in map
   { index : Natural, value : Text }
   { index : Natural, background : Text, contents : Text }
   (indexed Text words)
$ dhall-to-json <<< "./example"
$ dhall-to-yaml <<< "./example"
- contents: Lorem
  index: 0
  background: Green
- contents: ipsum
  index: 1
  background: Red
- contents: dolor
  index: 2
  background: Green
- contents: sit
  index: 3
  background: Red
- contents: amet
  index: 4
  background: Green
- contents: consectetur
  index: 5
  background: Red
- contents: adipiscing
  index: 6
  background: Green
- contents: elit
  index: 7
  background: Red

This library bundles both JSON and YAML functionality together because Haskell's yaml library reuses the exact same data type that Haskell's aeson library uses to represent JSON (i.e. the Value type). This means that if you can compile a Dhall expression to a Value then you can render that Value as both JSON and YAML.

Unlike the Dhall bindings to Nix, you can't compile most Dhall features to JSON or YAML, since they aren't real programming languages. After all, the whole point of this binding is to make JSON and YAML programmable! So if you try to compile anything other than primitive types or records you will get a compile error:

$ dhall-to-json <<< "λ(x : Bool) → x"

Error: Cannot translate to JSON

Explanation: Only primitive values, records, ❰List❱s, and ❰Optional❱ values can
be translated from Dhall to JSON

The following Dhall expression could not be translated to JSON:

↳ λ(x : Bool)x

Right now I'm slowly adding Dhall integrations with new languages and configuration file formats so that people can use Dhall as a universal configuration language. So far I've targeted integrations that can reuse the initial Haskell implementation of Dhall (i.e. Nix, JSON, and YAML bindings). However, my next integration will probably reimplement Dhall in another language so that Dhall can be used to natively configure a wider range of languages without having to invoke a command-line dhall executable.

I'm most likely to build the second implementation of Dhall in Rust so that I can also reuse the same implementation to expose a C and C++ API. However, I've never done this sort of thing before, so if this is a dumb idea, let me know!

You can find the dhall-json package on Github or Hackage and if you would like to contribute to Dhall in general you can open an issue here.

No comments:

Post a Comment