Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shorthand for deep field overrides #227

Open
benley opened this issue Aug 15, 2016 · 7 comments
Open

shorthand for deep field overrides #227

benley opened this issue Aug 15, 2016 · 7 comments

Comments

@benley
Copy link
Contributor

benley commented Aug 15, 2016

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 :)

@sparkprime
Copy link
Collaborator

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 

@sparkprime
Copy link
Collaborator

Perhaps it is OK to force the user to collect the c and b modification into a single mixin there?

@farcaller
Copy link

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.

@sparkprime
Copy link
Collaborator

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.

@CertainLach
Copy link
Contributor

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; })

@jacksongoode
Copy link

Has this feature been implemented?

@gytis-ivaskevicius
Copy link

Not in version v0.20.0

Is this planned? It really does not seem like that big of a feature

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants