Feature #12719
closed`Struct#merge` for partial updates
Description
Other languages have operators for performing partial updates on maps. I feel like Struct could be more useful if it provided an easy way of performing partial (or full) updates.
After the change you can do the following:
Point = Struct.new(:x, :y)
p = Point.new(1, 2)
p2 = p.merge(y: 4)
p3 = p2.merge(x: 10)
p.inspect # => #<struct Point x=1, y=2>
p2.inspect # => #<struct Point x=1, y=4>
p3.inspect # => #<struct Point x=10, y=4>
p.merge!("x" => 9)
p.inspect # => #<struct Point x=9, y=2>
Files
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- Tracker changed from Bug to Feature
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
If this is well received I think a similar syntax could be used for hashes in place of merge.
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File struct_merge.patch added
As an alternative since the | syntax might get shot down. Here's a patch adding a merge function instead:
Point = Struct.new(:x, :y)
p = Point.new(1, 2)
p2 = p.merge(y: 4)
p3 = p2.merge(x: 10)
puts p.inspect # => #<struct Point x=1, y=2>
puts p2.inspect # => #<struct Point x=1, y=4>
puts p3.inspect # => #<struct Point x=10, y=4>
Updated by nobu (Nobuyoshi Nakada) almost 10 years ago
In your example, the value in the LHS is ignored when the same key is present in the RHS hash.
It doesn't feel nice as | operator.
merge sounds nice in that sense, but your patch would segfault at p.merge(0).
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
Thanks, nice catch. I'll update this tomorrow to not segfault.
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File struct_merge_no_segfault.patch added
Updated so it won't segfault
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File merge_bang.patch added
Since merge closely resembles the similar hash function, I think it makes sense to also add merge! as a function. I'm not a fan of the mutating methods, but I would find it surprising if this interface was different. Here's an updated patch. I also fixed some of the documentation I wrote.
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File deleted (
struct_update.patch)
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File deleted (
struct_merge.patch)
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File deleted (
struct_merge_no_segfault.patch)
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File struct_merge.patch struct_merge.patch added
- Subject changed from `Struct#|` for partial updates to `Struct#merge` for partial updates
- Description updated (diff)
Update ChangeLog
Updated by halogenandtoast (Matthew Mongeau) almost 10 years ago
- File deleted (
merge_bang.patch)
Updated by matz (Yukihiro Matsumoto) over 9 years ago
- Status changed from Open to Feedback
I want to see a real-world use-case for the feature.
In addition, I don't think the name merge is the best for the functionality.
Matz.
Updated by Eregon (Benoit Daloze) over 9 years ago
Scala has "copy" for this purpose: some_case_class_object.copy(field: new_value)
Updated by marcotc (Marco Costa) over 4 years ago
To add a real-world use-case: in the ddtrace gem we have a configuration Struct called AgentSettings that holds the global defaults for our configuration:
AgentSettings = Struct.new(
:ssl,
:hostname,
:port,
:timeout_seconds,
:deprecated_for_removal_transport_configuration_proc,
:deprecated_for_removal_transport_configuration_options
) do
def initialize(
ssl:,
hostname:,
port:,
timeout_seconds:,
deprecated_for_removal_transport_configuration_proc:,
deprecated_for_removal_transport_configuration_options:,
)
super(ssl, hostname, port, timeout_seconds, deprecated_for_removal_transport_configuration_proc, \
deprecated_for_removal_transport_configuration_options)
freeze
end
end
But in a few places we want to override some of the default configuration values:
transport.adapter(
default_adapter,
agent_settings.hostname,
agent_settings.port,
# We explicitly use profiling_upload_timeout_seconds instead of agent_settings.timeout because profile
# uploads are bigger and thus we employ a separate configuration.
timeout: profiling_upload_timeout_seconds,
ssl: agent_settings.ssl
)
Today we resort to unpacking the Struct and effectively transferring the values to a hash (in the form of keyword arguments). We don't actually want our Struct to become a hash (or keyword arguments) when we pass it to #adapter in the example above, we would like a AgentSettings struct to be passed but with timeout overridden.