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
(< a b) will return true if a < b, and false otherwise.
(= 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 -- symbols compare lexicographically by symbol 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>=. In general, they work
similarly to the primitive operators:
(compare< 1 3) # true (compare> "bar" "foo") # false -- strings compare lexicographically (compare<= :bar :foo) # true -- symbols compare lexicographically by symbol 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
to establish an ordering between janet values. The
compares two values (here called
b), and returns -1, 0, or
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
function is as follows:
(:compare a b)is not nil, then return
(:compare a b)
(:compare b a)is not nil, then return
(- (:compare a b))
- else return
(cond (< a b) -1 (= a b) 0 1)
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.