structcmp

CUE v0.18.0

Surface: per-file @experiment(...) attribute

Enablement

@experiment(structcmp)

Lifecycle

preview since v0.14.0 · stable since v0.15.0

Description

StructCmp enables comparison of structs. This also defines the == operator to be defined on all values. For instance, comparing 1 and "foo" will return false, whereas previously it would return an error.

Specification changes

What this experiment changes in the language specification, section by section. Removed and added text is marked in place.

Support comparing values of different types

merged 2025-09-16 · v0.15.0 · also part of keepvalidators

ValuesIn the specification

5 unchanged paragraphs

In addition to simple values like "hello" and 42.0, CUE has structs. A struct is a map from labels to values, like {a: 42.0, b: "hello"}. Structs are CUE’s only way of building up complex values; lists, which we will see later, are defined in terms of structs.

All possible values are ordered in a lattice, a partial order where every two elements have a single greatest lower bound. A value a is an instance of a value b, denoted a ⊑ b, if b == a or b is more general than a, that is if a orders before b in the partial order (⊑ is not a CUE operator). We also say that b subsumes a in this case. In graphical terms, b is “above” a in the lattice.

At the top of the lattice is the single ancestor of all values, called top, denoted _ in CUE. Every value is an instance of top.

At the bottom of the lattice is the value called bottom, denoted _|_. A bottom value usually indicates an error. Bottom is an instance of every value.

An atom is any value whose only instances are itself and bottom. Examples of atoms are 42.0, "hello", true, and null.

A value is concrete if it is either an atom, or a struct whose field values of regular (non-hidden and non-definition fields) are all concrete, recursively.

2 unchanged blocks

CUE’s values also include what we normally think of as types, like string and float. It does not distinguish between types and values: only the relationship of values in the lattice is important. Each CUE “type” subsumes the concrete values that one would normally think of as part of that type. For example, "hello" is an instance of string, and 42.0 is an instance of float. In addition to string and float, CUE has null, int, bool, and bytes. We informally call these CUE’s “basic types”.

false ⊑ bool
true  ⊑ bool
true  ⊑ true
5.0   ⊑ float
bool  ⊑ _
_|_   ⊑ _
_|_   ⊑ _|_

_     ⋢ _|_
_     ⋢ bool
int   ⋢ bool
bool  ⋢ int
false ⋢ true
true  ⋢ false
float ⋢ 5.0
5     ⋢ 6

Expressions › Operators › String operators › Comparison operatorsIn the specification

Comparison operators compare two concrete operands and yield an untypeda boolean value.

==    equal
!=    not equal
<     less
<=    less or equal
>     greater
>=    greater or equal
=~    matches regular expression
!~    does not match regular expression

In any comparison, the types of the two operands must unify or one of the operands must be null.

In any comparison, both operands must be concrete; otherwise the result is bottom (_|_).

The equality operators == and != apply to operands that are comparable.can be applied to any two concrete operands. The ordering operators <, <=, >, and >= apply only to operands that are ordered.of the same ordered type (numeric, string, or bytes). The matching operators =~ and !~ apply to a string and a regular expression operand. These terms and the result of the comparisons are defined as follows:expression operand.

  • Null is comparable with itself and any other type. Two null values are always equal, null is unequal with anything else.
  • Boolean values are comparable. Two boolean values are equal if they are either both true or both false.
  • Integer values are comparable and ordered, in the usual way.
  • Floating-point values are comparable and ordered, as per the definitions for binary coded decimals in the IEEE-754-2008 standard.
  • Floating-point numbers may be compared with integers; the comparison is performed as if the integer was first converted to a floating-point number.
  • String and bytes values are comparable and ordered lexically byte-wise.
  • Structs are comparable but not ordered. Two structs are equal if they have the same set of regular field labels and the corresponding values are recursively equal. Only regular fields are considered in the comparison; field order and closedness are irrelevant.
  • Lists are comparable but not ordered. Two lists are equal if they have the same length and their corresponding elements are recursively equal.
  • The regular expression syntax is the one accepted by RE2, described in https://github.com/google/re2/wiki/Syntax, except for \C.
  • s =~ r is true if s matches the regular expression r.
  • s !~ r is true if s does not match regular expression r.

For equality comparisons (== and !=):

  • Two values of different basic types are always unequal, except for integers and floating-point numbers (see below).
  • Null values are equal only to other null values.
  • Boolean values are equal if they are both true or both false.
  • Numeric values are equal if they represent the same number. When comparing an integer with a floating-point number, the integer is first converted to floating-point.
  • String values are equal if they contain the same sequence of bytes.
  • Bytes values are equal if they contain the same sequence of bytes.
  • Struct values are equal if they have the same set of regular field labels and the corresponding values are recursively equal. Only regular fields are considered; field order and closedness are irrelevant.
  • List values are equal if they have the same length and their corresponding elements are recursively equal.

For ordering comparisons (<, <=, >, >=):

  • Numeric values are ordered by their numeric value, with integer-to-float conversion as described above.
  • String values are ordered lexically byte-wise.
  • Bytes values are ordered lexically byte-wise.

For pattern matching (=~, !~):

  • The regular expression syntax is that accepted by RE2 (https://github.com/google/re2/wiki/Syntax), except for \C.
  • s =~ r is true if string s matches regular expression r.
  • s !~ r is true if string s does not match regular expression r.
3 < 4       // true
3 < 4.0     // true
null == 2   // false
null != {}  // true
{} == {}    // _|_: structs are not comparable against structs

"Wild cats" =~ "cat"   // true
"Wild cats" !~ "dog"   // true

"foo" =~ "^[a-z]{3}$"  // true
"foo" =~ "^[a-z]{4}$"  // false

Support unary == bound

merged 2025-09-16 · v0.15.0 · also part of keepvalidators

Values › BoundsIn the specification

A bound, syntactically a unary expression, defines a logically infinite disjunction of concrete values represented as a single comparison. For example, >= 2 represents the infinite disjunction 2|3|4|5|6|7|….

For any comparison operator op except ==, op a is the disjunction of every x such that x op a.

2 & >=2 & <=5           // 2, where 2 is either an int or float.
2.5 & >=1 & <=5         // 2.5
2 & >=1.0 & <3.0        // 2.0
2 & >1 & <3.0           // 2.0
2.5 & int & >1 & <5     // _|_
2.5 & float & >1 & <5   // 2.5
int & 2 & >1.0 & <3.0   // _|_
2.5 & >=(int & 1) & <5  // _|_
>=0 & <=7 & >=3 & <=10  // >=3 & <=7
!=null & 1              // 1
>=5 & <=5               // 5
==[1, 2] & [1]          // _|_
!=[1, 2] & [1]          // [1]
Install CUE