Easily, even. The most direct would be this:
#let inputs = (
name: sys.inputs.at("name", default: "Guest"),
)
Personally, I think this here is also a great option:
#let inputs = (
name: "Guest",
..sys.inputs,
)
When you spread a dictionary into another, the later keys (i.e. the ones from sys.inputs) will override the earlier ones (i.e. default values). This way, the inputs dict will contain extra keys if they were provided via the CLI, while the previous option would only have the "name". As long as you only access known keys, you won’t notice the difference.
You can also more clearly separate the defaults and the CLI overrides like this:
#let defaults = (
name: "Guest",
)
#let inputs = for (key, default) in defaults {
((key): sys.inputs.at(key, default: default))
}
That code is a bit involved: is generates dicts where the key is not a literal "key" (which is what happens when the key is an identifier), but instead with the string contained in key (which is what happens when the key is a different expression, such as a parenthesized one). It then uses a for loop to join all those individual dicts, creating a single dictionary with all the values.
Of course, this can be rewritten as a function, too:
#let sys-inputs(..defaults) = {
assert.eq(defaults.pos(), (), message: "only named arguments allowed")
let defaults = defaults.named()
for (key, default) in defaults {
((key): sys.inputs.at(key, default: default))
}
}
#let inputs = sys-inputs(
name: "Guest",
)