[ruby-core:124721] [Ruby Feature#6478] BasicObject#__class__
Issue #6478 has been updated by trinistr (Alexander Bulancov). I would like to revive discussion for this feature request as I feel that `BasicObject#__class_` is still needed. Note: obviously, it is possible to transplant methods with `define_method` (that is very nice!), but one doesn't always control the class of the object. **What for?** - Inspection of objects. Currently, it's not possible to print anything reasonable for a BasicObject-derived object. - Introspection. Everything in Ruby is an object with a class, and not being able to get that class is weird and painful. It's possible to use `Kernel.instance_method(:class).bind_call(o)`, but why is this even required? It's very un-ergonomical. **Why?** 1. Printing of classes is especially useful for error messages, and Ruby's internals can just do that (though maybe this is also just `Kernel#class`?): ```ruby class A < BasicObject; end 1.clone(freeze: A.new) # in 'Numeric#clone': unexpected value for freeze: A (ArgumentError) ``` Creating such messages in regular code is unnecessarily hard when any object can happen, it requires conditionals and/or using the `bind_call` trick. 2. Getting a class of an object is probably mostly useful for interactive use (which is still important), but the long incantation to do it is inconvenient and hard to remember as something that can be done. 3. `bind_call` is an order of magnitude slower than just using the method: ```ruby class B < BasicObject; define_method(:__class__, ::Kernel.instance_method(:class)); end b = B.new class_method = Kernel.instance_method(:class) Benchmark.ips { |x| x.report("instance_method.bind_call") { Kernel.instance_method(:class).bind_call(b) } x.report("bind_call") { class_method.bind_call(b) } x.report("__class__") { b.__class__ } } ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [x86_64-linux] Warming up -------------------------------------- instance_method.bind_call 107.359k i/100ms bind_call 119.100k i/100ms __class__ 2.187M i/100ms Calculating ------------------------------------- instance_method.bind_call 1.022M (± 3.3%) i/s (978.28 ns/i) - 5.153M in 5.046952s bind_call 1.147M (± 4.6%) i/s (871.47 ns/i) - 5.836M in 5.096893s __class__ 22.494M (± 3.6%) i/s (44.46 ns/i) - 113.699M in 5.062084s ``` **Compatibility** I believe virtually no code would be negatively impacted by addition of `BasicObject#__class__`. Code would need to both check for the presence of `#__class__` and do something completely divorced from `#class` to be affected. On the other hand, it would allow for a method that doesn't conflict with a keyword. Searching for uses of `__class__` on GitHub (https://github.com/search?q=__class__+language%3Aruby&ref=searchresults&type=code&utf8=%E2%9C%93) seems to show just a few current uses: - defining `__class__` to refer to the class of a proxy object itself - `alias __class__ class`, seemingly to call directly instead of `self.class` - Rubinius has it (https://github.com/rubinius/rubinius/blob/84368419a49767ef9549a5778812e5f54b...) - Pythonification There are some results that define `__class__` if it doesn't exist already, but those should not be impacted, as the meaning would be the same. **In short** Adding `BasicObject#__class__` would - make it possible to always get the class for any object in the same way; - be faster than current solution; - have no negative impact on existing codebase. ---------------------------------------- Feature #6478: BasicObject#__class__ https://bugs.ruby-lang.org/issues/6478#change-116318 * Author: trans (Thomas Sawyer) * Status: Feedback * Assignee: matz (Yukihiro Matsumoto) ---------------------------------------- How else is one supposed to get the class of a subclass of BasicObject? -- https://bugs.ruby-lang.org/
participants (1)
-
trinistr (Alexander Bulancov)