Search code examples
jsonnet

Combining objects with + (plus operator) vs whitespace


Jsonnet's docs mention that the + operator can be used for inheritance, or, as it's worded in the tutorial, to combine objects:

{
  a: 1,
  b: 2,
}
+
{
  a: 3
}

However, I've noticed that - at least in simple cases like the above - simply omitting the + operator and writing the two consecutive objects separated by whitespace compiles down to the same result. That is, there's no difference in output between the program above and this one:

{
  a: 1,
  b: 2,
}
{
  a: 3
}

I'm puzzled, because I haven't noticed any mention of this implicit object combination in the docs. I also notice that this behaviour seems to be unique to objects and doesn't happen with other types. (In particular, despite Jsonnet drawing inspiration from Python for some of its features, you can't implicitly concatenate strings with whitespace like you can in Python.)

Thus, some questions:

  • Is this even intended behaviour, or a bug?
  • Is it documented anywhere?
  • Are there any behaviour differences between combining objects with the explicit + operator and combining them implicitly with whitespace?

Solution

  • A colleague nudges me towards a mention of this in the tutorial, in the Object Orientation section:

    Let's make it more concrete by mixing deriving some cocktails that are quite similar from a template that draws out their similarities. The + operator is actually implicit in these examples. In the common case where you write foo + { ... }, i.e. the + is immediately followed by a {, then the + can be elided. Try explicitly adding the + in the 4 cases, below.

    local templates = import 'templates.libsonnet';
    
    {
      // The template requires us to override
      // the 'spirit'.
      'Whiskey Sour': templates.Sour {
        spirit: 'Whiskey',
      },
    
      // Specialize it further.
      'Deluxe Sour': self['Whiskey Sour'] {
        // Don't replace the whole sweetner,
        // just change 'kind' within it.
        sweetener+: { kind: 'Gomme Syrup' },
      },
    
      Daiquiri: templates.Sour {
        spirit: 'Banks 7 Rum',
        citrus+: { kind: 'Lime' },
        // Any field can be overridden.
        garnish: 'Lime wedge',
      },
    
      "Nor'Easter": templates.Sour {
        spirit: 'Whiskey',
        citrus: { kind: 'Lime', qty: 0.5 },
        sweetener+: { kind: 'Maple Syrup' },
        // +: Can also add to a list.
        ingredients+: [
          { kind: 'Ginger Beer', qty: 1 },
        ],
      },
    }
    

    So, implicit object combination:

    • is legal
    • is documented, but seemingly only in the tutorial and formal spec, currently, not in the language reference
    • has behaviour identical to +, except that it's only usable when the second operand starts with a {, not when it's a variable.