Types and Values



The following introduces the basic building blocks in Cue.

Builtin Types

Cue has the following built in types. They are meant to align with JSON types.

null  bool  string  bytes  number  struct  list
                             |
                            int

builtins.cue

N: null
B: bool
S: string
By: bytes
Num: number  // Decimals or integers, a superclass if you will
Int: int     // Big Int which can represent values without limits
St: {...}
Li: [...]

Top and Bottom

Top and Bottom are special values in Cue. They form the opposite ends of the value lattice.

_” is top and matches all values

_|_” is bottom and represents an error

Errors

Errors result in bottom with a message attached. You can get errors for incomplete types when exporting Cue to data or for conflicts and invalid semantics in your code.

errors.cue

s: "hello"
s: "world"    // conflicting values "hello" and "world"

A: foo: "bar"
a: A.goo     // undefined field A.goo (cue eval -c)

l: [1,2]
l: [1,3]     // conflicting values 2 and 3
v: l[2]      // index out of range

Null Coalescing

Null coalescing allows us to provide fallback values when errors occur. This is technically error coalescing since null is a vaid value. This works by using disjunctions and defaults.

coalesce.cue

elems: ["a", "b", "c"]
a: *elems[0] | "A"
// out of bounds error
d: *elems[3] | "D"

S: {
	hello: "world"
}

// missing fields
s: *S.foo | "bar"

cue eval coalesce.cue

elems: ["a", "b", "c"]
a: "a"
d: "D"
S: {
    hello: "world"
}
s: "bar"

Numbers

Cue defines two number kinds:

  • int are whole numbers, implemented as BigInt to represent any value, and can be constrained for byte size (like int64)
  • number are decimals numbers, (also not bounded by byte size, also constrainable?), ints are also numbers

numbers.cue

a: int
a: 42

b: number
b: 3.14

c: int & 42.0  // conflict int and 42.0

d: 42     // will be type int
e: 3.14   // will be type number

Cue has syntactic sugar for writing numbers too:

  • 0x, 0o, 0b for hex, octal, and binary
  • K, M, G, T, P for sizing with optional i
  • e/E for decimal exponents
  • Underscores for visual readability for large numbers

number-sugar.cue

hex: 0xDEADBEEF
oct: 0o755
bin: 0b0101_0001
cpu: 0.5Mi
mem: 4Gi
mill: 1M
bill: 1G
zero: 0.
half: .5
trim: 01.23
mole: 6.022_140_76e+23
tiny: 1.2345E-12
long: 23_456_789_000_000000

cue eval number-sugar.cue

hex:  3735928559
oct:  493
bin:  81
cpu:  524288
mem:  4294967296
mill: 1000000
bill: 1000000000
zero: 0
half: 0.5
trim: 1.23
mole: 6.02214076e+23
tiny: 1.2345e-12
long: 23456789000000000

Strings

Cue strings are valid UTF-8 sequences with some escaping options

strings.cue

str: "hello world"
smile: "\U0001F60A"
quoted: "you can \"quote by escaping \\ \""
multiline: """
hello world
a "quoted string in a string"
down under
   - some author
"""

cue eval strings.cue

str:    "hello world"
smile:  "😊"
quoted: "you can \"quote by escaping \\ \""
multiline: """
    hello world
    a "quoted string in a string"
    down under
       - some author
    """

The escape sequences are:

\a   U+0007 alert or bell
\b   U+0008 backspace
\f   U+000C form feed
\n   U+000A line feed or newline
\r   U+000D carriage return
\t   U+0009 horizontal tab
\v   U+000b vertical tab
\/   U+002f slash (solidus)
\\   U+005c backslash
\'   U+0027 single quote  (valid escape only within single quoted literals)
\"   U+0022 double quote  (valid escape only within double quoted literals)

\nnn   for octals         (valid escape only within single quoted literals)
\xnn   for hex            (valid escape only within single quoted literals)

\uXXXX  for unicode
\UXXXXXXXX for longer unicode

https://cuelang.org/docs/references/spec/#string-and-byte-sequence-literals

There is one more escape for string interpolation which we will see in the next section.

“Raw” Strings

Cue allows you to modify the string delimiters so you can avoid escaping. Use any number of # on both ends of normal strings

rawstrings.cue

str1: #"avoid using \ to "escape""#
str2: ##"""
#"""
a nested multiline
string goes here
"""#
"""##

cue eval rawstrings.cue

str1: "avoid using \\ to \"escape\""
str2: ##"""
    #"""
    a nested multiline
    string goes here
    """#
    """##

Bytes

Bytes are single quoted and base64 encoded when output:

bytes.cue

b: '\x03abc\U0001F604'

cue eval bytes.cue

b: '\x03abc😄'

cue export bytes.cue

{
    "b": "A2FiY/CfmIQ="
}

Lists

Cue lists have arbitrary elements of mixed types. They can optionally be open and predefine some elements. Mismatched elements result in errors.

lists.cue

empty: []
any: [...]
ints: [...int]
nested: [...[...string]]

opened: ints & [1,2,...]
closed: ints & [1,2,3]

// list of for constrained ints
ip: 4 * [uint8]
// sets the first element
tendot: ip & [10, ...uint8]
// uses constraint as second element
one72: ip & [172, >=16 & <=32, ...]

mixed: any & [...] & ["a",1, { foo: "bar" }]
join: [1,2] + [3,4]
Join: opened & join

cue eval lists.cue

empty: []
any: []
ints: []
nested: []
opened: [1, 2]
closed: [1, 2, 3]
ip: [uint8, uint8, uint8, uint8]
tendot: [10, uint8, uint8, uint8]
one72: [172, uint & >=16 & <=32, uint8, uint8]
mixed: ["a", 1, {
    foo: "bar"
}]
join: [1, 2, 3, 4]
Join: [1, 2, 3, 4]

Structs

Structs are like JSON objects. They are the primary composite type in Cue. They have a set of fields (label: value). By default, they are open and you can add more fields.

structs.cue

// an open struct
a: {
	foo: "bar"
}

// shorthand nested field
a: hello: "world"

// a closed struct
b: close({
	left: "right"
})

// error, field up not allowed
b: up: "down"

Definitions

Definitions are very similar to structs and are primarily used for schemas. They are closed by default and are not emitted by Cue when exporting.

defns.cue

#schema: {
	word: string
	num: int | *42
	optional?: string
}

value: #schema & {
	word: "what's the good?"
}

cue eval defns.cue

#schema: {
    word: string
    num:  42
}
value: {
    word: "what's the good?"
    num:  42
}

cue export defns.cue

{
    "value": {
        "word": "what's the good?",
        "num": 42
    }
}

Embeddings

You can embed structs and definitions within each other as a method to build up values. You can achieve the same with opened structs / definitions and conjunctions, but often we cannot modify what we can embed.

embed.cue

#A: {
	num: number
}

#B: {
	ans: string
}

// this won't work
#bad: #A & #B
bad: #bad & {
	num: 42
	ans: "life"
}

// but this will
#val: { #A, #B }
val: #val & {
	num: 42
	ans: "life"
}

cue export embed.cue

{
    "val": {
        "num": 42,
        "ans": "life"
    }
}

Pattern Matching Constraints

Pattern matching allows you to specify constraints for labels which match a pattern. While limited in matching today, they will be significantly more powerful once the query proposal is accepted and implemented. For now, you can apply a constraint to string labels and use an identifier to set fields if you like.

patterns.cue

#schema: {
	name: string
	ans: string
	num: int | *42
}

// match elem fields and alias labels to Name,
// unify with schema, set name to Name by label
elems: [Name=_]: #schema & { name: Name }

elems: {
	one: {
		ans: "solo"
		num: 1
	}
	two: {
		ans: "life"
	}
}

elems: other: { ans: "id", num: 23 }

cue export patterns.cue

{
    "elems": {
        "one": {
            "name": "one",
            "ans": "solo",
            "num": 1
        },
        "other": {
            "name": "other",
            "ans": "id",
            "num": 23
        },
        "two": {
            "name": "two",
            "ans": "life",
            "num": 42
        }
    }
}

2021 Hofstadter, Inc