Issue #5534 has been updated by Alexey Muranov.


I was not precise, i meant not equality, but *identity* --- that there be only one empty range: Range::EMPTY_SET.  Like this:

(2..1)  # => Range::EMPTY_SET

This way it would be clear what a range is --- just an infinite set of a certain form, with methods to work with it.

I am in favor of reducing the number of internal attributes of class instances when possible and simplifying their definitions.  In my opinion, this should help to understand what a program is doing, to avoid side effects, to write specifications (and hence to maintain existing behavior in future versions), and to settle more easily on desire behavior for new methods.

Mathematically, ranges (2..1) et (3..0) are equal --- both empty, but Ruby remembers their "bounds" and treats them differently.

If my proposed definition of  Range  is accepted, the questions like the one discussed in #4541 will simply not arise: since the ranges  (4..1)  and  (3..1)  will be identical,  [1,2,3][3..1]  and  [1,2,3][4..1]  will be giving identical results, whatever the result is.

I propose to view the "memory" of bounds of an empty range as an artifact of implementation of  Range , which should not be used for operations like  a[1..-2] .


My more general view point is the following: i think it will help to settle on some kind of standards for the language if reasonable relations between different methods are enforced.  It is already done in cases where one method uses another, for example  Comparable#> ,  Comparable#< , etc., use  #<=> :

a < b  if and only if  (a <=> b) == -1

I think it would be helpful for understanding the language and coming up with some kind of standards or best practice guidelines if similar relations were imposed between methods that cannot be defined in terms of one another simply because of computer limitations (not being able to loop over infinitely many objects).  For example, require in specifications that (unless overridden in a subclass) for two ranges  x  and  y ,

x == y  must be true if and only if  x.include?(z) == y.include?(z)  is true for every possible object  z.
----------------------------------------
Feature #5534: Redefine Range class and introduce RelativeNumeric and RelativeRange
http://redmine.ruby-lang.org/issues/5534

Author: Alexey Muranov
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: 


I started by commenting on Feature #4541, but ended up with proposing a new feature myself.

I suggest to redefine the behavior of Range class so that all empty ranges be equal:

(2..1) == (1..-1) and (2..1) == (1...1) and (2..1) == ('z'..'a') # => true

In other fords, ranges `r1` and `r2` should be equal if and only if `r1.include?` and `r2.include?` give identical results for all inputs.  (Why is it not `includes?` by the way?)  Thus Range would simply be a way to store certain infinite sets.

This change will result in not being able to slice an array `a` from beginning and from the end simultaneously with `a[1..-2]`. To resolve this, i propose to introduce `RelativeNumeric` and `RelativeRange` classes.

Each `RelativeNumeric` would be a `Numeric` with an "anchor", which is an arbitrary symbol.  For example:

3.from(:bottom)  # would return a "relative" 3 with "anchor" :bottom

One can define shortcuts `#from_bottom` for `#from(:bottom)` and `#from_top` for `#from_top`.

A `RelativeRange` is a range with relative bounds.  If bounds of a relative range r are relative to the same anchor and the range is seen to be empty, it should be equal to *the* empty relative range with this anchor.  For example:

(3.from(:center)..2.from(:center)) == (0.from(:center)...0.from(:center)) # => true

Now, to do what is currently done by `a[1..-2]`, one can redefine `Array#slice` to use instead:

a[1.from_bottom..(-1).from_top]

What do you think?



-- 
http://redmine.ruby-lang.org