CompareChain

Description

Provides a macros for:

Examples

iex> import CompareChain
iex> compare?(1 < 2 < 3)
true
iex> compare?(1 > 2 or 3 < 4)
true
iex> compare?(1 > 2 and 3 < 4)
false
iex> compare?(~D[2017-03-31] < ~D[2017-04-01], Date)
true
iex> compare?(~D[2017-03-31] < ~D[2017-04-01] <= ~D[2017-04-02], Date)
true
iex> compare?(~T[16:00:00] < ~T[16:00:00] and ~T[17:00:00] >= ~T[17:00:00], Time)
false

Installation

If available in Hex, the package can be installed by adding compare_chain to your list of dependencies in mix.exs:

def deps do
[
{:compare_chain, "~> 0.1.0"}
]
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/compare_chain.

Background and motivation

Many languages provide syntactic sugar for chained comparisons. For example in Python, a < b < c would be evaluated as (a < b) and (b < c).

Elixir does not provide this. Instead, a < b < c is evaluated as (a < b) < c. Since a < b is a boolean, that's probably not what you want.

Further, operators like < do structural comparison instead of semantic comparison. For most situations, you probably want to use compare/2. From the docs:

Show/Hide

The comparison functions in this module perform structural comparison. This means structures are compared based on their representation and not on their semantic value. This is specially important for functions that are meant to provide ordering, such as >/2, </2, >=/2, <=/2, min/2, and max/2. For example:

~D[2017-03-31] > ~D[2017-04-01]

will return true because structural comparison compares the :day field before :month or :year. Therefore, when comparing structs, you often use the compare/2 function made available by the structs modules themselves:

iex> Date.compare(~D[2017-03-31], ~D[2017-04-01])
:lt

The compare/2 approach works well in many situations, but even moderately complicated logic can be cumbersome. If we wanted the native equivalent of:

iex> compare?(~D[2017-03-31] <= ~D[2017-04-01] < ~D[2017-04-02], Date)

we'd have to write:

iex> Date.compare(~D[2017-03-31], ~D[2017-04-01]) != :gt and Date.compare(~D[2017-04-01]), ~D[2017-04-02]) == :lt

The goal of both compare?/1 and compare?/2 is to provide the syntactic sugar for chained comparisons. With compare?/2, there is the added benefit of being able to use the structural comparison operators for semantic comparison.