Tsonnet #10 - There and back again… concatenating strings
Where we explore Jsonnet's "string + whatever = totally fine!"
Welcome to the Tsonnet series!
If you're just joining, you can check out how it all started in the first post of the series.
In the previous post, we added support for identifiers:
Now let's go back to one topic we covered already, concatenating strings.
Why revisit string concatenation?
In Jsonnet, the plus (+) operator is overloaded by design, meaning it can adhere to the object type and do a certain operation depending on the context. For example:
Those are all valid. Let's run this through Jsonnet and see what we get:
If you're curious about the nitty-gritty details of this behavior, I've dug deep into it in another post:
This is not very sound when it comes to type-checking. A safer and expected behavior would be std.toString
to convert whatever type we want to concatenate, to match the string type, explicitly. Not implicit conversions.
However, it is what it is. If I want to keep compatibility with Jsonnet, I need to support this behavior.
BREAKING CHANGE: the odd case where a string and an object side-by-side, without using the plus operator, result in a string concatenation won't be supported in Tsonnet -- really, this is possible in Jsonnet. How mindblowing is that?! I covered this in the Jsonnet post above, in case you haven't read it.
Eventually, I'd like to add a flag to the compiler, to strictly check and fail on such cases. But until the stdlib
is implemented, and for compatibility's sake, the default behavior will be the implicit conversion.
Let's concatenate strings, the universe, and everything
First things first. Let's add a new Jsonnet sample to cover the string concatenation to varied values:
And a cram test targeting it — plus a tiny refactoring renaming the cram test file:
Now comes the fun part — we need to make Tsonnet handle all these cases. Here's how we're going to restructure our code:
The
interpret_bin_op
function was doing arithmetic operations only, so it makes sense to rename it tointerpret_arith_op
The pattern match on the binary operation will check if one of the expressions is a string to know if it is a string concatenation operation
In the
interpret_concat_op
, the non-string expression is transformed to a string before concatenation
And voilà:
Conclusion
In this post, we've implemented Jsonnet's string concatenation behavior in Tsonnet, including its distinctive approach to type coercion. While this implicit type conversion might seem counterintuitive from a type safety perspective, it maintains compatibility with Jsonnet's specification.
This addition to Tsonnet highlights an interesting trade-off: the balance between type safety and convenience. While automatic type coercion can make code more concise and easier to write, it may also introduce subtle bugs that are hard to catch. That's why I'm planning to add that strict mode flag — you'll get to pick your poison: Jsonnet compatibility or stricter type checking.