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

Destructuring objects within other objects #847

Closed
arlyon opened this issue Sep 27, 2020 · 4 comments
Closed

Destructuring objects within other objects #847

arlyon opened this issue Sep 27, 2020 · 4 comments

Comments

@arlyon
Copy link

arlyon commented Sep 27, 2020

There seems to be a 'feature hole' for destructuring objects within other objects. In python:

> x = {"a": 1, "b": 2}
> {"c": 3, **x}
{'c': 3, 'a': 1, 'b': 2}

This is useful if using top-level functions for your definitions. Say I'd like to create a TLA that dynamically composes other objects:

local clients = import 'compose/clients.libsonnet';
local servers = import 'compose/servers.libsonnet';

// generate a config for a given client / server config
function(client, server) {
    local clientFn = clients[client.name],
    local serverFn = servers[server.name],

    local clientCompose = clientFn(client.version, client.config),
    local serverCompose = serverFn(server.version, server.config),
    local dependency = {services+: {client+: {depends_on+: ["frontend"]}}},

    **(clientCompose + serverCompose + dependency)
}

The alternative (some dictionary expression) doesn't work.

function(client, server) {
    ...
    local compose = clientCompose + serverCompose + dependency,
    [key]: compose[key] for key in std.objectFields(compose)
}
compose.jsonnet:14:52-59 Unknown variable: compose

    [key]: compose[key] for key in std.objectFields(compose)

Originally posted by @arlyon in #307 (comment)

@arlyon arlyon changed the title There seems to be a 'feature hole' for destructuring objects within other objects. In python: Destructuring objects within other objects Sep 27, 2020
@sbarzowski
Copy link
Collaborator

The "for" part in object comprehension doesn't have access to object locals (they are scope "inside" the for and have access to the variable you're iterating over). This is why you get the error.

The problem with a flattening operator like you describe, is that objects in Jsonnet are more than dicts in Python. In Python dicts are just mapping from key to value and here we have objects with OOP sense with self and super.

I think you can achieve your goal with just plain inheritance (+ operator):

local clients = import 'compose/clients.libsonnet';
local servers = import 'compose/servers.libsonnet';

// generate a config for a given client / server config
function(client, server)
    local clientFn = clients[client.name];
    local serverFn = servers[server.name];
    local clientCompose = clientFn(client.version, client.config);
    local serverCompose = serverFn(server.version, server.config);
    local dependency = {services+: {client+: {depends_on+: ["frontend"]}}};
    clientCompose + serverCompose + dependency + { whatever_additional_field_I_want: 42 }

Please let me know if that works for you.

@arlyon
Copy link
Author

arlyon commented Sep 28, 2020

Hi thanks for the info, maybe I should have added it to the initial description but this example reproduces the syntax error in the trivial case:

function(a, b) {
    local thisA = {a: a},
    local thisB = {b: b},

    thisA + thisB
}
❯ jsonnet tla.jsonnet --tla-str a="hello" --tla-str b="world"
STATIC ERROR: tla.jsonnet:5:5: expected one of :, ::, :::, +:, +::, +:::, got: +

@sbarzowski
Copy link
Collaborator

sbarzowski commented Sep 28, 2020

Yes. Please take a look at where the curly braces are placed.

This works:

local fun(a, b) =
    local thisA = {a: a};
    local thisB = {b: b};
    thisA + thisB;
fun(1, 2)

The curly braces are not a part of the function syntax – they are a part of the object literal syntax. You have no enclosing object here, so no curly braces.

@arlyon
Copy link
Author

arlyon commented Sep 28, 2020

Thanks for the quick reply. It seems it is not possible to apply this syntax to a top level function. Omitting the local keyword produces a new syntax error:

function(a, b) =
  local thisA = { a: a };
  local thisB = { b: b };
  thisA + thisB;
❯ jsonnet tla.jsonnet --tla-str a="hello" --tla-str b="world"
STATIC ERROR: tla.jsonnet:1:16: not a unary operator: =

edit: solved it, thanks for your help

function(a, b)
  local thisA = { a: a };
  local thisB = { b: b };
  thisA + thisB

@arlyon arlyon closed this as completed Sep 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants