Issue #16986 has been updated by shyouhei (Shyouhei Urabe).
JFYI JavaScript is having a similar proposal which now proposes `#{…}` syntax.
https://github.com/tc39/proposal-record-tuple
`#` is not a comment marker in that language though.
----------------------------------------
Feature #16986: Anonymous Struct literal
https://bugs.ruby-lang.org/issues/16986#change-100914
* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
----------------------------------------
# Abstract
How about introducing anonymous Struct literal such as `${a: 1, b: 2}`?
It is almost the same as `Struct.new(:a, :b).new(1, 2)`.
# Proposal
## Background
In many cases, people use hash objects to represent a set of values such as `person = {name: "ko1", country: 'Japan'}` and access its values through `person[:name]` and so on. It is not easy to write (three characters `[:]`!), and it easily introduces misspelling (`person[:nama]` doesn't raise an error).
If we make a `Struct` object by doing `Person = Struct.new(:name, :age)` and `person = Person.new('ko1', 'Japan')`, we can access its values through `person.name` naturally. However, it costs coding. And in some cases, we don't want to name the class (such as `Person`).
Using `OpenStruct` (`person = OpenStruct.new(name: "ko1", country: "Japan")`), we can access it through `person.name`, but we can extend the fields unintentionally, and the performance is not good.
Of course, we can define a class `Person` with attr_readers. But it takes several lines.
To summarize the needs:
* Easy to write
* Doesn't require declaring the class
* Accessible through `person.name` format
* Limited fields
* Better performance
## Idea
Introduce new literal syntax for an anonymous Struct such as: `${ a: 1, b: 2 }`.
Similar to Hash syntax (with labels), but with `$` prefix to distinguish.
Anonymous structs which have the same member in the same order share their class.
```ruby
s1 = ${a: 1, b: 2, c: 3}
s2 = ${a: 1, b: 2, c: 3}
assert s1 == s2
s3 = ${a: 1, c: 3, b: 2}
s4 = ${d: 4}
assert_equal false, s1 == s3
assert_equal false, s1 == s4
```
## Note
Unlike Hash literal syntax, this proposal only allows `label: expr` notation. No `${**h}` syntax.
This is because if we allow to splat a Hash, it can be a vulnerability by splatting outer-input Hash.
Thanks to this spec, we can specify anonymous Struct classes at compile time.
We don't need to find or create Struct classes at runtime.
## Implementatation
https://github.com/ruby/ruby/pull/3259
# Discussion
## Notation
Matz said he thought about `{|a: 1, b: 2 |}` syntax.
## Performance
Surprisingly, Hash is fast and Struct is slow.
```ruby
Benchmark.driver do |r|
r.prelude <<~PRELUDE
st = Struct.new(:a, :b).new(1, 2)
hs = {a: 1, b: 2}
class C
attr_reader :a, :b
def initialize() = (@a = 1; @b = 2)
end
ob = C.new
PRELUDE
r.report "ob.a"
r.report "hs[:a]"
r.report "st.a"
end
__END__
Warming up --------------------------------------
ob.a 38.100M i/s - 38.142M times in 1.001101s (26.25ns/i, 76clocks/i)
hs[:a] 37.845M i/s - 38.037M times in 1.005051s (26.42ns/i, 76clocks/i)
st.a 33.348M i/s - 33.612M times in 1.007904s (29.99ns/i, 87clocks/i)
Calculating -------------------------------------
ob.a 87.917M i/s - 114.300M times in 1.300085s (11.37ns/i, 33clocks/i)
hs[:a] 85.504M i/s - 113.536M times in 1.327850s (11.70ns/i, 33clocks/i)
st.a 61.337M i/s - 100.045M times in 1.631064s (16.30ns/i, 47clocks/i)
Comparison:
ob.a: 87917391.4 i/s
hs[:a]: 85503703.6 i/s - 1.03x slower
st.a: 61337463.3 i/s - 1.43x slower
```
I believe we can speed up `Struct` similarly to ivar accesses, so we can improve the performance.
BTW, OpenStruct (os.a) is slow.
```
Comparison:
hs[:a]: 92835317.7 i/s
ob.a: 85865849.5 i/s - 1.08x slower
st.a: 53480417.5 i/s - 1.74x slower
os.a: 12541267.7 i/s - 7.40x slower
```
For memory consumption, `Struct` is more lightweight because we don't need to keep the key names.
## Naming
If we name an anonymous class, literals with the same members share the name.
```ruby
s1 = ${a:1}
s2 = ${a:2}
p [s1, s2] #=> [#<struct a=1>, #<struct a=2>]
A = s1.class
p [s1, s2] #=> [#<struct A a=1>, #<struct A a=2>]
```
Maybe that is not a good behavior.
--
https://bugs.ruby-lang.org/
Issue #16986 has been updated by Bumppoman (Brendon Stanton).
In a Ruby 3.2 context, I wonder how this would look in the mindset of the new Data class instead of a struct?
----------------------------------------
Feature #16986: Anonymous Struct literal
https://bugs.ruby-lang.org/issues/16986#change-100912
* Author: ko1 (Koichi Sasada)
* Status: Open
* Priority: Normal
* Assignee: matz (Yukihiro Matsumoto)
----------------------------------------
# Abstract
How about introducing anonymous Struct literal such as `${a: 1, b: 2}`?
It is almost the same as `Struct.new(:a, :b).new(1, 2)`.
# Proposal
## Background
In many cases, people use hash objects to represent a set of values such as `person = {name: "ko1", country: 'Japan'}` and access its values through `person[:name]` and so on. It is not easy to write (three characters `[:]`!), and it easily introduces misspelling (`person[:nama]` doesn't raise an error).
If we make a `Struct` object by doing `Person = Struct.new(:name, :age)` and `person = Person.new('ko1', 'Japan')`, we can access its values through `person.name` naturally. However, it costs coding. And in some cases, we don't want to name the class (such as `Person`).
Using `OpenStruct` (`person = OpenStruct.new(name: "ko1", country: "Japan")`), we can access it through `person.name`, but we can extend the fields unintentionally, and the performance is not good.
Of course, we can define a class `Person` with attr_readers. But it takes several lines.
To summarize the needs:
* Easy to write
* Doesn't require declaring the class
* Accessible through `person.name` format
* Limited fields
* Better performance
## Idea
Introduce new literal syntax for an anonymous Struct such as: `${ a: 1, b: 2 }`.
Similar to Hash syntax (with labels), but with `$` prefix to distinguish.
Anonymous structs which have the same member in the same order share their class.
```ruby
s1 = ${a: 1, b: 2, c: 3}
s2 = ${a: 1, b: 2, c: 3}
assert s1 == s2
s3 = ${a: 1, c: 3, b: 2}
s4 = ${d: 4}
assert_equal false, s1 == s3
assert_equal false, s1 == s4
```
## Note
Unlike Hash literal syntax, this proposal only allows `label: expr` notation. No `${**h}` syntax.
This is because if we allow to splat a Hash, it can be a vulnerability by splatting outer-input Hash.
Thanks to this spec, we can specify anonymous Struct classes at compile time.
We don't need to find or create Struct classes at runtime.
## Implementatation
https://github.com/ruby/ruby/pull/3259
# Discussion
## Notation
Matz said he thought about `{|a: 1, b: 2 |}` syntax.
## Performance
Surprisingly, Hash is fast and Struct is slow.
```ruby
Benchmark.driver do |r|
r.prelude <<~PRELUDE
st = Struct.new(:a, :b).new(1, 2)
hs = {a: 1, b: 2}
class C
attr_reader :a, :b
def initialize() = (@a = 1; @b = 2)
end
ob = C.new
PRELUDE
r.report "ob.a"
r.report "hs[:a]"
r.report "st.a"
end
__END__
Warming up --------------------------------------
ob.a 38.100M i/s - 38.142M times in 1.001101s (26.25ns/i, 76clocks/i)
hs[:a] 37.845M i/s - 38.037M times in 1.005051s (26.42ns/i, 76clocks/i)
st.a 33.348M i/s - 33.612M times in 1.007904s (29.99ns/i, 87clocks/i)
Calculating -------------------------------------
ob.a 87.917M i/s - 114.300M times in 1.300085s (11.37ns/i, 33clocks/i)
hs[:a] 85.504M i/s - 113.536M times in 1.327850s (11.70ns/i, 33clocks/i)
st.a 61.337M i/s - 100.045M times in 1.631064s (16.30ns/i, 47clocks/i)
Comparison:
ob.a: 87917391.4 i/s
hs[:a]: 85503703.6 i/s - 1.03x slower
st.a: 61337463.3 i/s - 1.43x slower
```
I believe we can speed up `Struct` similarly to ivar accesses, so we can improve the performance.
BTW, OpenStruct (os.a) is slow.
```
Comparison:
hs[:a]: 92835317.7 i/s
ob.a: 85865849.5 i/s - 1.08x slower
st.a: 53480417.5 i/s - 1.74x slower
os.a: 12541267.7 i/s - 7.40x slower
```
For memory consumption, `Struct` is more lightweight because we don't need to keep the key names.
## Naming
If we name an anonymous class, literals with the same members share the name.
```ruby
s1 = ${a:1}
s2 = ${a:2}
p [s1, s2] #=> [#<struct a=1>, #<struct a=2>]
A = s1.class
p [s1, s2] #=> [#<struct A a=1>, #<struct A a=2>]
```
Maybe that is not a good behavior.
--
https://bugs.ruby-lang.org/
Issue #19278 has been reported by tenderlovemaking (Aaron Patterson).
----------------------------------------
Bug #19278: Constructing subclasses of Data with positional arguments
https://bugs.ruby-lang.org/issues/19278
* Author: tenderlovemaking (Aaron Patterson)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.0 (2022-12-25 revision a528908271) [arm64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
I'd expect both of the following subclasses to work, but the subclass that uses positional parameters raises an exception:
```ruby
Foo = Data.define
class Bar < Foo
def initialize foo:
p foo
end
end
class Baz < Foo
def initialize foo
p foo
end
end
Bar.new foo: 1 # Prints 1
Baz.new 1 # Raises ArgumentError
```
I'd expect the subclass that uses positional arguments to work.
--
https://bugs.ruby-lang.org/
Issue #15778 has been updated by st0012 (Stan Lo).
I think such feature has a great potential for production use too, especially for error monitoring. For example, both [Rollbar](https://rollbar.com) and [Sentry](https://sentry.io) tries to support local variables capturing with `TracePoint`:
- [Rollbar's implementation](https://github.com/rollbar/rollbar-gem/blob/caa82a97177b946… (captures all frames' data)
- [Sentry's implementation](https://github.com/getsentry/sentry-ruby/blob/3097c49c16de2… (captures only the current frame's data)
But as we all know, using `TracePoint` in production is usually risky. So this feature in both services remained "experimental" and is disabled by default, even though it'd be super helpful to users. I think in a sense it proves @Eregon's point:
> hiding it in the C API is not much of a safety, but it makes it inconvenient to use, inefficient (see above) and not portable (e.g., cannot be implemented on JRuby as a C API).
If there can be a bundled gem to power usage like:
```rb
caller_locations(1, 1, debug: true).first.binding
```
it'd level up our developer's debugging experience significantly in both development and production.
### Reasons to have a bundled gem then just use [`binding_of_caller`](https://github.com/banister/binding_of_caller)
1. From a business' perspective, it's easier to convince customers adding a language-official library than a community library.
2. We can be confident that it'd work well with Ruby's latest development, especially with YJIT.
3. It'd allow better cross-platform support.
(As a side note, [Honeybdger](https://www.honeybadger.io/) supports it via [`binding_of_caller`](https://github.com/banister/binding_of_caller), but also disables it by default).
----------------------------------------
Feature #15778: Expose an API to pry-open the stack frames in Ruby
https://bugs.ruby-lang.org/issues/15778#change-100904
* Author: gsamokovarov (Genadi Samokovarov)
* Status: Open
* Priority: Normal
* Assignee: ko1 (Koichi Sasada)
----------------------------------------
Hello,
I'm the maintainer of the web-console (https://github.com/rails/web-console/) gem, where one of our features is to jump between the frames in which an error occurred. To accomplish this, I currently use the Debug Inspector CRuby API. I think we should expose this functionality in Rubyland, so tools like web-console don't need to resort to C code for this. This also makes it quite harder for me to support different implementations like JRuby or TruffleRuby as everyone is having a different way to create Ruby Binding objects that represent the frames.
Here the API ideas:
Add `Thread::Backtrace::Location#binding` method that can create a binding for a specific caller of the current frame. We can reuse the existing `Kernel.caller_locations` method to generate the array of `Thread::Backtrace::Location` objects. We can optionally have the `Kernel.caller_locations(debug: true)` argument if we cannot generate the bindings lazily on the VM that can instruct the VM to do the slower operation.
- `Thread::Backtrace::Location#binding` returns `Binding|nil`. Nil result may mean that the current location is a C frame or a JITted/optimized frame and we cannot debug it.
We can also expose the DebugInspector API directly, as done in the https://github.com/banister/debug_inspector gem, but for tools like web-console, we'd need to map the bindings with the backtrace, as we cannot generate Bindings for every frame (C frames) and this needs to be done in application code, so I think the `Thread::Backtrace::Location#binding` is the better API for Ruby-land.
Such API can help us eventually write most of our debuggers in Ruby as right now we don't have a way to do Post-Mortem debugging without native code or even start our debuggers without monkey-patching `Binding`.
I have presented this idea in a RubyKaigi's 2019 talk called "Writing Debuggers in Plain Ruby", you can check-out the slides for more context: http://kaigi-debuggers-in-ruby.herokuapp.com.
--
https://bugs.ruby-lang.org/
Issue #19232 has been reported by dalexj (alex jensen).
----------------------------------------
Feature #19232: add NoMatchingPatternError#matchee
https://bugs.ruby-lang.org/issues/19232
* Author: dalexj (alex jensen)
* Status: Open
* Priority: Normal
----------------------------------------
currently if you pattern match a Hash, you can inspect the #key and #matchee methods from the NoMatchingPatternKeyError
```ruby
begin
{ error: true } => { value: }
rescue NoMatchingPatternKeyError => e
e.matchee
end
# => {:error=>true}
```
I would find it very helpful for this functionality to be available for the standard `NoMatchingPatternError` without hash key matching
proposed:
```ruby
begin
[:error, "message"] => [:ok, value]
rescue NoMatchingPatternError => e
e.matchee
end
# => [:error, "message"]
```
--
https://bugs.ruby-lang.org/
Issue #19287 has been reported by matsuda (Akira Matsuda).
----------------------------------------
Bug #19287: Let DelegateClass respect the original method's arity in case of 0
https://bugs.ruby-lang.org/issues/19287
* Author: matsuda (Akira Matsuda)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.3.0dev (2022-12-28T16:43:05Z master cada537040) +YJIT [arm64-darwin21]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
As reported in #19165, method delegation with *, **, and ... adds some overhead for a method that takes no argument. And current implementation of delegations in forwardable and delegate basically define all methods to take splat parameters.
But, with regard to DelegateClass, we can investigate the arity of the target methods in the initializer, then we can define the delegation method not to take the splat for zero arity methods.
A benchmark on trunk today results in 37% runtime performance improvement on a such method.
Benchmark:
Warming up --------------------------------------
old 551.697k i/100ms
new 721.906k i/100ms
Calculating -------------------------------------
old 6.511M (± 0.8%) i/s - 33.102M in 5.084530s
new 8.925M (± 1.0%) i/s - 44.758M in 5.015619s
Comparison:
new: 8924652.2 i/s
old: 6510691.1 i/s - 1.37x (± 0.00) slower
Patch: https://github.com/ruby/ruby/pull/7045
--
https://bugs.ruby-lang.org/
Issue #15072 has been updated by normalperson (Eric Wong).
Status changed from Open to Closed
Fixed in #15130
----------------------------------------
Bug #15072: thread.c:4356:5: error: implicit declaration of function ‘ubf_list_atfork’
https://bugs.ruby-lang.org/issues/15072#change-100886
* Author: duerst (Martin Dürst)
* Status: Closed
* Priority: Normal
* Assignee: normalperson (Eric Wong)
* ruby -v: ruby 2.6.0dev (2018-08-01 trunk 64156) [x86_64-cygwin]
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
----------------------------------------
When compiling on cygwin, I get the following error:
```
compiling thread.c
thread.c: In function ‘rb_thread_atfork_internal’:
thread.c:4356:5: error: implicit declaration of function ‘ubf_list_atfork’ [-Werror=implicit-function-declaration]
ubf_list_atfork();
^~~~~~~~~~~~~~~
```
The `ubf_list_atfork` function was introduced by issue #15013 in r64485.
The problem is that the function is defined *as static* in thread_pthread.c, but is used in thread.c. Not being an expert on threads, I thought that maybe `ubf_list_atfork` is a function that's available on some OSes, but that can't be the case. I only got empty results when searching for it.
There's also a macro for `ubf_list_atfork` in thread_win32.c, but that doesn't seem to be related here.
[below it says my Ruby version is trunk 64156, but I'm compiling on HEAD.]
--
https://bugs.ruby-lang.org/
Issue #19279 has been reported by kevin-j-m (Kevin Murphy).
----------------------------------------
Bug #19279: Allow `Coverage.supported?` to recognize oneshot lines mode
https://bugs.ruby-lang.org/issues/19279
* Author: kevin-j-m (Kevin Murphy)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.0 (2022-12-25 revision a528908271) [x86_64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN
----------------------------------------
Currently in Ruby 3.2.0, Coverage's new `supported?` method (introduced in https://bugs.ruby-lang.org/issues/19026) does not recognize support for oneshot lines. Given that it is an available mode of coverage, I'm proposing that `Coverage.supported?` report that oneshot lines is supported.
Current behavior:
``` bash
~|⇒ ruby -v
ruby 3.2.0 (2022-12-25 revision a528908271) [x86_64-darwin22]
~|⇒ irb
irb(main):001:0> require "coverage"
=> true
irb(main):002:0> Coverage.supported?(:lines)
=> true
irb(main):003:0> Coverage.supported?(:oneshot_lines)
=> false
```
--
https://bugs.ruby-lang.org/
I was wondering that every piece of code (gems, etc) that use the new Data object from now on will demand a mandatory upgrade to >= 3.2.0. It's a good thing upgrade the language versions, but often we can't do it for many reasons and would love to use the new code (updated or new gems) on our projects current versions.
On the original Data PR I saw one of the reasons asking Data be a language feature and not a gem ("nobody will even try to use a gem for representing it with, unless the framework/library used already provides one"), I agree with that, but I'm curious if i there is any plan to support older language versions, like releasing a gem to mimic main Data features into older language versions and not force a mandatory upgrade to >= 3.2.0. I searched on rubygems but didn't find one like that.
Btw, looks like what we had before with Fibers and Ractors, but seems to me Data will have an easier and faster adoption.
Thanks.