Cue is partially inspired by functional languages
and has some features which help us to write
“code” which generates large amounts of config or data.
When you combine comprehension with conjunctions, defaults, and guards,
they will all become invaluable assets to your Cue toolbox.
List and field comprehensions allow us to iterate over an iterable
and generate new lists or fields. An iterable is an existing list or the fields of a struct.
import"strings"band: { name: "Led Zeppelin"// List comprehension selfTitled: [ for i, I in _selfIndexAlbums {title: "\(name) \(I)"}]// nested list comprehension allAlbums: [for I in _selfIndexAlbums {title: "\(name) \(I)"},for N in _namedAlbums {title: "\(N)"}, ] Albums: {// Field comprehension for key, val in allAlbums {"\(strings.TrimSpace(val.title))": { pos: key artist: name title: strings.TrimSpace(val.title) titleLen: len(val.title) } } }// Hidden fields _selfIndexAlbums: ["", "II", "III", "IV"] _namedAlbums: ["Houses of the Holy","Physical Graffiti","Presence","In Through the Out Door","Coda", ]}
We’ve introduced a few things in this file:
list comprehension: [ for key, val in iterable { ... } ]. Notice you can leave the key off, and you just get the value.
field comprehension: { for key, val in iterable { ... } }. We’ve interpolated a field name by calling a builtin, notice the surrounding quotes.
string interpolation: "\(<a cue expression>)". These are based on Swift’s string interpolation, and it’s the only valid mechanism when you want to be JSON compatible.
hidden fields: _hidden: "I'm hidden" which begin with an underscore. There are also hidden definitions (_#)
band:name:Led ZeppelinselfTitled:- title:'Led Zeppelin '- title:Led Zeppelin II- title:Led Zeppelin III- title:Led Zeppelin IVallAlbums:- title:'Led Zeppelin '- title:Led Zeppelin II- title:Led Zeppelin III- title:Led Zeppelin IV- title:Houses of the Holy- title:Physical Graffiti- title:Presence- title:In Through the Out Door- title:CodaAlbums:Led Zeppelin:pos:0artist:Led Zeppelintitle:Led ZeppelintitleLen:13Led Zeppelin II:pos:1artist:Led Zeppelintitle:Led Zeppelin IItitleLen:15Led Zeppelin III:pos:2artist:Led Zeppelintitle:Led Zeppelin IIItitleLen:16Led Zeppelin IV:pos:3artist:Led Zeppelintitle:Led Zeppelin IVtitleLen:15Houses of the Holy:pos:4artist:Led Zeppelintitle:Houses of the HolytitleLen:18Physical Graffiti:pos:5artist:Led Zeppelintitle:Physical GraffitititleLen:17Presence:pos:6artist:Led Zeppelintitle:PresencetitleLen:8In Through the Out Door:pos:7artist:Led Zeppelintitle:In Through the Out DoortitleLen:23Coda:pos:8artist:Led Zeppelintitle:CodatitleLen:4
Conjunctions and Defaults
We’ve already seen conjunctions being used for validation.
You can also use them to build up Cue values and your configuration.
#labels: [string]: string#metadata: { name: string// we can explicitly prevent namespace by commenting it out and ensuring this definition is closed// namespace?: string labels: #labels annotations?: [string]: string}// require the following keys for labels#requiredLabels: #labels & { app: string env: string team: string}// for env, limit the choices and default to dev with '*'#defaultLabels: #requiredLabels & { env: *"dev"|"stg"|"prd"}// set concrete values to be reused#myLabels: #defaultLabels & { app: "cuetorials" team: "hofstadter"}// our Kubernetes definitions from before#Schema: #Deployment | #Service | #Ingress// Additionally apply our labels buildup to the resources#Schema: { metadata: #metadata & { labels: #myLabels }}#Deployment: { apiVersion: "apps/v1" kind: "Deployment" ...}#Service: { apiVersion: "v1" kind: "Service" ...}#Ingress: { apiVersion: "extensions/v1beta1" kind: "Ingress" ...}
Guards of Conditionality
Cue has an if statement called a guard which protects an area of code.
This is not a conditional branch as much as a way to conditionally include or extend some Cue code.
Recall that Cue is turing incomplete and thus doesn’t have concepts of loop or branches.
guards.cue
// use if guard to get even numbers in a list comprehensionnums: [1, 2, 3, 4, 5, 6, 7, 8]sqrs: [ for n in nums ifmod(n, 2) ==0 {n * n}]// use if guard to conditionally add fieldselems: [ {name: "a", public: true}, {name: "b", public: true}, {name: "c", public: false}, {name: "d"},]Elems: { hasPublicField: {for key, val in elems {// test against _|_ to see if a field existsif val.public !=_|_ {"\(key)": val } } } stringifyPublic: {for key, val in elems {// there is no short-circuit, so we must nest guard insteadif val.public !=_|_ {// test conjunctions not equal to bottom for "truthiness"if (val.public &true) !=_|_ {"\(key)": val & {PUB: "true"} }// note, there is no "else" clauseif (val.public &false) !=_|_ {"\(key)": val & {PUB: "false"} } } } }}