Feel.Runtime.Compare (feel v0.1.0)

Copy Markdown View Source

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/3 says: 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 result null, unless another element is already unequal; then it is false. 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, a NaN: Feel.Runtime.Kind.of/1 is :unknown for 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

equal(a, b)

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"

greater_or_equal(a, b)

@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}

greater_than(a, b)

@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}

less_or_equal(a, b)

@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"

less_than(a, b)

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

not_equal(a, b)

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}

ordering(atom, a, b)

@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}