shorthand for deep field overrides
local foo = {
a: { b: { c: { d: 1234 } } },
e: 1
};
foo + { a.b.c.d: 999 }
It would be really cool if the above (or something along those lines) worked and evaluated to:
{
a: { b: { c: { d: 999 } } },
e: 1
}
To achieve the same in today's jsonnet you would have to write:
local foo = {
a: { b: { c: { d: 1234 } } },
e: 1
};
foo + { a+: { b+: { c+: { d: 9999} }
I don't know how difficult and/or sane this would be to implement, but there is a similar construct in Nix that is quite useful :)
Yeah this has come up a few times. I agree it would be nice.
You need a way to be able to compute the fields too, perhaps like this:
foo + { ["a"].["b"].["c"].["d"]: e }
presumably self in each of those expressions bind the same way it would if there were syntax sugar, although there are no explicit { } there to guide the eye.
Then there's the question of avoiding duplicates:
{ ["a"].["b"]: e, ["a"].["c"]: e } // OK
{ ["a"].["b"]: e, ["a"].["b"]: e } // Not OK
Perhaps it is OK to force the user to collect the c and b modification into a single mixin there?
Here's the design I came up with after some thinking and trying to figure C++ and parsers.
Shorthand-dot operator suggestion
Currently, the parseObjectRemainder() will look for tokens BRACKET_L, IDENTIFIER, STRING_DOUBLE, STRING_SINGLE or STRING_BLOCK and will parse the following tokens as [+]:{1,3} construct.
I suggest to extend the case of IDENTIFIER initially to allow DOT to follow it. In that case, the remainder is parsed recursively as either IDENTIFIER+DOT pairs, or the usual [+]:{1,3}.
This makes the following:
{
a.b.c: 1,
}
equal to
{
a+: {
b+: {
c+: 1
}
}
}
which is, supposedly, one of the most used cases. The duplicates resolution must be updated to consider duplicates only of the leaves of DOT-chains, otherwise {a.b.c=1, a.b.d=2} would fail.
Version 2
To cover additional cases, STRING_DOUBLE, STRING_SINGLE or STRING_BLOCK should be allowed in the syntax (I don't think STRING_BLOCK would actually be any useful, but there's no good reason to exclude it).
Version 3
Allow indexing expressions of single elements of paths via [ expr ], e.g. { a.b[0].c: 1}. This would be primarily useful to override in-array values.
Note that I ignore the computational part of the dot-paths on purpose. The whole idea for them is to allow quick overrides in deep-nested objects (those are very common in kubernetes definitions) to make them more human-readable.
I think you mean:
{
a.b.c: 1,
}
is equal to
{
a+: {
b+: {
c: 1
}
}
}
whereas
{
a.b.c+: 1,
}
is equal to
{
a+: {
b+: {
c+: 1
}
}
}
For the duplicates resolution, I suppose you could just say that:
{
a.b.c: 1,
a.b.d: 1,
}
Must be expressed as:
{
a.b+: {
c: 1,
d: 1,
}
}
I.e., so there are no additional smarts needed in that code.
Then there's the question of avoiding duplicates:
{ ["a"].["b"]: e, ["a"].["c"]: e } // OK { ["a"].["b"]: e, ["a"].["b"]: e } // Not OK
I think this is fine to leave this feature as syntax sugar, and don't introduce any special runtime handling:
{ a.b.c[a].z: 1, a.b.c[a].x: 1 }
# =>
{
a+: {
b+: {
c+: {
[a]+: {
z: 1,
},
[a]+: {
x: 1,
},
},
},
},
}
Nix does it same way:
error: dynamic attribute 'test' already defined at (string):1:38
1| let a = "test"; in builtins.toJSON({ a.b.c.${a}.z = 1; a.b.c.${a}.x = 1; })
Has this feature been implemented?