Comparison#

You can perform various comparisons, such as comparing numbers or strings, or checking whether a specific element is contained in an array.

Binding of Comparison Operators#

The binding between comparison operators works in a special way, following these steps:

  1. For all comparison operators, perform the comparison between their left and right operands
  2. Take the logical AND of all those results

For example, 3 <= x <= 5 first evaluates 3 <= x, then evaluates x <= 5, and finally takes their logical AND: 3 <= x && x <= 5.

This allows you to write comparison chains concisely.

$ xa '3 <= 4 <= 5'
# TRUE

$ xa '3 <= 10 <= 5'
# FALSE

The following operators follow this specification:

  • > < >= <= == != === !== @ !@ ?= !?=

Comparison Operators#

The comparison operators are as follows:

Operator Meaning
left > right Returns whether left is greater than right
left < right Returns whether left is less than right
left >= right Returns whether left is greater than or equal to right
left <= right Returns whether left is less than or equal to right
$ xa '[1 > 2, 2 > 2, 3 > 2]'
# [FALSE;FALSE;TRUE]

$ xa '[1 < 2, 2 < 2, 3 < 2]'
# [TRUE;FALSE;FALSE]

$ xa '[1 >= 2, 2 >= 2, 3 >= 2]'
# [FALSE;TRUE;TRUE]

$ xa '[1 <= 2, 2 <= 2, 3 <= 2]'
# [TRUE;TRUE;FALSE]

Overriding Comparison Operators#

Comparison operators actually determine their behavior based on the result of the spaceship operator.

When the spaceship operator is overridden, the behavior of the various comparison operators changes accordingly.

Equality Operators#

Equality Operator#

The equality operator left == right returns whether the values on both sides are equal.

$ xa '[1 == 2, 2 == 2, 3 == 2]'
# [FALSE;TRUE;FALSE]

Currently, it is only defined for some types such as numbers and strings.


Numbers of different types are considered unequal even when they represent the same numeric value.

$ xa '[BIG.of(1) == BIG.of(1), BIG.of(1) == 1, BIG.of(1) == 1.0]'
# [TRUE;FALSE;FALSE]

Inequality Operator#

The inequality operator left != right returns whether the values on both sides are not equal.

This is the negation of the equality operator.

$ xa '[1 != 2, 2 != 2, 3 != 2]'
# [TRUE;FALSE;TRUE]

Strict Equality Operator#

The strict equality operator left === right returns whether the values of both sides are strictly equal.

When the types of the two sides differ, they are always judged to be not strictly equal.

When the types are the same, whether they are strictly equal is judged per type, as shown in the following table.

Type Name Comparison
INT Integer Value contents
DOUBLE Float Value contents
BIG Arbitrary-precision integer Value contents
STRING String Value contents
BOOLEAN Boolean Value contents
NULL Null Value contents
REGEX Regular expression Value contents
OBJECT Object Instance identity
ARRAY Array Instance identity
BLOB Blob Instance identity
STREAM Stream Instance identity
PROMISE Promise Instance identity
FUNCTION Function Instance identity
ERROR Error Instance identity
$ xa -q '
  OUT << 1 === 1
  OUT << 1 === 1.0
  OUT << 1 === 2
'
# TRUE
# FALSE
# FALSE

$ xa -q '
  OUT << (
    a := [1, 2]
    a === a
  )
'
# TRUE

For REGEX, equality requires equal pattern strings and equal flag strings.

The strict equality operator cannot be overridden.


For DOUBLE values, two NaN values are considered equal, while positive zero and negative zero are considered unequal.

$ xa '[NAN === DOUBLE.of("NaN"), 0.0 === -(0.0)]'
# [TRUE;FALSE]

Strict Inequality Operator#

The strict inequality operator left !== right is the negation of the strict equality operator left === right.

$ xa -q '
  OUT << 1 !== 1
  OUT << 1 !== 1.0
  OUT << 1 !== 2
'
# FALSE
# TRUE
# TRUE

Containment Operators#

Containment Operator#

The containment operator left @ right returns whether left is contained in right.

The behavior for each right-hand type is as follows:

Right Type Return Value
String Whether the left string is contained in the right string
Array Whether the left element is contained in the right array
Object Whether the left key is contained in the right object
$ xa '"bcd" @ "abcde"'
# TRUE

$ xa '"123" @ "abcde"'
# FALSE

$ xa '1 @ [1, 2, 3]'
# TRUE

$ xa '4 @ [1, 2, 3]'
# FALSE

$ xa '"a" @ {a: 1; b: 2; c: 3}'
# TRUE

$ xa '"d" @ {a: 1; b: 2; c: 3}'
# FALSE

The behavior of the containment operator is inconsistent: strings are substrings, arrays are elements, and objects are keys.

Negative Containment Operator#

The negative containment operator left !@ right returns whether left is not contained in right.

It behaves equivalently to the expression !(left @ right) using the containment operator.

$ xa '"bcd" !@ "abcde"'
# FALSE

$ xa '"123" !@ "abcde"'
# TRUE

Overriding the Containment Operator#

The containment operator is actually an operator that calls the _@_ method of the right-hand value and returns its boolean conversion.

$ xa -q '
  Basket := {
    `_@_`: this, item -> item @ this.items
  }

  basket := Basket{items: ["apple", "orange", "banana"]}

  OUT << "Basket: $basket"
  "apple" @ basket && (OUT << "apple is in basket")
  "cherry" @ basket && (OUT << "cherry is in basket")
'
# Basket: {items:[apple;orange;banana]}
# apple is in basket

InstanceOf Operators#

InstanceOf Operator#

The instanceOf operator left ?= right returns whether left is an instance of right.

More precisely, it determines whether object left is the same instance as right, or whether right exists somewhere in left’s inheritance chain.

$ xa -q '
  Animal := {}
  Human := Animal {}
  socrates := Human{}
  pythagoras := Human{}

  Animal ?= Animal && (OUT << "Animal is Animal")
  Human ?= Human && (OUT << "Human is Human")
  socrates ?= socrates && (OUT << "Socrates is Socrates")
  pythagoras ?= pythagoras && (OUT << "Pythagoras is Pythagoras")

  Human ?= Animal && (OUT << "Human is Animal")
  Animal ?= Human && (OUT << "Animal is Human")

  socrates ?= Human && (OUT << "Socrates is Human")
  Human ?= socrates && (OUT << "Human is Socrates")

  socrates ?= Animal && (OUT << "Socrates is Animal")
  Animal ?= socrates && (OUT << "Animal is Socrates")

  socrates ?= pythagoras && (OUT << "Socrates is Pythagoras")
  pythagoras ?= socrates && (OUT << "Pythagoras is Socrates")
'
# Animal is Animal
# Human is Human
# Socrates is Socrates
# Pythagoras is Pythagoras
# Human is Animal
# Socrates is Human
# Socrates is Animal

Negative InstanceOf Operator#

The negative instanceOf operator left !?= right returns whether left is not an instance of right.

It behaves equivalently to the expression !(left ?= right) using the instanceOf operator.

$ xa -q '
  Animal := {}
  Human := Animal {}
  Dog := Animal {}
  socrates := Human{}

  socrates !?= Animal && (OUT << "Socrates is not Animal")
  socrates !?= Human && (OUT << "Socrates is not Human")
  socrates !?= Dog && (OUT << "Socrates is not Dog")
'
# Socrates is not Dog

Spaceship Operator#

The spaceship operator <=> is an operator that returns the magnitude relationship between the left and right sides.

The relationship between the left-right magnitude relationship and the return value of the spaceship operator is as follows:

Condition Return Value
Left < Right -1
Left = Right 0
Left > Right 1
$ xa '1 <=> 2'
# -1

$ xa '2 <=> 2'
# 0

$ xa '3 <=> 2'
# 1

In numeric ordering comparisons, NaN compares greater than every other number, including positive infinity.

The spaceship operator returns 0 when comparing two NaN values.

$ xa 'DOUBLE.of("NaN") <=> DOUBLE.of("Infinity")'
# 1

$ xa 'DOUBLE.of("NaN") <=> DOUBLE.of("NaN")'
# 0

$ xa 'DOUBLE.of("NaN") > BIG.of(0)'
# TRUE

When comparing strings, they are determined in lexicographic order.

$ xa ' "a" <=> "b" '
# -1

$ xa ' "aa" <=> "a" '
# 1

Overriding the Spaceship Operator#

The spaceship operator is actually an operator that calls the _<=>_ method of the left-hand value and returns its return value.

$ xa '
  Person := {
    `_<=>_`: this, other -> this.age <=> other.age
  }

  alice := Person{age: 30}
  bob   := Person{age: 20}

  alice <=> bob
'
# 1

Use in SORT Function and Others#

The spaceship operator can be used as a comparison function in functions like SORT.

The following example sorts by string length.

$ xa '
  (
    "apple",
    "banana",
    "cherry",
    "durian",
    "elderberry",
    "fig",
    "grape",
  ) >> SORT[a, b -> $#a <=> $#b]
'
# fig
# apple
# grape
# banana
# cherry
# durian
# elderberry

Comparison Functions#

CONTAINS / ::CONTAINS: Function for Containment#

CONTAINS(container: VALUE; content: VALUE): BOOLEAN

VALUE::CONTAINS(content: VALUE): BOOLEAN

The CONTAINS function and the ::CONTAINS extension function are syntactic sugar for calling the containment operator @ in function form and extension function form.

CONTAINS(container; content), container::CONTAINS(content), and the infix function call container CONTAINS content are equivalent to content @ container.

$ xa ' CONTAINS("abcde"; "bcd") '
# TRUE

$ xa ' "abcde"::CONTAINS("bcd") '
# TRUE

$ xa ' "abcde" CONTAINS "bcd" '
# TRUE