Janet 1.42.0-dev-7fd75f3 Documentation
(Other Versions: 1.42.0 1.41.2 1.41.1 1.40.1 1.40.0 1.39.1 1.38.0 1.37.1 1.36.0 1.35.0 1.34.0 1.31.0 1.29.1 1.28.0 1.27.0 1.26.0 1.25.1 1.24.0 1.23.0 1.22.0 1.21.0 1.20.0 1.19.0 1.18.1 1.17.1 1.16.1 1.15.0 1.13.1 1.12.2 1.11.1 1.10.1 1.9.1 1.8.1 1.7.0 1.6.0 1.5.1 1.5.0 1.4.0 1.3.1 )

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

# strings compare lexicographically
(> "bar" "foo") # -> false

# keywords compare lexicographically by keyword name
(<= :bar :foo) # -> true

# nil always equals itself and only itself
(= nil nil) # -> true

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 in at least two ways.

First, 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.

# evaluates to false
(= (int/s64 1) (int/u64 1)) # -> false

# evaluates to true but this is due to the Janet internal type number
# for int/u64 types being greater than the internal type number for
# numbers!
(< 3 (int/u64 2)) # -> true

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.

Secondly, = does not compare the contents of buffers, arrays, tables, fibers, and some abstract types. Instead, it checks if the two values are the same object.

# even though these have the same content they are NOT considered =
(= @"abc" @"abc") # -> false
(= @{:a 1} @{:a 1}) # -> false
(= @[:a :b] @[:a :b]) # -> false

# = returns true for the same value
(def x @{:a 1})
(= x x) # -> true

# = works as expected on immutable values
(= nil nil) # -> true
(= true true) # -> true
(= 1 1) # -> true
(= 'sym 'sym) # -> true
(= :kwd :kwd) # -> true
(= "abc" "abc") # -> true
(= [:a :b] [:a :b]) # -> true
(= {:a 1} {:a 1}) # -> true

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

# strings compare lexicographically
(compare> "bar" "foo") # -> false

# keywords compare lexicographically by keyword name
(compare<= :bar :foo) # -> true

# nil always equals itself and only itself
(compare= nil nil) # -> true

# just like <
(compare< 1 2 3) # -> true

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

# they are "semantically" equal
(compare= (int/s64 1) (int/u64 1)) # -> true

# semantically 3 is not < 2
(compare< 3 (int/u64 2)) # -> false

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.

Deep Equality Operator

To compare the contents of mutable values, use the deep= function. This function compares buffers like strings, and recursively compares arrays and tables.

(deep= [:a :b] [:a :b]) # -> true
(deep= {:a 1} {:a 1}) # -> true
(deep= "abc" "abc") # -> true

(deep= @{:x @[@{:a 1} 3 @"hi"] :y 2}
       @{:x @[@{:a 1} 3 @"hi"] :y 2}) # -> true

# deep= uses = for immutable values
(deep= @{:x (int/s64 1)}
       @{:x (int/u64 1)}) # -> false