We use a lot of Jsonnet a work. We use it to generate Cloud Formation templates, Kubernetes YAML files, and application configuration from a single source file.

Jsonnet is effectively a templating language for structured data. It looks a lot like Json (which is by design since it’s meant to be a drop-in replacement) but includes a few quality of life improvements, such as comments and letting you include the last comma in a list; something that a strict Json parser will never let you do:

[
    { some: "value" },
    { another: "value" },  // Perfectively valid
]

Generally, it works pretty well, except for one sneaky “feature” that has caught me off guard a few times.

If you have an object next to another object, and the two objects do not have a comma separating them, like so:

[
    { some: "value" }
    { another: "value" }
]

Jsonnet will merge it into a single object:

[
    { some: "value", another: "value" }
]

This is a documented feature, but as someone who is used to dealing with Json and it’s strict parsing rules around commas, it seems to me like a bit of a gotcha. It might be obvious in this example but for larger files, a missing comma like this is less likely to be intentional, and more likely to be an accident.

Consider this: you’ve got a long list of large objects spanning tens of lines and you need to add a new object to the end:

[
    {
        "product": "apples",
        "price": 100,
        "tax": 20,
        "shipment": "basic"
    },

    // ... skip 23 other items here

    {
        "product": "yachts",
        "price": 50000,
        "tax": 20,
        "shipment": "expensive"
    }

    // Add price of zebras here
 ]

Given our evolved need to save energy, you’re more likely to copy the object for yachts instead of typing out the object for zebras in full. Note that the object for yachts doesn’t have a trailing comma since we’ve been trained to follow the strict rules of Json parsers. So you copy yachts and paste it on the line below. Now both the original and pasted objects for yachts have no comma, but you don’t notice that because you’re focused on changing the pasted-in object to be one about zebras.

Then, when it comes time to evaluate your config template, you’re down one item. Thinking that you’ve missed something trivial, like saving the file or something, you expect the missing object to be the zebras. But it’s not the zebras, it’s actually the yachts that are absent.

Eventually you find the missing comma, but you’re a little annoyed that a language that lets you include commas on the last item can ensnare you like this. Sure the feature is documented, but are you really going to read the manual when you’re doing something as trivial as adding new objects to a list? Not helping matters is there’s actually an explicit operator to merge two objects together (the + operator). There wouldn’t be a need for another one that does the same thing, right?

So, language designers, please consider things like this the next time you design a domain-specific language. Especially one based on an existing language with stricter syntax rules than the ones that you enforce. People come to rely on these rules to help them catch mistakes like this.