The data templating language

Comparisons With Other Languages / Tools

Comparison With JSON

JSON is used to describe data literally. Jsonnet extends JSON. Jsonnet has relaxed syntax and comments to make it easier for humans to write. Jsonnet also adds constructs for generating, translating and refining data. Because Jsonnet is an extension of JSON, valid JSON is also valid Jsonnet that just emits that JSON unchanged.

Comparison With YAML

YAML is also an extension of JSON that generates JSON. Like Jsonnet, YAML is intended to improve the user experience when data is transfered over the human / machine boundary. Its syntax is very tight; YAML files are much shorter than their JSON equivalents. However its constructs for generating, translating, and refining data are very weak in comparison to Jsonnet. Moreover, the tightness of its syntax makes it hard to extend YAML to add these features. In particular, YAML and Jsonnet disagree about what 1+1 should mean. YAML already defines it to mean the string "1+1", whereas in Jsonnet we want it to be 2. Jsonnet therefore builds from JSON and not YAML.

Comparison With Other Templating Languages

Jsonnet is a form of templating language as it generates data from programs that interleave verbatim data with computation constructs. However other templating languages operate on plain text. That is to say that the data amongst which the computational constructs are intermingled are simply plain text, and the output of the evaluation is also plain text. This means such systems can be used to produce files with arbitrary syntax, e.g. custom configuration file syntax, HTML, or even JSON.

However, since these templating languages are unaware of the syntax of the file they're generating (they think it is simply plain text), they cannot help the programmer avoid creating errors in the underlying representation.

By analogy, the C Pre-Processor has no understanding of C syntax. C Pre-Processor macros can break that syntax if they are used wrongly. The net result of this is that problems with the use of these macros must be debugged by examining the output of the evaluation (i.e. the generated C code). Errors cannot be presented at the level at which the programmer is actually working. For this reason, the vast majority of modern languages do not feature a pre-processor.

The situation is the same when templating languages are used to generate HTML, often the developer will check the generated HTML because successful evaluation of the template does not imply the HTML is correct. The same is true when the target language is JSON. Jsonnet avoids these problems, as although it is limited to JSON output, it is guaranteed that the output will be valid JSON.

Comparison With Other Functional Languages

ML is a functional language like Jsonnet. However ML is not pure, and also has strict evaluation. Haskell is a pure lazy functional language, but unlike Jsonnet it has static type safety. Providing static type safety means programmers have to understand type inference, unification errors, and also provide the occasional type annotation. Even then, some correct programs are rejected.

The Nix expression language, which is a pure lazy functional language with dynamic typing, is therefore closer to Jsonnet. Jsonnet differs from the Nix expression language mainly in that it also has object-oriented semantics in the form of mixins. Also, Jsonnet is an extension of JSON, whereas the Nix expression language is not (in particular it lacks floating point numbers). Finally, the Nix expression language is an embedded part of the Nix package manager (and other tools), whereas Jsonnet is a standalone evaluator that can sit in front of anything that accepts JSON.

Comparison With Other Scripting Languages

Most scripting languages are dynamically typed languages, often with object-oriented features.

However, Jsonnet is also a templating language, which makes it very good for generating data that is a mixture of verbatim and computed elements. Jsonnet programs can be easily understood and modified by someone who only knows JSON. Any parts of the program which are just JSON (i.e. do not make use of the extended Jsonnet constructs) can be modified with predictable results. Jsonnet's pure functional semantics also make modifying the program more predictable.

More importantly, Jsonnet has the property of hermiticity. This means that regardless of when or where a Jsonnet program is evaluated, it will always generate the same JSON. This means the program can be freely converted to data, and thus it can be thought of as equivalent to data. This is not the case with general purpose languages, as they are allowed to read files, contact the network, look up the time, or any number of other sources of non-deterministic behavior. While it is possible to sandbox some scripting languages in order to patch all of these holes, the result of this is a new language that is incompatible with the original language. This makes Jsonnet a lot more suitable for configuration than general purpose scripting languages.

One advantage that is often put forward for using general purpose scripting languages as configuration languages is that they are already well-understood by a large number of people. However this is also true of Jsonnet, because of its backwards compatibility with JSON, its use of mixins (a decades old construct that is well-understood) and the mimicking of Python syntax in cases where that makes sense (e.g. string formatting and comprehension constructs).

Finally, Jsonnet is a much smaller language than most general purpose scripting languages, yet despite this it has considerable expressive power.

Comparison With Other Configuration Languages

Coil is a configuration language that is interpreted by a Python library. Like Jsonnet, it has a JSON-like data model and some facilities for extending objects to avoid duplication, referring to other parts of the structure, and importing from other files. However it is a long way from the expressiveness of the object-oriented semantics of Jsonnet, and it also does not have the ability to do arithmetic beyond substituting values into strings. Finally, coil binds variables according to dynamic scoping rules, which have long been discredited because they quickly make code very difficult to understand. Jsonnet uses static scoping, like almost all mainstream languages.

Pystachio is a Python framework that makes it easier to use Python itself as a configuration language. It extends Python's existing data literals (which are JSON-like) to enforce immutability and add some templating functionality. However it lacks the full computational abilities of Jsonnet. Although it does allow some computation within string values via template expansion, the only variables you can access are stored in separate "environments", and there is no direct access to the data being configured. Even if we assume that all data is stored in environments and object fields merely forward to the environment, the language does not have any true object-orientation features. While scopes are inherited from outer objects, this just an implicit way of doing what you can do in Jsonnet with $ or local. In Pystachio there is no way to derive a struct from another struct with a few changes, let alone use late binding or the expressive power of mixins.

Finally, there are a number of other configuration languages that have grown to be used by a range of applications. For example INI files or Apache config files. Typically these offer few features beyond simple JSON and provide very little functionality when compared to Jsonnet.