ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:124721] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
@ 2026-02-08 22:55 ` trinistr (Alexander Bulancov) via ruby-core
  2026-02-09  5:22 ` [ruby-core:124723] " jneen (Jeanine Adkisson) via ruby-core
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 7+ messages in thread
From: trinistr (Alexander Bulancov) via ruby-core @ 2026-02-08 22:55 UTC (permalink / raw)
  To: ruby-core; +Cc: trinistr (Alexander Bulancov)

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/84368419a49767ef9549a5778812e5f54b6c6223/core/alpha.rb#L146)
- 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124723] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
  2026-02-08 22:55 ` [ruby-core:124721] [Ruby Feature#6478] BasicObject#__class__ trinistr (Alexander Bulancov) via ruby-core
@ 2026-02-09  5:22 ` jneen (Jeanine Adkisson) via ruby-core
  2026-02-09 10:05 ` [ruby-core:124734] " Eregon (Benoit Daloze) via ruby-core
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 7+ messages in thread
From: jneen (Jeanine Adkisson) via ruby-core @ 2026-02-09  5:22 UTC (permalink / raw)
  To: ruby-core; +Cc: jneen (Jeanine Adkisson)

Issue #6478 has been updated by jneen (Jeanine Adkisson).


I wonder if exposing a static `Object.class_of(thing)` would be appropriate? There's also the singleton_class to consider as well.

----------------------------------------
Feature #6478: BasicObject#__class__
https://bugs.ruby-lang.org/issues/6478#change-116321

* 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124734] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
  2026-02-08 22:55 ` [ruby-core:124721] [Ruby Feature#6478] BasicObject#__class__ trinistr (Alexander Bulancov) via ruby-core
  2026-02-09  5:22 ` [ruby-core:124723] " jneen (Jeanine Adkisson) via ruby-core
@ 2026-02-09 10:05 ` Eregon (Benoit Daloze) via ruby-core
  2026-02-09 13:26 ` [ruby-core:124735] " byroot (Jean Boussier) via ruby-core
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 7+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2026-02-09 10:05 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #6478 has been updated by Eregon (Benoit Daloze).


In terms of naming I think `__class__` looks like a hack that Python would use and not Ruby-like.

Why not add `BasicObject#class`?

I think `Kernel.instance_method(:class).bind_call(o)` is fine for most cases.

FWIW performance-wise on TruffleRuby there isn't much difference when storing the `UnboundMethod` in a constant:
```
truffleruby 33.0.1 (2026-01-20), like ruby 3.3.7, Oracle GraalVM Native [x86_64-linux]
Calculating -------------------------------------
instance_method.bind_call     33.492M (± 3.0%) i/s   (29.86 ns/i) -    169.962M in   5.079892s
                bind_call    631.582B (± 1.1%) i/s    (0.00 ns/i) -      3.157T in   4.998908s
                __class__    750.398B (± 1.3%) i/s    (0.00 ns/i) -      3.751T in   4.999106s
```

BasicObject are very difficult to use because they miss so many "standard" methods from Kernel.
As such I see them as mostly meant to proxy all the Kernel methods to maybe some delegate object or so, and the responsability of the BasicObject subclass to implement Kernel-like methods to make the object usable in normal Ruby code.

----------------------------------------
Feature #6478: BasicObject#__class__
https://bugs.ruby-lang.org/issues/6478#change-116335

* 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124735] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
                   ` (2 preceding siblings ...)
  2026-02-09 10:05 ` [ruby-core:124734] " Eregon (Benoit Daloze) via ruby-core
@ 2026-02-09 13:26 ` byroot (Jean Boussier) via ruby-core
  2026-02-09 16:17 ` [ruby-core:124738] " Eregon (Benoit Daloze) via ruby-core
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 7+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2026-02-09 13:26 UTC (permalink / raw)
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #6478 has been updated by byroot (Jean Boussier).


Not that I'm for nor against this feature request, but:

> In terms of naming I think __class__ looks like a hack that Python would use and not Ruby-like.

I mean, it's consistent with `__send__` and `__id__`, aliases of `send` and `object_id` meant to be used by generic code that want to ensure it can deal with any object, even ones that would redefine common methods.

> Why not add BasicObject#class?

I suspect that would break various proxy classes in gems. Might not be a huge deal.



----------------------------------------
Feature #6478: BasicObject#__class__
https://bugs.ruby-lang.org/issues/6478#change-116336

* 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124738] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
                   ` (3 preceding siblings ...)
  2026-02-09 13:26 ` [ruby-core:124735] " byroot (Jean Boussier) via ruby-core
@ 2026-02-09 16:17 ` Eregon (Benoit Daloze) via ruby-core
  2026-02-10  7:58 ` [ruby-core:124755] " byroot (Jean Boussier) via ruby-core
  2026-02-13 19:03 ` [ruby-core:124820] " trinistr (Alexander Bulancov) via ruby-core
  6 siblings, 0 replies; 7+ messages in thread
From: Eregon (Benoit Daloze) via ruby-core @ 2026-02-09 16:17 UTC (permalink / raw)
  To: ruby-core; +Cc: Eregon (Benoit Daloze)

Issue #6478 has been updated by Eregon (Benoit Daloze).


byroot (Jean Boussier) wrote in #note-18:
> I mean, it's consistent with `__send__` and `__id__`, aliases of `send` and `object_id` meant to be used by generic code that want to ensure it can deal with any object, even ones that would redefine common methods.

Yeah, I dislike those too 😅
Specifically I think it looks very unidiomatic (and ugly) when used in Ruby code.

I think at least it's clear it's not good to go towards having every `Kernel` method as a `__x__` variant on BasicObject.
Also even `__send__` and `__id__` can be redefined, so the `KERNEL_CLASS = Kernel.instance_method(:class); KERNEL_CLASS.bind_call(obj)` approach is in fact safer.

So my take on this is `KERNEL_CLASS = Kernel.instance_method(:class); KERNEL_CLASS.bind_call(obj)` is good enough,
and also most BasicObject subclasses should implement Kernel-like methods.

Concrete examples from real code where this is not good enough would most likely be helpful to move this forward if people want that.

----------------------------------------
Feature #6478: BasicObject#__class__
https://bugs.ruby-lang.org/issues/6478#change-116339

* 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124755] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
                   ` (4 preceding siblings ...)
  2026-02-09 16:17 ` [ruby-core:124738] " Eregon (Benoit Daloze) via ruby-core
@ 2026-02-10  7:58 ` byroot (Jean Boussier) via ruby-core
  2026-02-13 19:03 ` [ruby-core:124820] " trinistr (Alexander Bulancov) via ruby-core
  6 siblings, 0 replies; 7+ messages in thread
From: byroot (Jean Boussier) via ruby-core @ 2026-02-10  7:58 UTC (permalink / raw)
  To: ruby-core; +Cc: byroot (Jean Boussier)

Issue #6478 has been updated by byroot (Jean Boussier).


> So my take on this is KERNEL_CLASS = Kernel.instance_method(:class); KERNEL_CLASS.bind_call(obj) is good enough,

Yeah me too, I was just mentioning the existence of `__id__` etc. But I agree that when code need to work with any arbitrary object, like e.g. `zeitwerk` or `tapioca`, then `UnboundMethod` is the safe way.

That being said it's unfortunate that it doesn't perform better on CRuby. I just had a look and It does allocate a `Class` (at least for module methods) and a `method_entry_t`, which is quite heavy for a call like `#class`.

----------------------------------------
Feature #6478: BasicObject#__class__
https://bugs.ruby-lang.org/issues/6478#change-116361

* 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [ruby-core:124820] [Ruby Feature#6478] BasicObject#__class__
       [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
                   ` (5 preceding siblings ...)
  2026-02-10  7:58 ` [ruby-core:124755] " byroot (Jean Boussier) via ruby-core
@ 2026-02-13 19:03 ` trinistr (Alexander Bulancov) via ruby-core
  6 siblings, 0 replies; 7+ messages in thread
From: trinistr (Alexander Bulancov) via ruby-core @ 2026-02-13 19:03 UTC (permalink / raw)
  To: ruby-core; +Cc: trinistr (Alexander Bulancov)

Issue #6478 has been updated by trinistr (Alexander Bulancov).


jneen (Jeanine Adkisson) wrote in #note-16:
> I wonder if exposing a static `Object.class_of(thing)` would be appropriate? There's also the singleton_class to consider as well.

This is an interesting idea. It would solve the problems around this, in a simpler way than an `UnboundMethod`. But it defintely feels off for Ruby, though we have some methods like that (`Regexp.linear_time?` comes to mind).


Eregon (Benoit Daloze) wrote in #note-19:
> I think at least it's clear it's not good to go towards having every `Kernel` method as a `__x__` variant on BasicObject.
> Also even `__send__` and `__id__` can be redefined, so the `KERNEL_CLASS = Kernel.instance_method(:class); KERNEL_CLASS.bind_call(obj)` approach is in fact safer.
> 
> So my take on this is `KERNEL_CLASS = Kernel.instance_method(:class); KERNEL_CLASS.bind_call(obj)` is good enough,
> and also most BasicObject subclasses should implement Kernel-like methods.
> 
> Concrete examples from real code where this is not good enough would most likely be helpful to move this forward if people want that.

I agree, porting all methods from Kernel is unreasonable. It's just that having access to object's class is fundamental and would provide replacements for many generic methods (especially `methods` and co). In my experience, `__class__` would be much more useful than `__id__`, for example.

IMHO, redefining basic methods is not a compelling argument, `Kernel#class` could be redefined too.
On the other hand, `#class` being redefined [happens](https://github.com/search?q=%2Fdef+class%5Cb%2F+language%3Aruby&ref=searchresults&type=code&utf8=%E2%9C%93) more often than for `#__class__`.

> `KERNEL_CLASS = Kernel.instance_method(:class)`

This looks even less Ruby-like to me than a silly method name :p
Also, due to constant resolution, placing such constants requires extra consideration or littering the code with them.

> Concrete examples from real code where this is not good enough would most likely be helpful to move this forward if people want that.

```ruby
Ractor.shareable?(Kernel.instance_method(:class)) # => false
```
So Ractor-compatible code can not use such a constant (relevant #17513 is still open).

And just basic stuff like this would be much easier to write:
```ruby
def my_chunkers(collection)
  raise ArgumentError, "#{collection.__class__} is not enumerable" unless Enumerable === collection
  collection.chunk(&:itself)
end
```

byroot (Jean Boussier) wrote in #note-18:
> > Why not add BasicObject#class?
> 
> I suspect that would break various proxy classes in gems. Might not be a huge deal.

That's that I thought, too, there probably is code relying on `class` being proxied, otherwise I would push for that name.


----------------------------------------
Feature #6478: BasicObject#__class__
https://bugs.ruby-lang.org/issues/6478#change-116441

* 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/
______________________________________________
 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/lists/ruby-core.ml.ruby-lang.org/

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-02-13 19:03 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <redmine.issue-6478.20120522220542.137@ruby-lang.org>
2026-02-08 22:55 ` [ruby-core:124721] [Ruby Feature#6478] BasicObject#__class__ trinistr (Alexander Bulancov) via ruby-core
2026-02-09  5:22 ` [ruby-core:124723] " jneen (Jeanine Adkisson) via ruby-core
2026-02-09 10:05 ` [ruby-core:124734] " Eregon (Benoit Daloze) via ruby-core
2026-02-09 13:26 ` [ruby-core:124735] " byroot (Jean Boussier) via ruby-core
2026-02-09 16:17 ` [ruby-core:124738] " Eregon (Benoit Daloze) via ruby-core
2026-02-10  7:58 ` [ruby-core:124755] " byroot (Jean Boussier) via ruby-core
2026-02-13 19:03 ` [ruby-core:124820] " trinistr (Alexander Bulancov) via ruby-core

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).