Project

General

Profile

Actions

Feature #18749

closed

Strangeness of endless inclusive ranges

Added by sawa (Tsuyoshi Sawada) almost 2 years ago. Updated almost 2 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
[ruby-core:108365]

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) almost 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) almost 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.

Actions #3

Updated by sawa (Tsuyoshi Sawada) almost 2 years ago

  • Description updated (diff)

Updated by mame (Yusuke Endoh) almost 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], what ary[1..] and ary[1...] return respectively?
  • mame: ary[1..] should return [2, 3]. I don't know ary[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, but 1.. is acceptable as the same meaning as 1...
  • 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) almost 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) almost 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 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.

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0