Project

General

Profile

Actions

Feature #12084

open

`Class#instance`

Added by sawa (Tsuyoshi Sawada) over 6 years ago. Updated 5 months ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:73878]

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

Related issues 2 (1 open1 closed)

Related to Ruby master - Feature #12655: Accessing the method visibilityFeedbackActions
Related to Ruby master - Bug #11063: Special singleton class should return true for singleton_class? testRejectedActions

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.

Actions #2

Updated by jeremyevans0 (Jeremy Evans) 7 months ago

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 ObjectSpacefor 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 of String, and must resolve it back to define the memoization on the String 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)).

Actions #10

Updated by Eregon (Benoit Daloze) 2 months ago

  • Related to Bug #11063: Special singleton class should return true for singleton_class? test added
Actions

Also available in: Atom PDF