View Source CompareChain (compare_chain v0.6.0)
Convenience macros for doing comparisons
Valid expressions
Valid expressions for compare?/1
and compare?/2
follow these three rules:
1. A comparison operator like <
must be present.
At least one of these must be included: <
, >
, <=
, >=
, ==
, !=
,
===
, or !===
. So this is valid:
compare?(1 < 2 < 3)
but this is not:
compare?(true)
because it does not contain any comparisons.
2. All arguments to boolean operators must also be valid expressions.
The boolean operators and
, or
, and not
are allowed in expressions so
long as all of their arguments (eventually) contain a comparison. So this is
valid:
compare?(1 < 2 < 3 and 4 < 5)
as is this:
compare?(not (not 1 < 2 < 3))
but this is not:
compare?(1 < 2 < 3 and true)
because the right side of and
fails to contain a comparison. This
expression can be refactored to be valid by moving the non-comparison branch
outside compare?/1
like so:
compare?(1 < 2 < 3) and true
3. The root operator of an expression must be a comparison or a boolean.
So this is not valid:
compare?(my_function(a < b), Date)
because its root operator is my_function/1
. This expression can be
refactored to be valid by moving compare?/2
inside my_function/1
like so:
my_function(compare?(a < b, Date))
We restrict expressions in this fashion so we can guarantee that compare?/1
and compare?/2
will always return a boolean.
Also note that arguments to comparisons may be arbitrarily complicated:
compare?(a < Date.utc_today(), Date)
Summary
Functions
Macro that performs chained comparison with operators like <
.
Macro that performs chained, semantic comparison with operators like <
by
rewriting the expression using the compare/2
function defined by the
provided module.
Functions
Macro that performs chained comparison with operators like <
.
You may also include the boolean operators and
, or
, and not
in the
expression so long as all their arguments all (eventually) contain
comparisons. See the moduledoc for more details.
For a version that also does semantic comparison, see: compare?/2
.
Examples
Chained comparison:
iex> import CompareChain
iex> compare?(1 < 2 < 3)
true
Comparisons joined by logical operators:
iex> import CompareChain
iex> compare?(1 >= 2 >= 3 or 4 >= 5 >= 6)
false
Warnings and errors
Comparing structs will warn
Expressions which compare matching structs like:
iex> compare?(~D[2017-03-31] < ~D[2017-04-01])
false
Will result in a warning:
... [warning] Performing structural comparison on matching structs.
Did you mean to use `compare?/2`?
compare?(~D[2017-03-31] < ~D[2017-04-01], Date)
You probably want to use compare?/2
, which does semantic comparison,
instead.
Invalid expressions will raise
See the section on valid expressions in the moduledoc for details.
Macro that performs chained, semantic comparison with operators like <
by
rewriting the expression using the compare/2
function defined by the
provided module.
This is like how you can provide a module as the second argument to
Enum.sort/2
when you need to sort items semantically.
You may also include the boolean operators and
, or
, and not
in the
expression so long as all their arguments all (eventually) contain
comparisons. See the moduledoc for more details.
For a version that does chained comparison using the normal <
operators,
see: compare?/1
.
Examples
Semantic comparison:
iex> import CompareChain
iex> a = ~D[2017-03-31]
iex> b = ~D[2017-04-01]
iex> compare?(a < b, Date)
true
Semantic vs. Structural Comparison Differences
In the above example, compare?(a < b, Date)
evaluates to true
. On its
own, a < b
evaluates to false
(with a warning). This is why it's so
important to not use comparison operators on structs directly. The answer
is not what you would expect.
Trivia! If you're curious, b
comes before a
because in term ordering,
maps of equal size are compared key by key in ascending order. In this case,
:day
is the first key (due to ASCII byte ordering) where a
and b
differ. Since a.day == 31
and b.day == 1
, we have b < a
.
Chained, semantic comparison:
iex> import CompareChain
iex> a = ~D[2017-03-31]
iex> b = ~D[2017-04-01]
iex> c = ~D[2017-04-02]
iex> compare?(a < b < c, Date)
true
Comparisons joined by logical operators:
iex> import CompareChain
iex> a = ~T[15:00:00]
iex> b = ~T[16:00:00]
iex> c = ~T[17:00:00]
iex> compare?(a < b and b > c, Time)
false
More complex expressions:
iex> import CompareChain
iex> compare?(%{a: ~T[16:00:00]}.a <= ~T[17:00:00], Time)
true
Custom module:
iex> import CompareChain
iex> defmodule AlwaysGreaterThan do
iex> def compare(_left, _right), do: :gt
iex> end
iex> compare?(1 > 2 > 3, AlwaysGreaterThan)
true
Warnings and errors
Using the "strict" operators will warn
Expressions which include either ===
or !==
like:
iex> compare?(~D[2017-03-31] !== ~D[2017-04-01], Date)
true
Will result in a warning:
... [warning] Performing semantic comparison using either: `===` or `!===`.
This is reinterpreted as `==` or `!=`, respectively.
These operators have no additional meaning over ==
and !=
when doing
semantic comparison.
Invalid expressions will raise
See the section on valid expressions in the moduledoc for details.