DEV Community

Hercules Lemke Merscher
Hercules Lemke Merscher

Posted on • Originally published at bitmaybewise.substack.com

1

The Sneaky String Concatenation Of Jsonnet

Have you ever been bitten by a weird language quirk? Well, I stumbled upon something interesting while working with Jsonnet (that config language that makes our lives easier... mostly).

At first glance, this looks like it should throw an error, right?

"2 apples + " + 42.0
+ ", " { answer: 42 }
+ ", " + [42]
Enter fullscreen mode Exit fullscreen mode

This code actually works, despite the lack of a plus operator on the line where a string precedes an object:

$ jsonnet concat_string_to_multiple_types.jsonnet
"2 apples + 42, {\"answer\": 42}, [42]"
Enter fullscreen mode Exit fullscreen mode

It is happening because Jsonnet is automatically converting objects to strings and implicitly concatenating them 🤯

This seems an undesirable effect, but it might not be that unexpected. Let's dig in!

Jsonnet also uses the plus operator for merging objects, e.g.:

{ a: 1 } + { b: 2 }
Enter fullscreen mode Exit fullscreen mode

And the output will be:

$ jsonnet merge_objects.jsonnet
{
   "a": 1,
   "b": 2
}
Enter fullscreen mode Exit fullscreen mode

Looking at go-jsonnet parser, it returns a new ast.ApplyBrace value when it finds a left brace token:

case tokenBraceL:
                obj, end, err := p.parseObjectRemainder(op)
                if err != nil {
                    return nil, err
                }
                lhs = &ast.ApplyBrace{
                    NodeBase: ast.NewNodeBaseLoc(locFromTokens(begin, end), ast.Fodder{}),
                    Left:     lhs,
                    Right:    obj,
                }
Enter fullscreen mode Exit fullscreen mode

It holds the left and right-hand side values. Let's see the ast type definition:

// ApplyBrace represents e { }.  Desugared to e + { }.
type ApplyBrace struct {
    Left  Node
    Right Node
    NodeBase
}
Enter fullscreen mode Exit fullscreen mode

Huh!? Turns out, it's not an unexpected behavior.

It's a syntax sugar, where we can omit the plus operator:

{ a: 1 } { b: 2 }
Enter fullscreen mode Exit fullscreen mode

The output will be the same. It's a very common pattern used in Jsonnet. It might not look like so with the contrived example I presented above, but it's normal to see examples such as:

local book = { title: "hitchhiker's guide to the galaxy" };
// a bunch more code...
book {
    answer: 42
}
Enter fullscreen mode Exit fullscreen mode

That outputs:

$ jsonnet merge_objects.jsonnet
{
   "answer": 42,
   "title": "hitchhiker's guide to the galaxy"
}
Enter fullscreen mode Exit fullscreen mode

The thing is if either the left or right-hand side of ast.ApplyBrace is a string, the plus operator will be applied and when you try to add anything to a string, the value will be cast to string automatically to be concatenated.

As far as I know, this behavio is not documented, but is part of the language spec. I assume this is on purpose, as I doubt anyone would like users trying to rely on such a tricky aspect of the language.

So next time you're concatenating strings in Jsonnet, double-check those plus signs -- or your objects might just string themselves along! 😅


Thanks for reading Bit Maybe Wise! Subscribe for more Jsonnet-related posts in the future.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (0)

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up