ml.ruby-lang.org
Sign In Sign Up
Manage this list Sign In Sign Up

Keyboard Shortcuts

Thread View

  • j: Next unread message
  • k: Previous unread message
  • j a: Jump to all threads
  • j l: Jump to MailingList overview

ruby-core

Thread Start a new thread
Download
Threads by month
  • ----- 2025 -----
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2024 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2023 -----
  • December
  • November
  • October
  • September
  • August
  • July
  • June
  • May
  • April
  • March
  • February
  • January
  • ----- 2022 -----
  • December
  • November
ruby-core@ml.ruby-lang.org

December 2022

  • 10 participants
  • 244 discussions
[ruby-core:111371] [Ruby master Feature#19000] Data: Add "Copy with changes method" [Follow-on to #16122 Data: simple immutable value object]
by matz (Yukihiro Matsumoto) 21 Dec '22

21 Dec '22
Issue #19000 has been updated by matz (Yukihiro Matsumoto). `with` seems slightly better than other candidates. `Data` is (or can be considered as) a value object without identity. So names with identity implication (`dup`, `copy`, `clone`) are not ideal. I like `update` but I understand @ko1's concern (`Hash#update` modifies the receiver). Regarding error-on-no-argument, I thought passing no argument (even via double splat `**`) indicates some kind of logic error, and honored fail-early principle, but as @eregon said, it's not a big deal. I am not sure @naruse accepts this last minute change, but I am OK with the last PR from @ufuk. Matz. ---------------------------------------- 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-100745 * 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 require "values" # A new class Point = Value.new(: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) do |p, (field, delta)| p.with(field => p.send(field) + delta) end ``` ## 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/
1 0
0 0
[ruby-core:111366] [Ruby master Feature#13890] Allow a regexp as an argument to 'count', to count more interesting things than single characters
by shan (Shannon Skipper) 21 Dec '22

21 Dec '22
Issue #13890 has been updated by shan (Shannon Skipper). I'd love to have this feature. A `str.count(regexp)` is something I see folk trying fairly often. A `str.count(regexp)` also avoids the intermediary Array of `str.scan(regexp).size` or the back bending with `str.enum_for(:scan, regexp).count`. ---------------------------------------- Feature #13890: Allow a regexp as an argument to 'count', to count more interesting things than single characters https://bugs.ruby-lang.org/issues/13890#change-100741 * Author: duerst (Martin Dürst) * Status: Open * Priority: Normal ---------------------------------------- Currently, String#count only accepts strings, and counts all the characters in the string. However, I have repeatedly met the situation where I wanted to count more interesting things in strings. These 'interesting things' can easily be expressed with regular expressions. Here is a quick-and-dirty Ruby-level implementation: ````Ruby class String alias old_count count def count (what) case what when String old_count what when Regexp pos = -1 count = 0 count += 1 while pos = index(what, pos+1) count end end end ```` Please note that the implementation counts overlapping occurrences; maybe there is room for an option like `overlap: :no`. -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111359] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by Eregon (Benoit Daloze) 21 Dec '22

21 Dec '22
Issue #19078 has been updated by Eregon (Benoit Daloze). A good use-case for `Fiber#storage=`: https://github.com/ruby-concurrency/concurrent-ruby/issues/456#issuecomment… ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100735 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111358] [Ruby master Feature#19000] Data: Add "Copy with changes method" [Follow-on to #16122 Data: simple immutable value object]
by ufuk (Ufuk Kayserilioglu) 20 Dec '22

20 Dec '22
Issue #19000 has been updated by ufuk (Ufuk Kayserilioglu). **Status update** @alanwu and I reworked the PR at https://github.com/ruby/ruby/pull/6766 to ensure the following: 1. Calls to `.with` with no arguments is valid and returns the receiver as-is. As @eregon said, there is no way to differentiate between no args and empty kwargs, so the ability to do `.with(**hash)` freely won the discussion over. 2. The implementation works correctly with Data objects being frozen now. 3. The implementation checks explicitly for keyword arguments and raises argument error for hashes passed as positional argument, as well. Apart from the decision on the name, the PR should be good to merge. As @k0kubun said above, I also feel like this should be a part of the Ruby 3.2 release. If the only blocker is the name, @matz can you pick the final name in the next few days, so that we can ship this with 3.2? Thank you everyone for the discussion and ideas. ---------------------------------------- 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-100734 * 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 require "values" # A new class Point = Value.new(: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) do |p, (field, delta)| p.with(field => p.send(field) + delta) end ``` ## 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/
1 0
0 0
[ruby-core:111357] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by Eregon (Benoit Daloze) 20 Dec '22

20 Dec '22
Issue #19078 has been updated by Eregon (Benoit Daloze). I had call with @ioquatix, we agreed the changes of https://github.com/ruby/ruby/pull/6972 are OK. It's unclear if Fiber#storage and Fiber#storage= need to be allowed to be called for a different Fiber of the same thread (e.g., might be useful for a debugger). One concern is if Fibers become no longer fixed to a given Thread in the future then this condition wouldn't mean anything anymore, and we would have the issue of accessing state being concurrently mutated in another thread (on Rubies without GIL). I think for debugging we should have a new API to "transfer" to a Fiber but not execute any code of that Fiber, just whatever the debugger wants to run there (e.g. get the backtrace, the stack, local variables, get the fiber locals, etc). A bit like `thread N` in `gdb`. ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100733 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111355] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by Eregon (Benoit Daloze) 20 Dec '22

20 Dec '22
Issue #19078 has been updated by Eregon (Benoit Daloze). Eregon (Benoit Daloze) wrote in #note-30: > Also this is a necessary condition to avoid synchronization on Fiber[]/Fiber[]= for non-GIL Rubies. Correction: that is not the case since at least storage cannot be shared between threads. But it's still a problem for all the other reasons. These are fiber-scoped variables, hence it should never be possible to affect a parent fiber by a change in a child fiber (otherwise that just becomes global state). If it's wanted to change something and have all fiber referencing it to see it, it's very easy, just an use an indirection and not the fiber-scoped variable itself. ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100730 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111354] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by Eregon (Benoit Daloze) 20 Dec '22

20 Dec '22
Issue #19078 has been updated by Eregon (Benoit Daloze). Fixes for the above are in https://github.com/ruby/ruby/pull/6972 > Also there is no experimental warning or anything as matz has asked. There is some mention in the docs, but I think that's not enough, especially for `Fiber#storage=` so I added an experimental warning there. ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100729 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111353] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by Eregon (Benoit Daloze) 20 Dec '22

20 Dec '22
Issue #19078 has been updated by Eregon (Benoit Daloze). matz (Yukihiro Matsumoto) wrote in #note-22: > (4) Fiber.new(...,storage: hash|true|false|nil) - accepted, but true/false should be experimental > The feature (4) with true/false are hard to read the intention (true to inherit with dup, false to inherit without dup), so we need experiment here as well. `false` is a violation of everything we discussed (with @byroot etc). It means no more isolation between Fibers, and setting a variable accidentally changes other Fiber storages, leading to tons of confusion. I believe no language does this for very good reasons. I don't know when you added this @ioquatix but that is an unacceptable change to the design long discussed. Also there is no experimental warning or anything as matz has asked. I will remove `storage: false` now, this is the only safe thing to do before the release. Also this is a necessary condition to avoid synchronization on Fiber[]/Fiber[]= for non-GIL Rubies. BTW, Fiber#storage and Fiber#storage= do not check which thread calls them. But a different thread should of course never be allowed to reassign the storage of a Fiber it doesn't belong to. Accessing it also feels wrong. ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100727 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
[ruby-core:111352] [Ruby master Feature#19000] Data: Add "Copy with changes method" [Follow-on to #16122 Data: simple immutable value object]
by Eregon (Benoit Daloze) 20 Dec '22

20 Dec '22
Issue #19000 has been updated by Eregon (Benoit Daloze). k0kubun (Takashi Kokubun) wrote in #note-37: > +1. I'm not sure if it's possible to distinguish them in Ruby, but ideally `data.with` or `data.with()` should be rejected even if we accept `data.with(**changes)` when `changes` are empty. That's not possible to differentiate and also it shouldn't be (it would be very confusing if it was different). > If it's feasible, then it might clear Matz's concern at #note-27. @matz Is it really that big a concern? I think it's far far more important that `with(**changes)` works, whether `changes` is empty or not. Honestly I think there is no point to prevent that, it's necessary for the `with(**changes)` use case and it seems pretty much harmless. ---------------------------------------- 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-100726 * 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 require "values" # A new class Point = Value.new(: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) do |p, (field, delta)| p.with(field => p.send(field) + delta) end ``` ## 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/
1 0
0 0
[ruby-core:111351] [Ruby master Feature#19078] Introduce `Fiber#storage` for inheritable fiber-scoped variables.
by lloeki (Loic Nageleisen) 20 Dec '22

20 Dec '22
Issue #19078 has been updated by lloeki (Loic Nageleisen). for the sake of thoroughness the nodejs one above was inspired by .Net https://learn.microsoft.com/en-us/dotnet/api/system.threading.asynclocal-1?… ---------------------------------------- Feature #19078: Introduce `Fiber#storage` for inheritable fiber-scoped variables. https://bugs.ruby-lang.org/issues/19078#change-100725 * Author: ioquatix (Samuel Williams) * Status: Closed * Priority: Normal * Assignee: ioquatix (Samuel Williams) ---------------------------------------- Pull Request: https://github.com/ruby/ruby/pull/6612 This is an evolution of the previous ideas: - https://bugs.ruby-lang.org/issues/19058 - https://bugs.ruby-lang.org/issues/19062 This PR introduces fiber scoped variables, and is a solution for problems like <https://github.com/ioquatix/ioquatix/discussions/17>. The main interface is: ```ruby Fiber[key] = value Fiber[key] # => value ``` The variables are scoped (local to) a fiber and inherited into child fibers and threads. ```ruby Fiber[:request_id] = SecureRandom.hex(16) Fiber.new do p Fiber[:request_id] # prints the above request id end ``` The fiber scoped variables are stored and can be accessed: ```ruby Fiber.current.storage # => returns a Hash (copy) of the internal storage. Fiber.current.storage= # => assigns a Hash (copy) to the internal storage. ``` Fiber itself has one new keyword argument: ``` Fiber.new(..., storage: hash, false, undef, nil) ``` This can control how the fiber variables are setup in a child context. To minimise the performance overhead of some of the implementation choices, we are also simultaneously implementing <https://bugs.ruby-lang.org/issues/19077>. ## Examples ### Request loop ```ruby Thread.new do while request = queue.pop Fiber.new(storage: {id: SecureRandom.hex(16)}) do handle_request.call(request) end end end ``` OR ```ruby Thread.new do while request = queue.pop Fiber.current.storage = {id: SecureRandom.hex(16)} handle_request.call(request) end end ``` -- https://bugs.ruby-lang.org/
1 0
0 0
  • ← Newer
  • 1
  • ...
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • ...
  • 25
  • Older →

HyperKitty Powered by HyperKitty version 1.3.12.