Feature #18749
closedStrangeness of endless inclusive ranges
Description
I came to think about this while looking at the pull request linked in #18748.
Currently, an endless inclusive range covers the corresponding endless exclusive range, but not vice versa:
("a"..nil).cover?("a"...nil) #=> true
("a"...nil).cover?("a"..nil) #=> false
(nil..nil).cover?(nil...nil) #=> true
(nil...nil).cover?(nil..nil) #=> false
This looks strange to me. There is not a single element covered by an endless inclusive range that is not covered by the corresponding endless exclusive range. This should mean that there is no difference in coverage between an endless inclusive range and the corresponding endless exclusive range.
However, actually, an interval in mathematics (which I think is the counterpart to Ruby's range) ending in ∞ (which I think is the counterpart to an endless range) is always an open interval (which I think is the counterpart to an exclusive range), and never a closed interval (which I think is the counterpart to an inclusive range).
[a, ∞) is correct.
[a, ∞] is wrong.
From analogy, ideally, endless inclusive ranges should be prohibited in the first place. But that would cause new issues: There is no inclusive-exclusive distinction on the begin side of a range, and that is actually always assumed to be inclusive. Since we have beginless (inclusive) ranges, prohibiting endless inclusive ranges would cause asymmetry.
So what I can think of are the following possibilities (ordered from conservative to radical):
A. Endless inclusive ranges are allowed as is. An endless inclusive range and the corresponding endless exclusive range cover each other.
B. Endless inclusive ranges are disallowed. Beginless (inclusive) ranges are allowed as is.
C. New syntax is introduced in order to describe ranges that are exclusive on the begin side. Inclusive-exclusive distinction can be described on both begin and end sides independently. Endless inclusive ranges and beginless inclusive ranges are disallowed.
Updated by janosch-x (Janosch Müller) over 2 years ago
Maybe a simple, non-breaking solution would be to make endless Ranges exclude the end regardless of how they are written?
1..nil # => 1...
1...nil # => 1...
# as a result:
1..nil == 1...nil # => true
(1..nil).cover?(1...nil) # => true
(1...nil).cover?(1..nil) # => true
Updated by sawa (Tsuyoshi Sawada) over 2 years ago
@janosch-x (Janosch Müller) (Janosch Müller) That seems to be a good idea. That is a possible compromise. Thank you for the idea.
Updated by mame (Yusuke Endoh) over 2 years ago
- Status changed from Open to Rejected
We were aware of this issue, and we agreed with the current behavior. This thread in Twitter discusses the issue: https://twitter.com/mrkn/status/987509913365594112
Here is a simple English translation:
- ko1: Under
ary = [1, 2, 3]
, whatary[1..]
andary[1...]
return respectively? - mame:
ary[1..]
should return[2, 3]
. I don't knowary[1...]
- matz:
ary[1...]
should also return[2, 3]
- shugo: How is
1...
useful? - mrkn:
1...
is mathematically correct - shugo: Okay, but we want to write
1..
in practical, so having both looks good - mrkn: I will write
1...
in practical, but1..
is acceptable as the same meaning as1...
- shugo: What should
(1..).exclude_end?
return? - mrkn: I want it to be false formally because
1..
is something unnatural - shugo: I agree with you
- mame: Let keep the current behavior as is until there is a use case
So I close this issue. Let me know if you have a practical use case to change the behavior.
BTW, I have a new question about exclusive endless range: (0...).include?(Float::INFINITY)
returns true currently, but should it return false? (IMO, the current behavior is good enough.)
Updated by sawa (Tsuyoshi Sawada) over 2 years ago
@mame (Yusuke Endoh) san, thanks for letting me know about the discussion.
It looks to me that the discussion you quoted was directed toward letting 1..
be an alias of 1...
, just like https://bugs.ruby-lang.org/issues/18749#note-1 suggests, all the way up to shugo's comment "I agree with you". And then, it suddenly ended with your comment "Let [us] keep the current behavior as is until there is a use case," which is a different approach.
However, since you claim so, I do not oppose your decision to close this issue.
Regarding (0...).include?(Float::INFINITY)
, from a mathematical standpoint, ∞ (counterpart to Float::INFINITY
) is not a real number whereas [0, ∞) (counterpart to 0...
) is a subset of real numbers, so I think it should return false
.
Updated by mame (Yusuke Endoh) over 2 years ago
sawa (Tsuyoshi Sawada) wrote in #note-5:
@mame (Yusuke Endoh) san, thanks for letting me know about the discussion.
It looks to me that the discussion you quoted was directed toward letting
1..
be an alias of1...
, just like https://bugs.ruby-lang.org/issues/18749#note-1 suggests, all the way up to shugo's comment "I agree with you". And then, it suddenly ended with your comment "Let [us] keep the current behavior as is until there is a use case," which is a different approach.
It was a typo in his tweet. @mrkn (Kenta Murata) meant "I want it to be false formally". I confirmed him, and I updated my previous comment.