Issue #12912 has been updated by nobu (Nobuyoshi Nakada).


Why is `(1..).size` `nil`, but not `Float::INFINITY`?

----------------------------------------
Feature #12912: An endless range `(1..)`
https://bugs.ruby-lang.org/issues/12912#change-71702

* Author: mame (Yusuke Endoh)
* Status: Closed
* Priority: Normal
* Assignee: 
* Target version: 2.6
----------------------------------------
Why don't you allow a range without end, like `(1..)`?

There are two advantages.  First, we can write `ary[1..]` instead of `ary[1..-1]`.  The `-1` is one of the most I dislike in Ruby.  It is very magical, ugly, redundant, and disappointing.  I envy Python's `ary[1:]`.  I know that `ary.drop(1)` is slightly fast, but too long for such a common operation, IMO.
Second, we can write `(1..).each {|n| ... }`.  As far as I know, there is no simple way in Ruby to write an endless loop with index.  Which do you like among these?

    (1..).each {|n| ... }
    n = 1; loop { ...; n += 1}
    1.upto(Float::INFINITY) {|n| ... }

Contrary to my expectation, this syntax extension causes no parser conflict.  A patch is attached (for proof of concept).  It requires more work, for example, `(1..).step(2){}` is not supported yet.  But I'd like to hear your opinion first.

What do you think?


## Side remarks

I don't like `ary[1..-2]` so much, but it is actually needed because `ary[1..ary.size-2]` is absurdly long.
Some people may prefer `ary[1..-1]` because of consistency to `ary[1..-2]`.  However, I don't think it is reasonable to force a user who just want to take a suffix of an array, to use the dirty hack of negative index.
I don't think `ary[1...]` (exclusive) is meaningful.
It is better to have `ary[..1]` as a consistency.  But it will cause a shift/reduce conflict.  Anyway, `ary[0..1]` looks not so bad to me since it have no cursed negative index.  So I don't push it.


## How to implement

In the case if this proposal is accepted, what I concern is its semantics: what `(1..)` should return?  I have three candidates.

1) Equivalent to `(1..-1)`.  This is the simplest: `ary[1..]` will work great with no change.  We just have to fix `Range#each` (and other relevant methods).
However, it may be inconvenient to distinguish `(1..)` from `(1..-1)`.  For example, `(1..-1).each {}` will also loop, which is an incompatibility.
Also, we must decide how we handle `("foo"..)`.

2) Equivalent to `(1..nil)`.  We must modify both `Array#[]` and `Range#each`, but the change will rarely cause incompatibility.
An existing method that does not know this proposal may not work correctly (such as raising an exception), but won't cause any catastrophic failure (such as core dump).

3) Introduce a new type of Range object, such as `(1..Qundef)`.  This is the most radical way.  An old method that does not know the type of Range will cause abnormal termination.

IMO, 2) is the most reasonable.  The attached patch uses 1) as a proof of concept.

Note that `(1..nil)` is not a brand new type of Range.  It is traditionally possible to create `(1..nil)` in pure Ruby, you know:

    p Marshal.load("\x04\bo:\nRange\b:\texclF:\nbegini\x06:\bend0")
    #=> (1..nil)

A piece of cake :-)  So, to be precise, an incompatibility issue may occur if any method uses `(1..nil)` meaningfully.  I believe that there is no such a method, though.

---Files--------------------------------
endless-range.patch (943 Bytes)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>