Is "composability" really a fundamental ytt limitation that jsonnet doesn't have? #392
-
Hi, Am I missing something? The following seems impossible in ytt, but is so easy in jsonnet: Composability - give the output of one template as the input to another template. I hope I'm wrong because there are many other things I love about ytt over jsonnet and would prefer to stay with ytt. It's as if the data.values mechanism is a single pass only. And that for me is a deal-breaker limitation that jsonnet doesn't seem to have. It's as if ytt is a programming language where you can call functions, but functions cannot call functions. Example time:
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 6 replies
-
Hey @pmorch! Thank you for taking the time to detail the situation and make so clear your question. 🙏🏼 There are a number of angles I see in here. I think I can best contribute here by drawing up the levels of composability present in Level 0: A FunctionWithin a given invocation of Attempting to translate from the jsonnet example you gave, we'd have:
#@ load("@ytt:template", "template")
#! First, capture the YAML document in a "YAML Fragment"
#!
#@ def my_template(foo):
---
field1: #@ foo + "1"
field2: #@ foo + "2"
#@ end
#! Later in the same file, I start a YAML document (`---`), and replace it with the document produced by `my_template()`, passing in the value for `foo`:
--- #@ template.replace(my_template("a-yada-")) yielding: field1: a-yada-1
field2: a-yada-2 So far, so good. Now, we can write a second function in terms of this first function (for clarity, I'm repeating the whole example): #@ load("@ytt:template", "template")
#! First, capture the YAML document in a "Fragment Function"
#!
#@ def my_template(foo):
---
field1: #@ foo + "1"
field2: #@ foo + "2"
#@ end
#@ def another_template(prefix):
#@ return my_template(prefix + "-yada-")
#@ end
#! Again, in the same file, I start a YAML document (`---`), and replace it with the document produced by `another_template()`, passing in the value for `foo`:
--- #@ template.replace(another_template("a"))
--- #@ template.replace(another_template("b")) producing: field1: a-yada-1
field2: a-yada-2
---
field1: b-yada-1
field2: b-yada-2 Level 1: A "Library" FunctionThe next tiny step would be to extract a function into a reusable chunk in a separate file. The key here is to add the
#! Define the "Fragment Function"
#!
#@ def my_template(foo):
---
field1: #@ foo + "1"
field2: #@ foo + "2"
#@ end
#@ load("@ytt:template", "template")
#@ load("template.lib.yml", "my_template")
#@ def another_template(prefix):
#@ return my_template(prefix + "-yada-")
#@ end
#! showing off the `lambda` expression for creating function values wrapping an expression
#@ yet_another_template = lambda prefix: my_template(prefix + "-yada")
#! Again, in the same file, I start a YAML document (`---`), and replace it with the document produced by `another_template()`, passing in the value for `foo`:
--- #@ template.replace(another_template("a"))
--- #@ template.replace(yet_another_template("b")) (More details on lambdas: Starlark spec > Lambda expressions) Level 2: A
|
Beta Was this translation helpful? Give feedback.
Hey @pmorch! Thank you for taking the time to detail the situation and make so clear your question. 🙏🏼
There are a number of angles I see in here. I think I can best contribute here by drawing up the levels of composability present in
ytt
and then we can step back and look at it, together.Level 0: A Function
Within a given invocation of
ytt
, we can capture any size chunk of YAML, wrap it in a function and then invoke it (Docs: Language > YAML Fragment).Attempting to translate from the jsonnet example you gave, we'd have:
template.yml