Janet 1.15.0-2795e8a Documentation

Comparison Operators

Comparison operators are used for comparing values in Janet, in order to establish equality or ordering. Janet has two types of comparison operators, which we refer to as "primitive" comparison operators, and "polymorphic" comparison operators, respectively.

Primitive Comparison Operators

The primitive comparison operators are =, <, <=, >, >=. In the simple case, each operator can be used to compare two values as follows: (< a b) will return true if a < b, and false otherwise. Similarly (= a b) will be true only if a == b.

(= 3 3)          # true
(< 1 3)          # true
(>= :a :a)       # true
(> "bar" "foo")  # false -- strings compare lexicographically
(<= :bar :foo)   # true -- keywords compare lexicographically by keyword name
(= nil nil)      # true -- nil always equal to itself and only itself

More generally, each of these operators can take any number of arguments (from 0) and will return true if the arguments do not violate the ordering implied by the operator. So (< 1 2 3) returns true but (> 3 2 4) returns false. As an extreme, (<) returns true since its (nonexistent) arguments do not violate the ordering.

(= 1 1 1)  # true
(< 1 3 5)  # true
(>= 3 1 7) # false
(> 1)      # true

The primitive comparison operators provide a total ordering for all Janet types, but importantly, these operators compare values of different types in a way that the user might find surprising. If two arguments of a primitive comparison are of different types, they will be ordered by Janet's internal type number. This is not necessarily what the user needs, for example, when comparing Janet int/s64 types to Janet numbers.

# surprisingly evaluates to false:
(= (int/s64 1) (int/u64 1))
# surprisingly evaluates to true but this due to Janet internal type number
# for int/u64 types being greater than the internal type number for numbers!
(< 3 (int/u64 2))

If you require comparison between types to be ordered by something other than type number (e.g. "numeric value") then, use the polymorphic comparison operators, described below.

Polymorphic Comparison Operators

The polymorphic comparison operators are used for comparing different types in some manner rather than just by internal Janet type number. The semantics are determined by the types involved. The purpose of these operators is to allow comparison to work in some "less surprising" way (relative to the primitive comparison operators) when two types have a natural ordering between them.

The polymorphic comparison operators are compare=, compare<, compare<=, compare>, compare>=. In general, they work similarly to the primitive operators:

(compare< 1 3)          # true
(compare> "bar" "foo")  # false -- strings compare lexicographically
(compare<= :bar :foo)   # true -- keywords compare lexicographically by keyword name
(compare= nil nil)      # true -- nil always equal to itself and only itself
(compare< 1 2 3)        # true -- just like <

However, when comparing between int/s64, int/u64, and number types, these operators will "do the right thing".

(compare= (int/s64 1) (int/u64 1))  # true -- they are "semantically" equal
(compare< 3 (int/u64 2))            # false -- semantically 3 is not < 2

In general the polymorphic operators are slower than the primitive ones, so use the primitive ones unless you need the extra polymorphic features.

Implementing Polymorphic Comparison

If you just want to use the polymorphic comparison for the built in types, you can skip this section, which is about how to implement polymorphic comparison for your own types.

The polymorphic comparison operators all use a function called compare to establish an ordering between janet values. The compare function compares two values (here called a and b), and returns -1, 0, or 1 for a < b, a = b, a > b respectively. This result is used by the comparison operator, like compare<, to return false or true. (The comparison operators are extended to work for multiple arguments using multiple calls to compare). The algorithm for the compare function is as follows:

Since compare defers to the primitive operators as a last resort, the polymorphic comparison operators produce a total ordering of Janet types.

The compare method on an abstract type can be implemented in C, and the compare method for a table-based "object" can be implemented in Janet. For more information on the latter see the object oriented programming section and the prototypes section for more information on developing object oriented methods on tables.