Issue #16122 has been updated by zverok (Victor Shepelev).


> What would a simple implementation of `Struct::Value.new` look like in Ruby code?

Oh, I've probably answered the wrong question... But I am not quite sure I understand yours.

Theoretically, it is just something like this (ignoring the fact that `Struct`s implementation has optimized storage and other tricks, and any input validation):

```ruby
class Struct::Value
  def self.new(*args, keyword_init: false)
    name, *members = args.first.is_a?(String) ? args : [nil, *args]
    Class.new(self) do
      @members = members

      def self.new(*args)
        allocate.tap { |o| o.__send__(:initialize, *args) }
      end

      members.each { |m| define_method(m) { instance_variable_get("@#{m}") }}
    end.tap { |cls| const_set(name, cls) if name }
  end
# ....
```

So, (if that's what you've asking) it produces object of different class, `Struct::Value`, unrelated to `Struct`, but sharing most of the implementation.

----------------------------------------
Feature #16122: Struct::Value: simple immutable value object
https://bugs.ruby-lang.org/issues/16122#change-80970

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
Currently, **Struct** behaves like a *value object*:
* it is created from simple attributes;
* its equality and representation based only on those attributes.

But also, it behaves like a **mutable object** (allows to set attributes), and **as a kind-of collection** (includes `Enumerable`, has `to_a` and `[]` accessors).

And while `Struct` is kinda useful as is, I found that in a lot of cases what I really *mean* creating a Struct is just creating a pure, immutable value object, that is *just it*. There are a lot of gems that go this or that far to provide "value object" concept, but in fact, the concept is so simple that typically nobody will install the gem to just have it -- that's why I believe it should be in language core.

So, here is the proposal **and its C implementation:**

* Class `Struct::Value`, which shares most of the implementation with a `Struct` (but is neither subclass nor superclass of it);
* Unlike struct, it is: 
  * Not Enumerable;
  * Immutable;
  * Doesn't think of itself as "almost hash" (doesn't have `to_a`, `values` and `[]` methods);
  * Can have empty members list (fun fact: `Struct.new('Foo')` creating member-less `Struct::Foo`, is allowed, but `Struct.new()` is not) to allow usage patterns like:

```ruby
class MyService
  Success = Struct::Value.new(:results)
  NotFound = Struct::Value.new
end
```
`NotFound` here, unlike, say, `Object.new.freeze` (another pattern for creating "empty typed value object"), has nice inspect `#<value NotFound>`, and created consistently with the `Success`, making the code more readable.

---Files--------------------------------
struct_value.patch (18.6 KB)


-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>