Comparison Operators

OperatorDescription
(x operator y)
x, y same type or are numericx, y different types
==x is equals to yerror if not definedfalse if not defined
!=x is not equals to yerror if not definedtrue if not defined
>x is greater than yerror if not definedfalse if not defined
>=x is greater than or equals to yerror if not definedfalse if not defined
<x is less than yerror if not definedfalse if not defined
<=x is less than or equals to yerror if not definedfalse if not defined

Comparison operators between most values of the same type are built in for all standard types.

Floating-point numbers interoperate with integers

Comparing a floating-point number with an integer is also supported.

42 == 42.0;         // true

42.0 == 42;         // true

42.0 > 42;          // false

42 >= 42.0;         // true

42.0 < 42;          // false

Decimal numbers interoperate with integers

Comparing a decimal number with an integer is also supported.

let d = parse_decimal("42");

42 == d;            // true

d == 42;            // true

d > 42;             // false

42 >= d;            // true

d < 42;             // false

Strings interoperate with characters

Comparing a string with a character is also supported, with the character first turned into a string before performing the comparison.

'x' == "x";         // true

"" < 'a';           // true

'x' > "hello";      // false

Comparing different types defaults to false

Comparing two values of different data types defaults to false unless the appropriate operator functions have been registered.

The exception is != (not equals) which defaults to true. This is in line with intuition.

42 > "42";          // false: i64 cannot be compared with string

42 <= "42";         // false: i64 cannot be compared with string

let ts = new_ts();  // custom type

ts == 42;           // false: different types cannot be compared

ts != 42;           // true: different types cannot be compared

ts == ts;           // error: '==' not defined for the custom type

Safety valve: Comparing different numeric types has no default

Beware that the above default does NOT apply to numeric values of different types (e.g. comparison between i64 and u16, i32 and f64) – when multiple numeric types are used it is too easy to mess up and for subtle errors to creep in.

// Assume variable 'x' = 42_u16, 'y' = 42_u16 (both types of u16)

x == y;             // true: '==' operator for u16 is built-in

x == "hello";       // false: different non-numeric operand types default to false

x == 42;            // error: ==(u16, i64) not defined, no default for numeric types

42 == y;            // error: ==(i64, u16) not defined, no default for numeric types

Boolean Operators

Note

All boolean operators are built in for the bool data type.

OperatorDescriptionArityShort-circuits?
! (prefix)NOTunaryno
&&ANDbinaryyes
&ANDbinaryno
||ORbinaryyes
|ORbinaryno

Double boolean operators && and || short-circuit – meaning that the second operand will not be evaluated if the first one already proves the condition wrong.

Single boolean operators & and | always evaluate both operands.

a() || b();         // b() is not evaluated if a() is true

a() && b();         // b() is not evaluated if a() is false

a() | b();          // both a() and b() are evaluated

a() & b();          // both a() and b() are evaluated

Null-Coalescing Operator

OperatorDescriptionArityShort-circuits?
??Null-coalescebinaryyes

The null-coalescing operator (??) returns the first operand if it is not (), or the second operand if the first operand is ().

This operator short-circuits – meaning that the second operand will not be evaluated if the first operand is not ().

a ?? b              // returns 'a' if it is not (), otherwise 'b'

a() ?? b();         // b() is only evaluated if a() is ()

Tip: Default value for object map property

Use the null-coalescing operator to implement default values for non-existent object map properties.

let map = #{ foo: 42 };

// Regular property access
let x = map.foo;            // x == 42

// Non-existent property
let x = map.bar;            // x == ()

// Default value for property
let x = map.bar ?? 42;      // x == 42

Short-circuit loops and early returns

The following statements are allowed to follow the null-coalescing operator:

This means that you can use the null-coalescing operator to short-circuit loops and/or early-return from functions when the value tested is ().

let total = 0;

for value in list {
    // Whenever 'calculate' returns '()', the loop stops
    total += calculate(value) ?? break;
}

In Operator

Trivia

The in operator is simply syntactic sugar for a call to the contains function.

Similarly, !in is a call to !contains.

The in operator is used to check for containment – i.e. whether a particular collection data type contains a particular item.

Similarly, !in is used to check for non-existence – i.e. it is true if a particular collection data type does not contain a particular item.

42 in array;

array.contains(42);     // <- the above is equivalent to this

123 !in array;

!array.contains(123);   // <- the above is equivalent to this

Built-in support for standard data types

Data typeCheck for
Numeric rangeinteger number
Arraycontained item
Object mapproperty name
Stringsub-string or character

Examples

let array = [1, "abc", 42, ()];

42 in array == true;                // check array for item

let map = #{
    foo: 42,
    bar: true,
    baz: "hello"
};

"foo" in map == true;               // check object map for property name

'w' in "hello, world!" == true;     // check string for character

'w' !in "hello, world!" == false;

"wor" in "hello, world" == true;    // check string for sub-string

42 in -100..100 == true;            // check range for number