# Why is there no isless(::Int64, ::UnitRange{Int64})?

It seems to me that all of these are unambiguous:

``````1 < 4:5 == true
2 ≤ 2:5 == true
3 ≤ 2:5 == true
5 ≤ 2:5 == true
6 ≤ 2:5 == false
6 > 2:5 == true
4 > 2:5 == false
``````

That is, for any `Int`, we can derive an unambiguous and unsurprising answer for `<`, `≤`, `≥`, and `>` of a given `UnitRange{Int}`.

Is this just an oversight, or is there something about my take on what these comparisons should mean which doesn’t work?

What about `4 < 4:3`?

I think it’s generally better to not have default comparisons between scalars and collections

14 Likes

I think all of these are ambiguous. What does it even mean for a number to be ‘bigger’ than a collection? `2:5` has four members, how can it be smaller than a single number? If you compare the largest members, I suppose, but I don’t find that obvious.

What about `3 < 2:4`? Intuitively, to me, it’s neither smaller, bigger or equal.

2 Likes

I’ve never seen people write down 1 < [2, 3] in math classes. This kind of comparison neither exists as a common convention among programming languages nor in the common math notation.

2 Likes

A range is a collection, yes, but it’s also an interval.

`false`, clearly. `3` is not smaller than the interval `2:4`.

On the other hand, `3 ≤ 2:4` is correct.

`n == a:b` is false for all `n,a,b`, because a number and an interval aren’t ever equal.

That’s weird, because inequalities can be expressed as intervals, so `[2,5]` is a way of representing `2 ≤ x ≤ 5`. So check this out:

``````julia> 2 ≤ 1 ≤ 5
false

julia> 2 ≤ 3 ≤ 5
true

julia> 2 ≤ 6 ≤ 5
false
``````

So what’s wrong with:

``````1 < 2:5 == false
2 ≤ 2:5 ==  true
``````

Exactly?

If we want to spin the thread of “what happens when we define this” further, what should `4 < 1:2:7` be? Clearly, 4 isn’t even in that range, so should it even be possible to compare these? What about floating point ranges?

For the vast majority of intervals, asking whether the entirety of the range is less/greater than a given value doesn’t really help with establishing an ordering between the two, since they are entirely different objects. You could use it as a shorthand for whether all of the numbers in the range are less/greater than the given number, but that feels much more like a `mapreduce` operation to me than trying to establish an order between integers and ranges of integers, so if it exists, should have a different name than `<`.

6 Likes

If it isn’t smaller, and it isn’t bigger, I guess `3 == 2:4` must be `true`, then?

IMO, if I write `3 < (2:4)`, I’ve made a mistake, and the desired result is an error. Otherwise, I would be very disappointed.

6 Likes

I missed this. I disagree that it’s an interval, it’s purely a discrete collection. If it were an interval then `2.5 in (1:5)` should be true.

5 Likes

Nope! The set of scalars and intervals has a strict partial order, where equality is replaced with an equivalence relation, conventionally denoted `~`. The equivalence relation is `∈`.

So, in pursuit of notational orthodoxy, we might want to define `<` and `≲` where:

``````≲(i::Integer, r::UnitRange{<:Integer}) = i < r || i ∈ r
``````

But I wouldn’t call it an outright abuse of notation to spell that `≤`, this is a programming language, not a chalkboard. It would be weird if `<` gave an answer but `<=/≤` threw an error harassing you to type `\lesssim` instead.

So, then we would have

``````2 < 2:5 ==  false
2 == 2:5 ==  false
2 ≤ 2:5 ==  true
``````

?

1 Like

It’s an interval in the Integers, intervals of discrete sets are a valid concept. I agree that `2.5 ∈ 1:5` should be `false`, but don’t see the relevance to the question at hand.

`[1,2,3]` is not an interval and neither is `1:3`.

You equated greater/smaller comparison to a membership test in an interval a few posts up.

If `1 <= 2.5 <= 5` is true, then `2.5 ∈ 1:5` should also be.

Yes. Not all orderings are total. What we would have is:

``````i < a:b → ¬(i ∈ a:b) ∧ ¬(i > a:b)
i ≤ a:b → i < a:b ∨ ∈ a:b
∀i,a,b ∈ ℤ, i ≠ a:b
``````

Seems reasonable to me.

Ok, call 1:3 “the set of integers in the interval [1,3]”. We’ll call that range.

So obviously, `2.5 ∈ 1:3` is `false` and `2 ∈ 1:3` is `true`. Nothing else changes.

`x <= y` means that either `x < y` or `x == y`.

I only have a MSc in mathematics, so I am struggling to follow your arguments. The definitions you are proposing on common, everyday types are deeply puzzling and counterintuitive, and would not allow me to catch obvious bugs.

1 Like

Was the bit about `≾` really so unclear? Not every ordering is total, antisymmetry is not a requirement to have an ordering relation!

It gets its own notation in mathematics, because a) mathematicians do like to have a symbol for every possible variation of everything and b) sometimes one might deploy both sorts of orderings on a single blackboard, but this is a poor argument against overloading `≤` in this particular case. If someone were giving a lecture and had established that the set of interest has a strict partial order, and then wrote `κ ≤ φ` or whatever, would you correct them?

Why not just define your own operator? The following uses `\lessdot`.

``````julia> ⋖(x::Number, y::UnitRange) = x < first(y) && x < last(y)
⋖ (generic function with 1 method)

julia> 0 ⋖ 1:5
true
``````
6 Likes

Or maybe:

``````julia> struct Interval{T <: Number}
a::T
b::T
end

julia> a..b = Interval(promote(a, b)...)
.. (generic function with 1 method)

julia> Base.isless(n::Number, it::Interval) = n < it.a && n < it.b

julia> 2 < 3..4
true

julia> 2 < 2..4
false
``````
1 Like

I’ve no objection, and would probably use `≾` just out of orthodoxy. But here’s the documentation from `isless`, it’s expected to uphold the following two relations:

If isless(x, y) is defined, then so is isless(y, x) and isequal(x, y), and exactly one of those three yields true.

``````3 < 4:5 == true
3 > 4:5 == false
3 == 4:5 == false
``````

The relation defined by isless is transitive, i.e., isless(x, y) && isless(y, z) implies isless(x, z).

``````2 < 3:5 == true
3:5 < 6 == true
2 < 6 == true
``````

So no problem there. All that the documentation says about `≤` is this:

Less-than-or-equals comparison operator. Falls back to (x < y) | (x == y).

What it does not say is that antisymmetry and total ordering has to hold for this operator! It could! It just says “in the event that `≤` isn’t defined, it will be treated as though the set of the type is totally ordered”.

So it seems like a reasonable candidate for `≤/<=`, which are both much less ponderous to enter than `\lesssim` or `\lessdot`, and would allow for easier generic code.

For example, Intervals.jl does this for you.

6 Likes