Issue #18368 has been updated by mame (Yusuke Endoh).
The three clarifications described in #note-17 were discussed at the dev meeting.
@matz said he wanted to make sure if the following pseudo code meets @zverok's expectation.
```ruby
class Range
def step(n)
# TODO: we need to care "exclude end"
# TODO: we should actually use <=> for comparison instead of >= or <=
a, b = self.begin, self.end
case n <=> 0
when -1
while a >= b
yield a
a += n
end
when 0
raise "step can't be 0"
when 1
while a <= b
yield a
a += n
end
end
end
end
```
If it does, @zverok's three clarifications are all approved. However, it was noted that the following case does not work well with @zverok's patch.
```ruby
(1..-1).step(-1.seconds) { p _1 }
# expected: 1, 0 seconds, -1 seconds
# actual: no output
```
Is it possible to fix this issue?
@matz approved the third clarification "Drop support for generic #to_int.", but he also asked if we could keep the behavior by respecting `#to_int` on the `#coerce` side. Welcome if anyone can consider this.
----------------------------------------
Feature #18368: Range#step semantics for non-Numeric ranges
https://bugs.ruby-lang.org/issues/18368#change-103490
* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
----------------------------------------
I am sorry if the question had already been discussed, can't find the relevant topic.
"Intuitively", this looks (for me) like a meaningful statement:
```ruby
(Time.parse('2021-12-01')..Time.parse('2021-12-24')).step(1.day).to_a
# ^^^^^ or just 24*60*60
```
Unfortunately, it doesn't work with "TypeError (can't iterate from Time)".
Initially it looked like a bug for me, but after digging a bit into code/docs, I understood that `Range#step` has an odd semantics of "advance the begin N times with `#succ`, and yield the result", with N being always integer:
```ruby
('a'..'z').step(3).first(5)
# => ["a", "d", "g", "j", "m"]
```
The fact that semantic is "odd" is confirmed by the fact that for Float it is redefined to do what I "intuitively" expected:
```ruby
(1.0..7.0).step(0.3).first(5)
# => [1.0, 1.3, 1.6, 1.9, 2.2]
```
(Like with [`Range#===` some time ago](https://bugs.ruby-lang.org/issues/14575), I believe that to be a strong proof of the wrong generic semantics, if for numbers the semantics needed to be redefined completely.)
Another thing to note is that "skip N elements" seem to be rather "generically Enumerable-related" yet it isn't defined on `Enumerable` (because nobody needs this semantics, typically!)
Hence, two questions:
* Can we redefine generic `Range#step` to new semantics (of using `begin + step` iteratively)? It is hard to imagine the amount of actual usage of the old behavior (with String?.. to what end?) in the wild
* If the answer is "no", can we define a new method with new semantics, like, IDK, `Range#over(span)`?
**UPD:** More examples of useful behavior (it is NOT only about core `Time` class):
```ruby
require 'active_support/all'
(1.minute..20.minutes).step(2.minutes).to_a
#=> [1 minute, 3 minutes, 5 minutes, 7 minutes, 9 minutes, 11 minutes, 13 minutes, 15 minutes, 17 minutes, 19 minutes]
require 'tod'
(Tod::TimeOfDay.parse("8am")..Tod::TimeOfDay.parse("10am")).step(30.minutes).to_a
#=> [#<Tod::TimeOfDay 08:00:00>, #<Tod::TimeOfDay 08:30:00>, #<Tod::TimeOfDay 09:00:00>, #<Tod::TimeOfDay 09:30:00>, #<Tod::TimeOfDay 10:00:00>]
require 'matrix'
(Vector[1, 2, 3]..).step(Vector[1, 1, 1]).take(3)
#=> [Vector[1, 2, 3], Vector[2, 3, 4], Vector[3, 4, 5]]
require 'unitwise'
(Unitwise(0, 'km')..Unitwise(1, 'km')).step(Unitwise(100, 'm')).map(&:to_s)
#=> ["0 km", "1/10 km", "1/5 km", "3/10 km", "2/5 km", "0.5 km", "3/5 km", "7/10 km", "4/5 km", "9/10 km", "1 km"]
```
**UPD:** Responding to discussion points:
**Q:** Matz is concerned that the proposed simple definition will be confusing with the classes where `+` is redefined as concatenation.
**A:** I believe that simplicity of semantics and ease of explaining ("it just uses `+` underneath, whatever `+` does, will be performed") will make the confusion minimal.
**Q:** Why not introduce new API requirement (like "class of range's `begin` should implement `increment` method, and then it will be used in `step`)
**A:** require *every* gem author to change *every* of their objects' behavior. For that, they should be aware of the change, consider it important enough to care, clearly understand the necessary semantics of implementation, have a resource to release a new version... Then all users of all such gems would be required to upgrade. The feature would be DOA (dead-on-arrival).
The two alternative ways I am suggesting: change the behavior of `#step` or introduce a new method with desired behavior:
1. Easy to explain and announce
2. Require no other code changes to immediately become useful
3. With something like [backports](https://github.com/marcandre/backports) or [ruby-next](https://github.com/ruby-next/ruby-next) easy to start using even in older Ruby version, making the code more expressive even before it would be possible for some particular app/compny to upgrade to (say) 3.2
All examples of behavior from the code above are real `irb` output with monkey-patched `Range#step`, demonstrating how little change will be needed to code outside of the `Range`.
--
https://bugs.ruby-lang.org/
Issue #18572 has been updated by shugo (Shugo Maeda).
Eregon (Benoit Daloze) wrote in #note-9:
> From https://bugs.ruby-lang.org/issues/14083#note-3 it seems part of the problem at least is CRuby currently implements what I would call invalid usages of refinements (different set of activates refinements over time for a given call site), and that basically means extra checks and overhead, e.g., for a method which was refined once, even if there are no refinements active in the current scope.
If it's needed to fix the performance issue, it may be acceptable to raise exceptions in some cases (but I don't know the exact condition of such cases).
@ko1, @matz What do you think?
It's simple to prohibit using calls in blocks, but it will break backward compatibility, e.g., using in module_eval.
----------------------------------------
Bug #18572: Performance regression when invoking refined methods
https://bugs.ruby-lang.org/issues/18572#change-103487
* Author: palkan (Vladimir Dementyev)
* Status: Assigned
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
* Backport: 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Since Ruby 3.0, defining a refinement for a method slows down its execution even if we do not activate the refinement:
```ruby
require "benchmark_driver"
source = <<~RUBY
class Hash
def symbolize_keys
transform_keys { |key| key.to_sym rescue key }
end
def refined_symbolize_keys
transform_keys { |key| key.to_sym rescue key }
end
end
module HashRefinements
refine Hash do
def refined_symbolize_keys
raise "never called"
end
end
end
HASH = {foo: 1, bar: 2, baz: 3}
class Foo
def original
end
def refined
end
end
module FooRefinements
refine Foo do
def refined
raise "never called"
end
end
end
FOO = Foo.new
RUBY
Benchmark.driver do |x|
x.prelude %Q{
#{source}
}
x.report "#symbolize_keys original", %{ HASH.symbolize_keys }
x.report "#symbolize_keys refined", %{ HASH.refined_symbolize_keys }
end
Benchmark.driver do |x|
x.prelude %Q{
#{source}
}
x.report "no-op original", %{ FOO.original }
x.report "no-op refined", %{ FOO.refined }
end
```
The results for Ruby 3.1:
```sh
...
Comparison:
#symbolize_keys original: 2372420.1 i/s
#symbolize_keys refined: 1941019.0 i/s - 1.22x slower
...
Comparison:
no-op original: 51790974.2 i/s
no-op refined: 14456518.9 i/s - 3.58x slower
```
For Ruby 2.6 and 2.7:
```sh
Comparison:
#symbolize_keys original: 2278339.7 i/s
#symbolize_keys refined: 2264153.1 i/s - 1.01x slower
...
Comparison:
no-op refined: 64178338.5 i/s
no-op original: 63357980.1 i/s - 1.01x slower
```
You can find the full code and more results in this [gist](https://gist.github.com/palkan/637dc83edd86d70b5dbf72f2a4d702e5).
P.S. The problem was originally noticed by @byroot, see https://github.com/ruby-i18n/i18n/pull/573
--
https://bugs.ruby-lang.org/
Issue #19721 has been reported by andrykonchin (Andrew Konchin).
----------------------------------------
Bug #19721: IO#timeout= can be called without required argument
https://bugs.ruby-lang.org/issues/19721
* Author: andrykonchin (Andrew Konchin)
* Status: Open
* Priority: Normal
* ruby -v: 3.2.1
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
```ruby
f = File.open("a.txt", "w")
f.timeout=() # => nil
```
`IO#timeout=` requires an argument (or it's supposed to require it) but if it's called as a method it seems the check is skipped and missing argument is treated as `nil` value.
If it's called with `#send` - then argument presence is checked:
```ruby
f.send :"timeout="
# ...:in `timeout=': wrong number of arguments (given 0, expected 1) (ArgumentError)
```
--
https://bugs.ruby-lang.org/
Issue #19702 has been reported by hsbt (Hiroshi SHIBATA).
----------------------------------------
Bug #19702: Promote racc as bundled gems
https://bugs.ruby-lang.org/issues/19702
* Author: hsbt (Hiroshi SHIBATA)
* Status: Assigned
* Priority: Normal
* Assignee: hsbt (Hiroshi SHIBATA)
* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
In the past, we need to bundle `racc` runtime for RD parser of RDoc. Now, We have racc compiler and runtime in ruby repository.
I'm not sure we should bundle and maintain `racc` as stdlib in the future. Can we promote `racc` to bundled gems?
--
https://bugs.ruby-lang.org/
Issue #19718 has been reported by nobu (Nobuyoshi Nakada).
----------------------------------------
Feature #19718: Extend `-0` option
https://bugs.ruby-lang.org/issues/19718
* Author: nobu (Nobuyoshi Nakada)
* Status: Open
* Priority: Normal
----------------------------------------
Recently I learned that Perl's `-0` option is extended to accept a hexadecimal Unicode codepoint.
However it uses `-0x`, and since `-x` is used for shebang and cd, it would cause a backward incompatibility if we will incorporate it as it is.
So I propose that `-0uCODEPOINT` instead.
This can be extended to comma- or colon-separated codepoint list.
Also, another idea is `-0:sepaerator` which specifies the separator as-is.
Considering many of modern shells provide escaped string form (e.g., `$'\uHHHH'), this may be a more modern answer, except for NUL cannot be represented.
https://github.com/ruby/ruby/pull/7914
--
https://bugs.ruby-lang.org/
Issue #19520 has been reported by ioquatix (Samuel Williams).
----------------------------------------
Feature #19520: Support for `Module.new(name)` and `Class.new(superclass, name)`.
https://bugs.ruby-lang.org/issues/19520
* Author: ioquatix (Samuel Williams)
* Status: Open
* Priority: Normal
----------------------------------------
See <https://bugs.ruby-lang.org/issues/19450> for previous discussion and motivation.
[This proposal](https://github.com/ruby/ruby/pull/7376) introduces the `name` parameter to `Class.new` and `Module.new`:
```ruby
Class.new(superclass, name)
Module.new(name)
```
As a slight change, we could use keyword arguments instead.
--
https://bugs.ruby-lang.org/
Issue #11704 has been updated by matz (Yukihiro Matsumoto).
Status changed from Assigned to Rejected
The behavior is intended.
Matz.
----------------------------------------
Bug #11704: Refinements only get "used" once in loop
https://bugs.ruby-lang.org/issues/11704#change-103451
* Author: danielpclark (Daniel P. Clark)
* Status: Rejected
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
Same results on Ruby 2.2.2 through Ruby 2.3.0dev (2015-11-18 trunk 52625) [x86_64-linux]
I wrote a benchmark for testing different ways of implementing a `uniq` method and I chose to do it using refinements. I looped over the results in the benchmark and refined the method with 6 different refinements which each worked. But the next iteration when `using` is called it doesn't re-refine with previously used refinements.
Example benchmark output on first iteration:
~~~
Array.new(200) !self.dup.uniq! 1.770000 0.010000 1.780000 ( 1.778248)
Array.new(200) == uniq.length 1.860000 0.000000 1.860000 ( 1.866862)
Array.new(200) == uniq.sort 2.580000 0.010000 2.590000 ( 2.584515)
Array.new(200) each index 41.450000 0.080000 41.530000 ( 41.626149)
Array.new(200) combination(2) 23.460000 0.060000 23.520000 ( 23.568865)
Array.new(200) == self.uniq 1.900000 0.010000 1.910000 ( 1.909466)
~~~
After that the same methods did not get refined.
~~~
Array.new(210) !self.dup.uniq! 1.990000 0.000000 1.990000 ( 2.004269)
Array.new(210) == uniq.length 2.030000 0.010000 2.040000 ( 2.032602)
Array.new(210) == uniq.sort 1.990000 0.000000 1.990000 ( 1.999509)
Array.new(210) each index 1.990000 0.010000 2.000000 ( 2.000181)
Array.new(210) combination(2) 2.000000 0.000000 2.000000 ( 2.010159)
Array.new(210) == self.uniq 2.000000 0.010000 2.010000 ( 2.009117)
Array.new(220) !self.dup.uniq! 2.100000 0.010000 2.110000 ( 2.113701)
Array.new(220) == uniq.length 2.070000 0.000000 2.070000 ( 2.075249)
Array.new(220) == uniq.sort 2.090000 0.010000 2.100000 ( 2.102771)
Array.new(220) each index 2.070000 0.000000 2.070000 ( 2.077393)
Array.new(220) combination(2) 2.070000 0.010000 2.080000 ( 2.079561)
Array.new(220) == self.uniq 2.100000 0.010000 2.110000 ( 2.110839)
Array.new(230) !self.dup.uniq! 2.210000 0.000000 2.210000 ( 2.236008)
Array.new(230) == uniq.length 2.160000 0.010000 2.170000 ( 2.166484)
Array.new(230) == uniq.sort 2.140000 0.000000 2.140000 ( 2.150384)
Array.new(230) each index 2.130000 0.010000 2.140000 ( 2.134572)
Array.new(230) combination(2) 2.120000 0.000000 2.120000 ( 2.129683)
Array.new(230) == self.uniq 2.130000 0.010000 2.140000 ( 2.137515)
~~~
I found no way to inspect what code was being used as refinements currently don't allow introspection (I have read the Refinement Specs https://bugs.ruby-lang.org/projects/ruby-trunk/wiki/RefinementsSpec ). But I figure if I wanted to see what the refinement was I could write an additional method to document the current refinement in use.
~~~ruby
module BooleanUniqWithEqualLength
refine Array do
def uniq?
self.length == self.uniq.length
end
def which_refinement_uniq?
"self.length == self.uniq.length"
end
end
end
~~~
But introspection is not the issue I'm raising here. The issue is that you can only use the refinement once within the scope of the loop. Look at the benchmark results and see the first 6 are correct, and the following 3 times the output is lying about what refinement is being used.
Here's the full benchmark code:
~~~ruby
require 'securerandom'
require 'benchmark'
# written to allow 65,536 unique array items
array_of_x = lambda {|x| SecureRandom.hex(x*2).scan(/..../) }
module Refinements
module BooleanUniqWithDupUniqBang
refine Array do
def uniq?
!self.dup.uniq!
end
end
end
module BooleanUniqWithEqualLength
refine Array do
def uniq?
self.length == self.uniq.length
end
end
end
module BooleanUniqWithEqualSort
refine Array do
def uniq?
self.sort == self.uniq.sort
end
end
end
module BooleanUniqWithEachIndex
refine Array do
def uniq?
self.each_with_index { |a,b|
self.each_with_index { |c,d|
next if b == d
return false if a == c
}
}
true
end
end
end
module BooleanUniqWithCombination
refine Array do
def uniq?
self.combination(2).each {|a,b| return false if a == b}
true
end
end
end
module BooleanUniqWithUniq
refine Array do
def uniq?
self == self.uniq
end
end
end
end
bench_reps = 10_000
bench = Benchmark.benchmark("\nTesting various ways of implementing :uniq? on Array\n(smaller numbers are better)\n\n",34) do |x|
# Note doing anymore than one test per test type seems to wash the results into all things being equal.
# Only the first test gives realistic numbers.
[500].each do |qty|
x.report("Array.new(#{qty}) !self.dup.uniq!") {
using Refinements::BooleanUniqWithDupUniqBang
bench_reps.times do
array_of_x.(qty).uniq?
end
}
x.report("Array.new(#{qty}) == uniq.length") {
using Refinements::BooleanUniqWithEqualLength
bench_reps.times do
array_of_x.(qty).uniq?
end
}
x.report("Array.new(#{qty}) == uniq.sort") {
using Refinements::BooleanUniqWithEqualSort
bench_reps.times do
array_of_x.(qty).uniq?
end
}
x.report("Array.new(#{qty}) each index") {
using Refinements::BooleanUniqWithEachIndex
bench_reps.times do
array_of_x.(qty).uniq?
end
}
x.report("Array.new(#{qty}) combination(2)") {
using Refinements::BooleanUniqWithCombination
bench_reps.times do
array_of_x.(qty).uniq?
end
}
x.report("Array.new(#{qty}) == self.uniq") {
using Refinements::BooleanUniqWithUniq
bench_reps.times do
array_of_x.(qty).uniq?
end
}
end
end
~~~
--
https://bugs.ruby-lang.org/
Issue #19057 has been updated by ioquatix (Samuel Williams).
Okay, here is a PR to introduce deprecations: https://github.com/ruby/ruby/pull/7916
----------------------------------------
Feature #19057: Hide implementation of `rb_io_t`.
https://bugs.ruby-lang.org/issues/19057#change-103448
* Author: ioquatix (Samuel Williams)
* Status: Assigned
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
----------------------------------------
In order to make improvements to the IO implementation like <https://bugs.ruby-lang.org/issues/18455>, we need to add new fields to `struct rb_io_t`.
By the way, ending types in `_t` is not recommended by POSIX, so I'm also trying to rename the internal implementation to drop `_t` where possible during this conversion.
Anyway, we should try to hide the implementation of `struct rb_io`. Ideally, we don't expose any of it, but the problem is backwards compatibility.
So, in order to remain backwards compatibility, we should expose some fields of `struct rb_io`, the most commonly used one is `fd` and `mode`, but several others are commonly used.
There are many fields which should not be exposed because they are implementation details.
## Current proposal
The current proposed change <https://github.com/ruby/ruby/pull/6511> creates two structs:
```c
// include/ruby/io.h
#ifndef RB_IO_T
struct rb_io {
int fd;
// ... public fields ...
};
#else
struct rb_io;
#endif
// internal/io.h
#define RB_IO_T
struct rb_io {
int fd;
// ... public fields ...
// ... private fields ...
};
```
However, we are not 100% confident this is safe according to the C specification. My experience is not sufficiently wide to say this is safe in practice, but it does look okay to both myself, and @Eregon + @tenderlovemaking have both given some kind of approval.
That being said, maybe it's not safe.
There are two alternatives:
## Hide all details
We can make public `struct rb_io` completely invisible.
```c
// include/ruby/io.h
#define RB_IO_HIDDEN
struct rb_io;
int rb_ioptr_descriptor(struct rb_io *ioptr); // accessor for previously visible state.
// internal/io.h
struct rb_io {
// ... all fields ...
};
```
This would only be forwards compatible, and code would need to feature detect like this:
```c
#ifdef RB_IO_HIDDEN
#define RB_IOPTR_DESCRIPTOR rb_ioptr_descriptor
#else
#define RB_IOPTR_DESCRIPTOR(ioptr) rb_ioptr_descriptor(ioptr)
#endif
```
## Nested public interface
Alternatively, we can nest the public fields into the private struct:
```c
// include/ruby/io.h
struct rb_io_public {
int fd;
// ... public fields ...
};
// internal/io.h
#define RB_IO_T
struct rb_io {
struct rb_io_public public;
// ... private fields ...
};
```
## Considerations
I personally think the "Hide all details" implementation is the best, but it's also the lest compatible. This is also what we are ultimately aiming for, whether we decide to take an intermediate "compatibility step" is up to us.
I think "Nested public interface" is messy and introduces more complexity, but it might be slightly better defined than the "Current proposal" which might create undefined behaviour. That being said, all the tests are passing.
--
https://bugs.ruby-lang.org/
Issue #19684 has been reported by mame (Yusuke Endoh).
----------------------------------------
Misc #19684: DevMeeting-2023-06-08
https://bugs.ruby-lang.org/issues/19684
* Author: mame (Yusuke Endoh)
* Status: Open
* Priority: Normal
----------------------------------------
# The next dev meeting
**Date: 2023/06/08 13:00-17:00** (JST)
Log: *TBD*
- Dev meeting *IS NOT* a decision-making place. All decisions should be done at the bug tracker.
- Dev meeting is a place we can ask Matz, nobu, nurse and other developers directly.
- Matz is a very busy person. Take this opportunity to ask him. If you can not attend, other attendees can ask instead of you (if attendees can understand your issue).
- We will write a record of the discussion in the file or to each ticket in English.
- All activities are best-effort (keep in mind that most of us are volunteer developers).
- The date, time and place of the meeting are scheduled according to when/where we can reserve Matz's time.
- *DO NOT* discuss then on this ticket, please.
# Call for agenda items
If you have a ticket that you want matz and committers to discuss, please post it into this ticket in the following format:
```
* [Ticket ref] Ticket title (your name)
* Comment (A summary of the ticket, why you put this ticket here, what point should be discussed, etc.)
```
Example:
```
* [Feature #14609] `Kernel#p` without args shows the receiver (ko1)
* I feel this feature is very useful and some people say :+1: so let discuss this feature.
```
- It is recommended to add a comment by 2023/06/05. We hold a preparatory meeting to create an agenda a few days before the dev-meeting.
- The format is strict. We'll use [this script to automatically create an markdown-style agenda](https://gist.github.com/mame/b0390509ce1491b43610b9ebb665eb86). We may ignore a comment that does not follow the format.
- Your comment is mandatory. We cannot read all discussion of the ticket in a limited time. We appreciate it if you could write a short summary and update from a previous discussion.
--
https://bugs.ruby-lang.org/
Issue #18743 has been updated by nobu (Nobuyoshi Nakada).
Your PR makes `stop_exc` to be overwritten for each `StopIteration`.
IMO, it should not change and the `cause` of all subsequent `StopIteration`s should be the same first exception.
----------------------------------------
Bug #18743: Enumerator#next / peek re-use each others stacktraces
https://bugs.ruby-lang.org/issues/18743#change-103434
* Author: sos4nt (Stefan Schüßler)
* Status: Open
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
I encountered an odd behavior.
If I rescue the `StopIteration` exception from `peek` and call `next` afterwards: (or vice-versa)
```ruby
# enum.rb # 1
# 2
enum = [].each # 3
enum.peek rescue nil # 4
enum.next # 5
```
it will show the stacktrace from the rescued `peek` call:
```
$ ruby enum.rb
enum.rb:4:in `peek': iteration reached an end (StopIteration)
from enum.rb:4:in `<main>'
```
Whereas the error should refer to `next` on line number 5.
The same happens when calling `peek` after `next` or when having muliple `peek` / `next` calls:
```ruby
# enum.rb # 1
# 2
enum = [].each # 3
enum.peek rescue nil # 4
enum.next rescue nil # 5
enum.peek rescue nil # 6
puts "line #{__LINE__}" # 7
enum.next # 8
```
The stacktrace from the first (rescued) `peek` or `next` call will be shown which doesn't reflect the actual error location:
```
$ ruby enum.rb
line 7
enum.rb:4:in `peek': iteration reached an end (StopIteration)
from enum.rb:4:in `<main>'
```
This is very confusing when debugging code.
---Files--------------------------------
01-Recreate-stacktrace-enumerator.patch (1.29 KB)
--
https://bugs.ruby-lang.org/