Issue #19160 has been reported by kaiquekandykoga (Kaíque Koga).
----------------------------------------
Bug #19160: cmp_clamp arguments
https://bugs.ruby-lang.org/issues/19160
* Author: kaiquekandykoga (Kaíque Koga)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-freebsd13.1]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
If clamp receives min higher than max, it will raise an exception. The message says *min argument must be smaller than max argument* , but min can actually be equal to max too.
Patch https://github.com/ruby/ruby/pull/6802.
--
https://bugs.ruby-lang.org/
Issue #19161 has been reported by werebus (Matt Moretti).
----------------------------------------
Bug #19161: Cannot compile 3.0.5 or 3.1.3 on Red Hat 7
https://bugs.ruby-lang.org/issues/19161
* Author: werebus (Matt Moretti)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
When attempting to run `make` on either the 3.0.5 or 3.1.3 release, I get the following error (I included the whole output as it's pretty short):
```
BASERUBY = /opt/ruby/bin/ruby --disable=gems
CC = gcc -std=gnu11
LD = ld
LDSHARED = gcc -std=gnu11 -shared
CFLAGS = -O3 -fno-fast-math -ggdb3 -Wall -Wextra -Wdeprecated-declarations -Wimplicit-function-declaration -Wimplicit-int -Wpointer-arith -Wwrite-strings -Wold-style-definition -Wmissing-noreturn -Wno-cast-function-type -Wno-constant-logical-operand -Wno-long-long -Wno-missing-field-initializers -Wno-overlength-strings -Wno-packed-bitfield-compat -Wno-parentheses-equality -Wno-self-assign -Wno-tautological-compare -Wno-unused-parameter -Wno-unused-value -Wsuggest-attribute=format -Wsuggest-attribute=noreturn -Wunused-variable
XCFLAGS = -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fno-strict-overflow -fvisibility=hidden -fexcess-precision=standard -DRUBY_EXPORT -fPIE -I. -I.ext/include/x86_64-linux -I./include -I. -I./enc/unicode/13.0.0
CPPFLAGS =
DLDFLAGS = -Wl,--compress-debug-sections=zlib -fstack-protector-strong -pie
SOLIBS = -lz -lpthread -lrt -lrt -ldl -lcrypt -lm
LANG = en_US.UTF-8
LC_ALL =
LC_CTYPE =
MFLAGS =
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
compiling ./main.c
compiling dmydln.c
compiling miniinit.c
In file included from vm_core.h:83:0,
from iseq.h:14,
from mini_builtin.c:3,
from miniinit.c:51:
thread_pthread.h:108:43: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘struct’
RUBY_EXTERN RB_THREAD_LOCAL_SPECIFIER struct rb_execution_context_struct *ruby_current_ec;
^
In file included from iseq.h:14:0,
from mini_builtin.c:3,
from miniinit.c:51:
vm_core.h: In function ‘rb_current_execution_context’:
vm_core.h:1870:34: error: ‘ruby_current_ec’ undeclared (first use in this function)
rb_execution_context_t *ec = ruby_current_ec;
^
vm_core.h:1870:34: note: each undeclared identifier is reported only once for each function it appears in
miniinit.c: At top level:
cc1: warning: unrecognized command line option "-Wno-tautological-compare" [enabled by default]
cc1: warning: unrecognized command line option "-Wno-self-assign" [enabled by default]
cc1: warning: unrecognized command line option "-Wno-parentheses-equality" [enabled by default]
cc1: warning: unrecognized command line option "-Wno-constant-logical-operand" [enabled by default]
cc1: warning: unrecognized command line option "-Wno-cast-function-type" [enabled by default]
make: *** [miniinit.o] Error 1
```
Both Ruby 3.0.4 and 3.1.2 build without error and pass (all but one of) the `make check` tests.
I know RHEL 7 is getting to be pretty old; I suspect a factor here are the ancient build tools available to me. But... EOL for Ruby 2.7 comes before the one for RHEL 7, so I'm trying to prioritize.
--
https://bugs.ruby-lang.org/
Issue #19158 has been reported by deivid (David Rodríguez).
----------------------------------------
Bug #19158: Ruby 3.1.3 installs wrong gemspec for debug gem
https://bugs.ruby-lang.org/issues/19158
* Author: deivid (David Rodríguez)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [arm64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
A pristine installation of Ruby 3.1.3 shows an installed `debug-1.6.3.gemspec` file that claims that debug-1.6.3 has no dependencies. This is incorrect, and causes issues for Bundler as reported at https://github.com/rubygems/rubygems/issues/6082.
An issue workaround is to manually reinstall the gem with `gem install debug:1.6.3`. That will reinstall the gem, including a correct gemspec file.
This is the file diff before and after reinstalling the gem
```diff
--- old.gemspec 2022-11-29 13:54:36
+++ /Users/deivid/.asdf/installs/ruby/3.1.3/lib/ruby/gems/3.1.0/specifications/debug-1.6.3.gemspec 2022-11-29 13:55:25
@@ -24,4 +24,16 @@
s.summary = "Debugging functionality for Ruby".freeze
s.installed_by_version = "3.3.26" if s.respond_to? :installed_by_version
+
+ if s.respond_to? :specification_version then
+ s.specification_version = 4
+ end
+
+ if s.respond_to? :add_runtime_dependency then
+ s.add_runtime_dependency(%q<irb>.freeze, [">= 1.3.6"])
+ s.add_runtime_dependency(%q<reline>.freeze, [">= 0.3.1"])
+ else
+ s.add_dependency(%q<irb>.freeze, [">= 1.3.6"])
+ s.add_dependency(%q<reline>.freeze, [">= 0.3.1"])
+ end
end
```
In general, the files installed by ruby-core for bundled gems should be identical to the ones installed by explicit `gem install`.
This issue is also present in master and I'm guessing it was introduced by https://github.com/ruby/ruby/commit/a2c66f52f402cb58372e271226f3341065561e53 (backported at https://github.com/ruby/ruby/commit/2d26e45135af8b427d9ccc6d47082c21be8b9c74).
--
https://bugs.ruby-lang.org/
Issue #19153 has been reported by cpinto (Celso Pinto).
----------------------------------------
Bug #19153: Since 2.7.7 CGI::Cookie raises ArgumentError when cookie domains is prefixed with a dot
https://bugs.ruby-lang.org/issues/19153
* Author: cpinto (Celso Pinto)
* Status: Open
* Priority: Normal
* ruby -v: ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [arm64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
The rspec tests of our Rails app started failing with an ArgumentError after upgrading to 2.7.7. On inspection, the issue seems to be caused by CGI::Cookie.domain=:
```
def domain=(str)
if str and ((str = str.b).bytesize > 255 or !DOMAIN_VALUE_RE.match?(str))
raise ArgumentError, "invalid domain: #{str.dump}"
end
@domain = str
end
```
Setting a breakpoint:
0> str
=> ".example.com"
0> DOMAIN_VALUE_RE
=> /\A(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z/
0> DOMAIN_VALUE_RE.match?(str)
=> false
0> DOMAIN_VALUE_RE.match?('example.com')
=> true
--
https://bugs.ruby-lang.org/
Issue #19154 has been reported by fxn (Xavier Noria).
----------------------------------------
Bug #19154: Specify require and autoload guarantees in ractors
https://bugs.ruby-lang.org/issues/19154
* Author: fxn (Xavier Noria)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.2.0preview3 (2022-11-27) [x86_64-darwin22]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Given a file `c.rb`:
```ruby
class C
end
```
the following script:
```ruby
r1 = Ractor.new do
require './c.rb'
end
r2 = Ractor.new do
require './c.rb'
end
r1.take
r2.take
```
raises:
```
% ruby -v foo.rb
ruby 3.2.0preview3 (2022-11-27) [x86_64-darwin22]
foo.rb:1: warning: Ractor is experimental, and the behavior may change in future versions of Ruby! Also there are many implementation issues.
#<Thread:0x000000010fee2928 run> terminated with exception (report_on_exception is true):
#<Thread:0x00000001102acfe0 run> terminated with exception (report_on_exception is true):
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
from foo.rb:6:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require'<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': : can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError) from foo.rb:2:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from foo.rb:2:in `block in <main>'
from foo.rb:6:in `block in <main>'
<internal:ractor>:698:in `take': thrown by remote Ractor. (Ractor::RemoteError)
from foo.rb:9:in `<main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:164:in `ensure in require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from <internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:167:in `require'
from foo.rb:2:in `block in <main>'
<internal:/Users/fxn/.rbenv/versions/3.2.0-preview3/lib/ruby/3.2.0+3/rubygems/core_ext/kernel_require.rb>:37:in `require': can not access non-shareable objects in constant Kernel::RUBYGEMS_ACTIVATION_MONITOR by non-main ractor. (Ractor::IsolationError)
from foo.rb:2:in `block in <main>'
```
Would it be possible to have documentation about their interaction?
This is important also to understand autoloading within ractors, since constant references may trigger `require` calls.
--
https://bugs.ruby-lang.org/
Issue #19150 has been reported by Eregon (Benoit Daloze).
----------------------------------------
Bug #19150: pack/unpack silently ignores unknown directives
https://bugs.ruby-lang.org/issues/19150
* Author: Eregon (Benoit Daloze)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
But I believe it should be an error instead.
Typically when a parser sees a syntax error it should fail not continue silently.
For instance `[1].pack('<L')` succeeds and only emits a warning if `$VERBOSE` is true.
This behavior caused confusion in https://github.com/oracle/truffleruby/issues/2791
I think it should fail with an `ArgumentError` instead.
Extracted from https://bugs.ruby-lang.org/issues/19108#note-3
--
https://bugs.ruby-lang.org/
Issue #19156 has been reported by mk (Matthias Käppler).
----------------------------------------
Bug #19156: ObjectSpace.dump_all segfault during string inspection
https://bugs.ruby-lang.org/issues/19156
* Author: mk (Matthias Käppler)
* Status: Open
* Priority: Normal
* ruby -v: ruby 3.0.4p208 (2022-04-12 revision 3fa771dded) [x86_64-linux]
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
I am working on a feature that would allow our application to capture heap dumps during shutdown for later inspection.
These heap dumps are captured via `ObjectSpace.dump_all(output: io)`. While walking the object space, MRI occasionally segfaults while inspect string object in `search_nonascii` of `string.c`:
```
/usr/local/lib/ruby/3.0.0/objspace.rb:87: [BUG] Segmentation fault at 0x00007efee4201000
ruby 3.0.4p208 (2022-04-12 revision 3fa771dded) [x86_64-linux]
...
-- Control frame information -----------------------------------------------
c:0053 p:---- s:0312 e:000311 CFUNC :_dump_all
c:0052 p:0130 s:0305 e:000304 METHOD /usr/local/lib/ruby/3.0.0/objspace.rb:87
c:0051 p:0023 s:0295 e:000294 METHOD /home/git/gitlab/lib/gitlab/memory/reports/heap_dump.rb:26
...
-- C level backtrace information -------------------------------------------
/usr/local/lib/libruby.so.3.0(rb_print_backtrace+0x11) [0x7efee4ad0c5e] vm_dump.c:758
/usr/local/lib/libruby.so.3.0(rb_vm_bugreport) vm_dump.c:998
/usr/local/lib/libruby.so.3.0(rb_bug_for_fatal_signal+0xf8) [0x7efee48d0b08] error.c:787
/usr/local/lib/libruby.so.3.0(sigsegv+0x55) [0x7efee4a23db5] signal.c:963
/lib/x86_64-linux-gnu/libpthread.so.0(__restore_rt+0x0) [0x7efee4f12140] ../sysdeps/pthread/funlockfile.c:28
/usr/local/lib/libruby.so.3.0(search_nonascii+0x30) [0x7efee4a3ca60] string.c:552
/usr/local/lib/libruby.so.3.0(coderange_scan) string.c:585
/usr/local/lib/libruby.so.3.0(enc_coderange_scan+0x1b) [0x7efee4a3e28a] string.c:709
/usr/local/lib/libruby.so.3.0(rb_enc_str_coderange) string.c:727
/usr/local/lib/ruby/3.0.0/x86_64-linux/objspace.so(is_broken_string+0x8) [0x7efeced9c304] ../../internal/string.h:116
/usr/local/lib/ruby/3.0.0/x86_64-linux/objspace.so(dump_object) objspace_dump.c:388
/usr/local/lib/ruby/3.0.0/x86_64-linux/objspace.so(heap_i+0x39) [0x7efeced9caa9] objspace_dump.c:521
/usr/local/lib/libruby.so.3.0(objspace_each_objects_without_setup+0xaf) [0x7efee48e878f] gc.c:3232
/usr/local/lib/libruby.so.3.0(objspace_each_objects_protected+0x14) [0x7efee48e87c4] gc.c:3242
/usr/local/lib/libruby.so.3.0(rb_ensure+0x12a) [0x7efee48d96aa] eval.c:1162
/usr/local/lib/libruby.so.3.0(objspace_each_objects+0x28) [0x7efee48fb458] gc.c:3310
/usr/local/lib/libruby.so.3.0(rb_objspace_each_objects) gc.c:3298
/usr/local/lib/ruby/3.0.0/x86_64-linux/objspace.so(objspace_dump_all+0x88) [0x7efeced9b068] objspace_dump.c:616
...
```
Unfortunately I couldn't get my hands on that memory region to see which strings are causing this since this doesn't always happen.
I suspect this is also a problem with MRI master since the code looks unchanged from 3.0.4.
--
https://bugs.ruby-lang.org/
Issue #19000 has been updated by RubyBugs (A Nonymous).
For comparison, you can plug in the following example code for a faster implementation of `#with` and it should work the same:
```ruby
Point = Data.define(:x, :y) do
# Example only, too slow. Real implementation should eliminate allocations.
# def with(**args)
# raise ArgumentError unless args.keys.all? { |k| members.include? k }
# self.class.new(**(to_h.merge(args)))
# end
# A trick to avoid `nil` for defaults so that `nil` can be valid
DEFAULT = class.new.new.freeze
# An example of a faster implementation that could inspire auto-generation
def with(x: DEFAULT, y: DEFAULT)
self.class.new(
x: x.equal?(DEFAULT) ? self.x : x),
y: y.equal?(DEFAULT) ? self.y : y),
)
end
end
```
----------------------------------------
Feature #19000: Data: Add "Copy with changes method" [Follow-on to #16122 Data: simple immutable value object]
https://bugs.ruby-lang.org/issues/19000#change-100364
* Author: RubyBugs (A Nonymous)
* Status: Open
* Priority: Normal
----------------------------------------
*As requested: extracted a follow-up to #16122 Data: simple immutable value object from [this comment](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/109815)*
# Proposal: Add a "Copy with changes" method to Data
Assume the proposed `Data.define` exists.
Seeing examples from the [[Values gem]](https://github.com/ms-ati/Values):
```ruby
# A new class
Point = Data.def(:x, :y)
# An immutable instance
Origin = Point.with(x: 0, y: 0)
# Q: How do we make copies that change 1 or more values?
right = Origin.with(x: 1.0)
up = Origin.with(y: 1.0)
up_and_right = right.with(y: up.y)
# In loops
movements = [
{ x: +0.5 },
{ x: +0.5 },
{ y: -1.0 },
{ x: +0.5 },
]
# position = Point(x: 1.5, y: -1.0)
position = movements.inject(Origin) { |p, move| p.with(**move) }
```
## Proposed detail: Call this method: `#with`
```ruby
Money = Data.define(:amount, :currency)
account = Money.new(amount: 100, currency: 'USD')
transactions = [+10, -5, +15]
account = transactions.inject(account) { |a, t| a.with(amount: a.amount + t) }
#=> Money(amount: 120, currency: "USD")
```
## Why add this "Copy with changes" method to the Data simple immutable value class?
Called on an instance, it returns a new instance with only the provided parameters changed.
This API affordance is now **widely adopted across many languages** for its usefulness. Why is it so useful? Because copying immutable value object instances, with 1 or more discrete changes to specific fields, is the proper and ubiquitous pattern that takes the place of mutation when working with immutable value objects.
**Other languages**
C# Records: “immutable record structs — Non-destructive mutation” — is called `with { ... }`
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-t…
Scala Case Classes — is called `#copy`
https://docs.scala-lang.org/tour/case-classes.html
Java 14+ Records — Brian Goetz at Oracle is working on adding a with copy constructor inspired by C# above as we speak, likely to be called `#with`
https://mail.openjdk.org/pipermail/amber-spec-experts/2022-June/003461.html
Rust “Struct Update Syntax” via `..` syntax in constructor
https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-insta…
## Alternatives
Without a copy-with-changes method, one must construct entirely new instances using the constructor. This can either be (a) fully spelled out as boilerplate code, or (b) use a symmetrical `#to_h` to feed the keyword-args constructor.
**(a) Boilerplate using constructor**
```ruby
Point = Data.define(:x, :y, :z)
Origin = Point.new(x: 0.0, y: 0.0, z: 0.0)
change = { z: -1.5 }
# Have to use full constructor -- does this even work?
point = Point.new(x: Origin.x, y: Origin.y, **change)
```
**(b) Using a separately proposed `#to_h` method and constructor symmetry**
```ruby
Point = Data.define(:x, :y, :z)
Origin = Point.new(x: 0.0, y: 0.0, z: 0.0)
change = { z: -1.5 }
# Have to use full constructor -- does this even work?
point = Point.new(**(Origin.to_h.merge(change)))
```
Notice that the above are not ergonomic -- leading so many of our peer language communities to adopt the `#with` method to copy an instance with discrete changes.
--
https://bugs.ruby-lang.org/
Issue #19000 has been updated by RubyBugs (A Nonymous).
mame (Yusuke Endoh) wrote in #note-17:
> @RubyBugs Please check my comment https://bugs.ruby-lang.org/issues/19000#note-13 . A wrong motivation example raises the suspicion that this API is actually confusing to users.
Quite right @mame, thank you for catching that! I provided two examples. The first example is intended to use the previous values in creating the next values, and that part was broken.
How is this?
```ruby
##
# Example of proposed Data#with
#
# To try this out:
#
# ruby-install ruby-3.2.0-preview3
#
##
Point = Data.define(:x, :y) do
# Example only, too slow. Real implementation should eliminate allocations.
def with(**args)
raise ArgumentError unless args.keys.all? { |k| members.include? k }
self.class.new(**(to_h.merge(args)))
end
end
Origin = Point.new(x: 0, y: 0)
# Q: How do we make copies that change 1 or more values?
right = Origin.with(x: 1.0)
up = Origin.with(y: 1.0)
up_and_right = right.with(y: up.y)
begin
Origin.with(x: 1, z: 0)
rescue ArgumentError
puts "#with(z: 0) raised ArgumentError as expected"
end
# In loops
movements = [
[ :x, +0.5 ],
[ :x, +0.5 ],
[ :y, -1.0 ],
[ :x, +0.5 ],
]
position = movements.inject(Origin) do |p, (field, delta)|
p.with(field => p.to_h[field] + delta)
end
# position = Point(x: 1.5, y: -1.0)
```
----------------------------------------
Feature #19000: Data: Add "Copy with changes method" [Follow-on to #16122 Data: simple immutable value object]
https://bugs.ruby-lang.org/issues/19000#change-100363
* Author: RubyBugs (A Nonymous)
* Status: Open
* Priority: Normal
----------------------------------------
*As requested: extracted a follow-up to #16122 Data: simple immutable value object from [this comment](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/109815)*
# Proposal: Add a "Copy with changes" method to Data
Assume the proposed `Data.define` exists.
Seeing examples from the [[Values gem]](https://github.com/ms-ati/Values):
```ruby
# A new class
Point = Data.def(:x, :y)
# An immutable instance
Origin = Point.with(x: 0, y: 0)
# Q: How do we make copies that change 1 or more values?
right = Origin.with(x: 1.0)
up = Origin.with(y: 1.0)
up_and_right = right.with(y: up.y)
# In loops
movements = [
{ x: +0.5 },
{ x: +0.5 },
{ y: -1.0 },
{ x: +0.5 },
]
# position = Point(x: 1.5, y: -1.0)
position = movements.inject(Origin) { |p, move| p.with(**move) }
```
## Proposed detail: Call this method: `#with`
```ruby
Money = Data.define(:amount, :currency)
account = Money.new(amount: 100, currency: 'USD')
transactions = [+10, -5, +15]
account = transactions.inject(account) { |a, t| a.with(amount: a.amount + t) }
#=> Money(amount: 120, currency: "USD")
```
## Why add this "Copy with changes" method to the Data simple immutable value class?
Called on an instance, it returns a new instance with only the provided parameters changed.
This API affordance is now **widely adopted across many languages** for its usefulness. Why is it so useful? Because copying immutable value object instances, with 1 or more discrete changes to specific fields, is the proper and ubiquitous pattern that takes the place of mutation when working with immutable value objects.
**Other languages**
C# Records: “immutable record structs — Non-destructive mutation” — is called `with { ... }`
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-t…
Scala Case Classes — is called `#copy`
https://docs.scala-lang.org/tour/case-classes.html
Java 14+ Records — Brian Goetz at Oracle is working on adding a with copy constructor inspired by C# above as we speak, likely to be called `#with`
https://mail.openjdk.org/pipermail/amber-spec-experts/2022-June/003461.html
Rust “Struct Update Syntax” via `..` syntax in constructor
https://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-insta…
## Alternatives
Without a copy-with-changes method, one must construct entirely new instances using the constructor. This can either be (a) fully spelled out as boilerplate code, or (b) use a symmetrical `#to_h` to feed the keyword-args constructor.
**(a) Boilerplate using constructor**
```ruby
Point = Data.define(:x, :y, :z)
Origin = Point.new(x: 0.0, y: 0.0, z: 0.0)
change = { z: -1.5 }
# Have to use full constructor -- does this even work?
point = Point.new(x: Origin.x, y: Origin.y, **change)
```
**(b) Using a separately proposed `#to_h` method and constructor symmetry**
```ruby
Point = Data.define(:x, :y, :z)
Origin = Point.new(x: 0.0, y: 0.0, z: 0.0)
change = { z: -1.5 }
# Have to use full constructor -- does this even work?
point = Point.new(**(Origin.to_h.merge(change)))
```
Notice that the above are not ergonomic -- leading so many of our peer language communities to adopt the `#with` method to copy an instance with discrete changes.
--
https://bugs.ruby-lang.org/