From: "zverok (Victor Shepelev) via ruby-core" Date: 2022-12-30T10:56:06+00:00 Subject: [ruby-core:111527] [Ruby master Bug#19278] Constructing subclasses of Data with positional arguments Issue #19278 has been updated by zverok (Victor Shepelev). > I do this all the time with structs. I assumed Data.define would basically just be a read-only Struct.new and that's how I got here. (Wrote a long theoretical answer, deleted it!) Wait. I now reread your initial example more attentively, and I see the source of the confusion now: that we are _adjusting_ the `initialize` to accept the arguments the initial class knew nothing about (not redefining it to accept the same args, but differently), and the behavior is indeed hard to grasp immediately. I am not sure something can be done about it in a consistent manner, though. That's a source of the conveniences that are provided in other cases (I think for "simple data classes" ability to be initialized by both keyword and positional argument is crucial for their adoption; and I still believe that the convention of "initialize is easy to redefine, but it should always accept keywords" is a good enough compromise). It seems that "completely redefine what data is accepted to constructor" would be a natural limitation of `Data`, which seems more or less suitable (inheriting to provide some convenience methods is one thing, building an inheritance chain is another). The only way I see so far to make your `Baz` work is this: ```ruby Foo = Data.define class Bar < Foo def self.new(foo) = super(foo:) # convert positional to keyword def initialize(foo:) = p foo # ...then they are safely passed to initialize end Bar.new(1) #=> 1 ``` BTW, note that before 3.2, for structs it was either `keyword_init: false`, or `keyword_init: true`, not both. That's why you were able to easily redefine `initialize`: you _knew_ it receives either keywords or positionals, not "both and need to choose". After 3.2, it is different, and I think that people would stumble on that many times (the design decision was made differently, too, unlike Data): ```ruby S = Struct.new(:x) do def initialize(*a, **kwa) puts "a=#{a}, kwa=#{kwa}" end end S.new(1) # a=[1], kwa={} S.new(x: 1) # a=[], kwa={:x=>1} ``` I think it will cause a fair share of confusion in completely different ways than the design choice for `Data` did :) ---------------------------------------- Bug #19278: Constructing subclasses of Data with positional arguments https://bugs.ruby-lang.org/issues/19278#change-100891 * Author: tenderlovemaking (Aaron Patterson) * Status: Feedback * 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/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-core.ml.ruby-lang.org/