From: "jeremyevans0 (Jeremy Evans)" <noreply@...>
Date: 2022-08-19T21:50:02+00:00
Subject: [ruby-core:109580] [Ruby master Bug#18580] Range#include? inconsistency for beginless String ranges

Issue #18580 has been updated by jeremyevans0 (Jeremy Evans).


nobu (Nobuyoshi Nakada) wrote in #note-4:
> matz (Yukihiro Matsumoto) wrote in #note-3:
> > So I decided to make `include?` to raise exception for beginless/endless non-numeric ranges.
> 
> I understood that all non-numeric ranges raise `ArgumentError`.
> Only beginless/endless ranges?

Assuming @matz only wants this behavior change for beginless/endless non-numeric ranges, I submitted a pull request to implement that: https://github.com/ruby/ruby/pull/6261

Beginless ranges other than those having a string end already raised TypeError in this case, and I think that makes the most sense, so I used TypeError for the exception.

I don't think we should change `Range#include?` behavior for ranges with both a begin and an end, as that is likely to break a substantial amount of existing code.

----------------------------------------
Bug #18580: Range#include? inconsistency for beginless String ranges
https://bugs.ruby-lang.org/issues/18580#change-98752

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
The follow-up of #18577.

`Range#include?` is [specialized for strings](https://github.com/ruby/ruby/blob/master/range.c#L1782). For all I can tell, this behavior is relatively new: it emerged when switching `#===` to use `#cover?` instead of `#include?`:
* in 2.6, it was decided that String Ranges should continue to use `#include?` (#14575)
* ...but in 2.7, it was decided that they should use `#cover?` as all others (#15449)

The "despecialization" in 2.7 was actually done by adding more specialization in `range_include_internal`.

This leads to the following:
```ruby
# default Range behavior:
(..Date.today).include? Date.today
# (irb):10:in `each': can't iterate from NilClass (TypeError)

# String Range behavior:
(..'z').include?('w')
# => true 
```

Why I think **it is bad**:
1. This is a leftover of the relatively recent change; why it is 3 versions old, I doubt there is a lot of code relying on this quirk (only beginless ranges are affected)
2. The more "invisible specialization", the more possibility of bugs (as #18577 shows)
3. It is easy to stumble upon while learning Ruby/experimenting (strings and ranges are the most basic objects to be seen in many examples and courses), and to become confused, because there is no reasonable explanation for the semantics other than "well... for String, it is so"
4. It *can* be said that the String ranges behavior have the same specialization as Numeric ones, but it is not so; while Numeric Ranges specialization is long-living legacy, it is at least consistent (`#include?` just behaves like `#cover?` for them, always):

Numerics:
```ruby
(1...3).include? 1.5
# => true, like #cover?

(...3).include? 1.5
# => true, same, the explanation is "just like #cover?"
```
Strings:
```ruby
('a'..'z').include?('ww')
# => false, like #include? for any other type

(..'z').include?('ww')
# => true, this is deeply confusing
```



-- 
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>