Equality and ordering: =, !=, <, <=, >, and >= (DMN 1.6, Tables 50
and 53 to 55).
Each function takes the two values and returns an Feel.Runtime.Env.outcome/0.
Equality
Two values are equal only when they are of the same kind (Feel.Runtime.Kind);
"1" = 1 is null, with a type mismatch. null is the exception (Table 50):
null = null is true, and null = x is false for any other value, so =
and != never give null because of a null operand. Within a kind:
- numbers are equal by value, not by scale:
1 = 1.000. - strings are equal when they are the same code points: no case folding, no normalization.
- dates, and the durations of one kind, are equal by value. A years and months duration and a days and time duration are different kinds, so they never compare (D-54).
- times, and dates and times, are equal as
Feel.Runtime.Temporal.compare/3says: by instant when both have a time offset or a zone, and by their local fields when one has none (D-54). A date and a date and time are different kinds. - lists are equal when they have the same length and equal elements, contexts when
they have the same keys and equal values, ranges when they have the same
endpoints, the same inclusion of them, and were written the same way (
(< 10)is not(null..10), D-61). An element that cannot be compared makes the resultnull, unless another element is already unequal; then it isfalse. A list or a context that is the same term as the other, or has the same terms in it, is equal without its elements being compared (D-168), so it is equal if a term in it is not a value of FEEL (an atom, a float, aNaN:Feel.Runtime.Kind.of/1is:unknownfor it) and is the same one on both sides, where that term alone is not equal to itself.
!= is not (=).
Ordering
<, <=, >, and >= are defined for numbers, strings, dates, times, dates and times, and
durations of one kind (Table 55), and give null for everything else. Strings compare by code point,
with the shorter of two strings that start alike first (the specification pads with
U+0000, so trailing U+0000 characters do not count). A null operand of < or > gives
:missing, and one of <= or >= is null with an error, :null_operand, as the TCK flags the one and not
the other (Feel.Runtime.NullOperand, D-103); operands of different kinds, or of a kind that has no order (booleans,
lists, contexts, ranges, functions), give a type mismatch.
This is the specification's sentence that the operators "are defined only for the
datatypes listed in Table 55". Its other sentence, that <= is < or =, still holds
within those types, but it is not applied to the others: null <= null and
true <= true are null, not true. The two readings differ only there, and the TCK
has no test for either (decision D-04, D-103). Reading the formula literally is the
order/5 clause for the unordered kinds.
Functions
Two functions are equal when they are the same function value, and unequal otherwise (notes 05, [E13]; D-82): the specification says "the same parameters, body and scope", and the identity of the closure is that, taken for a function that was written once and bound to a name. Two functions that are written alike in two places are two values, so they are unequal. A function has no order.
Summary
Functions
Tests two values for equality.
Tests whether the first value is greater than or equal to the second.
Tests whether the first value is greater than the second.
Tests whether the first value is less than or equal to the second.
Tests whether the first value is less than the second.
Tests two values for inequality: the negation of equal/2.
The comparison of an IR node (Feel.IR.comparison_operator/0) that is an ordering: :lt, :lte,
:gt or :gte.
Functions
@spec equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
Tests two values for equality.
iex> Feel.Runtime.Compare.equal(Decimal.new("1"), Decimal.new("1.000"))
{:ok, true}
iex> Feel.Runtime.Compare.equal(nil, nil)
{:ok, true}
iex> Feel.Runtime.Compare.equal(Decimal.new(1), nil)
{:ok, false}
iex> {:error, diagnostic} = Feel.Runtime.Compare.equal("1", Decimal.new(1))
iex> diagnostic.message
"cannot compare a string with a number"
@spec greater_or_equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
Tests whether the first value is greater than or equal to the second.
iex> Feel.Runtime.Compare.greater_or_equal(Decimal.new(2), Decimal.new(3))
{:ok, false}
@spec greater_than(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
Tests whether the first value is greater than the second.
iex> Feel.Runtime.Compare.greater_than("b", "a")
{:ok, true}
@spec less_or_equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
Tests whether the first value is less than or equal to the second.
iex> Feel.Runtime.Compare.less_or_equal(Decimal.new(1), Decimal.new(1))
{:ok, true}
iex> {:error, diagnostic} = Feel.Runtime.Compare.less_or_equal(nil, nil)
iex> diagnostic.code
:null_operand
iex> {:error, diagnostic} = Feel.Runtime.Compare.less_or_equal(Decimal.new(1), nil)
iex> diagnostic.message
"an operand of <= is null"
@spec less_than(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
Tests whether the first value is less than the second.
iex> Feel.Runtime.Compare.less_than("a", "ab")
{:ok, true}
iex> Feel.Runtime.Compare.less_than(Decimal.new(1), nil)
:missing
@spec not_equal(Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
Tests two values for inequality: the negation of equal/2.
iex> Feel.Runtime.Compare.not_equal(Decimal.new(1), nil)
{:ok, true}
iex> Feel.Runtime.Compare.not_equal("a", "a")
{:ok, false}
@spec ordering(:lt | :lte | :gt | :gte, Feel.Value.t(), Feel.Value.t()) :: Feel.Runtime.Env.outcome()
The comparison of an IR node (Feel.IR.comparison_operator/0) that is an ordering: :lt, :lte,
:gt or :gte.
iex> Feel.Runtime.Compare.ordering(:lte, "a", "b")
{:ok, true}
iex> Feel.Runtime.Compare.ordering(:gt, "a", "b")
{:ok, false}