Is there any built-in comparison function on two numbers / strings / booleans that returns :lt
, :eq
or :gt
?
No, only indirectly here: myers
Also Date and Time compare return those: Date — Elixir v1.14.0
Also Version.compare/2
and Logger.compare_levels/2
.
I think such atoms are not needed for types that can be compared directly using term order.
When both data (only integers, strings, booleans, and datetime) and comparison rules are given by end-users, I think it would be convenient to have a consistent API.
can be easily defined and refined by the end user, e.g
def compare(a, b) do
cond do
a < b -> :lt
a > b -> :gt
true -> :eq
end
end
too much niche depending on the implementation and use case, some may argue that the function needs to be in the view of the second argument not first, some may say that it needs to return -1, 0, 1
I know I can define that. Actually, that’s exactly what I did. I’m facing 3 conventions now:
-
DateTime.compare/2
returns:lt
,:eq
and:gt
. -
Timex.compare/2
returns -1, 0, 1. - for data types other than date and time, there’s no
compare/2
to use. I have to use>
,<
,==
, etc. and work on booleans.
It would be better that Elixir standard lib provides a unified API.
defprotocol Comparable do
def compare(a, b)
end
defimpl Comparable,
for: [Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference] do
def compare(a, b) when a < b, do: :lt
def compare(a, b) when a == b, do: :eq
def compare(a, b) when a > b, do: :gt
end
Usage:
iex(1)> Comparable.compare(1, 2)
:lt
iex(2)> Comparable.compare("z", "a")
:gt
iex(3)> Comparable.compare([{:foo, "bar"}], foo: "bar")
:eq
To have everything:
defimpl Comparable,
for: [Date, Time, NaiveDateTime, DateTime] do
defdelegate compare(a, b), to: @for
end
defimpl Comparable, for: [Timex] do
# TODO
end
Comparable.compare(1, 2)
Comparable.compare(~D[2016-04-16], ~D[2016-04-28])
Comparable.compare(~T[16:04:16], ~T[16:04:28])
Comparable.compare(~N[2016-04-16 13:30:15], ~N[2016-04-28 16:19:25])
Or: defdelegate compare(a, b), to: @for
yes better, changed it.
I’d change the module to Ord
and the function to cmp
– I really like Rust’s API for it that much – but otherwise: excellent job! Hasn’t occurred to me to use protocols at all, I kind of avoid them like the plague.
Pretty elegant and nice, really like it.
Elixir didn’t introduce a protocol, but it already kinda standardized compare/2
to return :lt | :eq | :gt
for passing a module to sorting functions of Enum
with elixir 1.10: Enum — Elixir v1.14.0
Decimal
had to do a mayor version update for the breaking change moving from -1 | 0 | 1
to :lt | :eq | :gt
. It seems like Timex
didn’t do so.