Feature #12084
open`Class#instance`
Description
For meta-programming/debugging purposes, I would like to request the inverse of Object#singleton_class
. Namely, a method that is called on a class that is a singleton class, and returns the object it is a singleton of. Since the Singleton
module in the standard library http://ruby-doc.org/stdlib-2.3.0/libdoc/singleton/rdoc/Singleton.html assigns the method name instance
to such classes, I think Class#instance
should be the name for such feature.
Array.singleton_class.instance # => Array
"foo".singleton_class.instance # => "foo"
When the receiver is a class but is not a singleton class, then it should raise an error.
Array.instance # => error
Updated by justcolin (Colin Fulton) about 6 years ago
This feature would solve a lot of problems I had while doing what should have been simple meta-programming (if there is such a thing as "simple" meta-programming...).
Something to consider is what happens with nested singleton classes. Let's say you have this code:
Array.singleton_class
.singleton_class
.instance
Does that return Array
or the first singleton class? I would think the former is more useful, but less obvious.
Updated by jeremyevans0 (Jeremy Evans) 7 months ago
- Related to Feature #12655: Accessing the method visibility added
Updated by Eregon (Benoit Daloze) 6 months ago
#instance
seems a too generic name for such a rarely-needed meta-programming feature, I think #singleton_instance
is better.
@justcolin (Colin Fulton) it would result in Array.singleton_class
, otherwise that would break the invariant of #singleton_instance
being the reverse of #singleton_class
.
A concrete use-case would be welcome.
Updated by ufuk (Ufuk Kayserilioglu) 6 months ago
Agreed that instance
is a bad name for this concept and we should not be basing the name on the Singleton
class, since it is not the same kind of singleton in the singleton_class
.
From what I understand this would be exposing the concept of "attached_object" to user code and that concept is clearly documented inside the CRuby source code to mean exactly this:
...
* - attached object: A singleton class knows its unique instance.
* The instance is called the attached object for the singleton class.
...
So a good name would be attached_object
and its behaviour could be:
Array.singleton_class.attached_object # => Array
"foo".singleton_class.attached_object # => "foo"
Array.attached_object # => nil
"foo".attached_object # => nil
Updated by sawa (Tsuyoshi Sawada) 6 months ago
ufuk (Ufuk Kayserilioglu) wrote in #note-4:
Array.attached_object # => nil "foo".attached_object # => nil
That clearly cannot be accepted as a feature because you would then have no way to distinguish whether the class's singleton object is nil
or it does not have a singleton object. Note the following:
NilClass.instance # => nil
Updated by matz (Yukihiro Matsumoto) 6 months ago
-
instance
is NG. For example,Array.instance => nil
is confusing -
attached_object
is better, at least for singleton classes. But there's still no real-world use-case.
For your information, NilClass
is not a singleton class. It's a class with only an instance. This is side evidence of this method is confusing. Even the original proposer can misunderstand the concept.
Matz.
Updated by Eregon (Benoit Daloze) 6 months ago
For your information,
NilClass
is not a singleton class. It's a class with only an instance. This is side evidence of this method is confusing. Even the original proposer can misunderstand the concept.
Interesting, I think what confuses people (including myself) is nil.singleton_class # => NilClass
.
And basically that's because #singleton_class
has special behavior for true/false/nil (special_singleton_class_of
).
Such behavior is not used for any other Ruby value (the rest is TypeError or create a new anonymous singleton class).
It's a bit funny too:
irb(main):002:0> TrueClass.singleton_class?
=> false
irb(main):003:0> true.singleton_class.singleton_class?
=> false
It kind of makes sense given TrueClass
, etc are defined as regular classes, and then creating an extra/separate singleton class would be confusing (methods could be defined on either).
I guess it would be possible to create TrueClass as a singleton class (with superclass Object
) and name it, but that's another proposal and the pros/cons are unclear to me at this stage.
Updated by jemmai (Jemma Issroff) 5 months ago
matz (Yukihiro Matsumoto) wrote in #note-6:
But there's still no real-world use-case.
We have defined the method described here , for MemoWise, a memoization gem. (For what it’s worth, we named it original_class_from_singleton
.)
It’s feasible that someone wants to memoize a method on a singleton class. Here is an example*:
require "memo_wise"
class << String
prepend MemoWise
def example_method
"example"
end
memo_wise :example_method
end
String.example_method
# => “example”
Within MemoWise, we receive the memo_wise(:example_method)
call on the singleton class of String
, and must resolve it back to define the memoization on the String
class itself, not its singleton class. We therefore resolve the original class by searching ObjectSpace
for the class whose singleton class is the one we received:
def self.original_class_from_singleton(klass)
ObjectSpace.each_object(Module).find do |cls|
cls.singleton_class == klass
end
end
I believe this is almost exactly the built in method being proposed.
*It is worth noting that this same functionality could be achieved by the following snippet, instead of opening up the singleton class:
require "memo_wise"
class String
prepend MemoWise
def self.example_method
"example"
end
memo_wise self: :example_method
end
String.example_method
# => "example"
But I think part of the beauty of Ruby is that there are multiple ways to express the same sentiment, and while we allow for opening up the singleton class to do this, it makes sense to me that a gem like MemoWise must support this case, and therefore a method as defined in this issue does have a real world use case. It’s also worth noting that a different design decision in MemoWise might make the need go away soon for MemoWise specifically.
Updated by Eregon (Benoit Daloze) 5 months ago
jemmai (Jemma Issroff) wrote in #note-8:
Within MemoWise, we receive the
memo_wise(:example_method)
call on the singleton class ofString
, and must resolve it back to define the memoization on theString
class itself, not its singleton class.
That's not clear to me, why do you need to access String? Can't the memoization be stored on the singleton class (String.singleton_class)?
Modules/classes are always "unique", there are no two same instances, so it seems always fine to store the data on the class/module holding/owning (Method#owner
) the method (the module/class for which method_defined?(name)
is true/has it in instance_methods
).
# * Performance concern: searches all Class objects # But, only runs at load time and results are memoized
BTW, it (internally) iterates all objects on most Ruby implementations, not just modules/classes, the filtering is typically done on top because there is no "next instance pointer" (e.g., unlike in some Smalltalk).
ObjectSpace.each_object
is a clear no-no when anyone cares about the time to load a library/use it during startup (it's O(nb of objects in heap at the time of call)
).
Updated by Eregon (Benoit Daloze) 2 months ago
- Related to Bug #11063: Special singleton class should return true for singleton_class? test added