ruby picture

RCR 231: Kernel#singleton_class

Submitted by dblack (Wed Mar 17 19:03:38 UTC 2004)

Abstract

Add new method, Kernel#singleton_class, which is the same as #class but returns the object's singleton class

Problem

Experience shows that people have enormous trouble understanding the concept of singleton classes. I think part of the problem is lack of symmetry. When discussing regular classes, you can talk about the #class method; but the equivalent, for singleton classes, is "(class << self; self; end)".

I have nothing against the << syntax. But I think there's something missing: namely, something which allows people to understand easily the fact that singleton classes are classes.

Proposal

Addition of Kernel#singleton_class method. This will eliminate the need to do:
(class << obj; self; end).class_eval ...

as you will be able to do:

obj.singleton_class.class_eval

and so forth.

Analysis

This method can be implemented easily on the fly, but based on several years of watching people struggle with the class << obj syntax (and with singleton classes in general), I believe there's good reason to provide this companion method to #class for every object.

Implementation

module Kernel
  def singleton_class
     class << self; self; end
  end
end
ruby picture
Comments Current voting

It might be that Kernel#self_class might be more descriptive and also help prevent confusion between singleton classes and Singleton classes. If we're going to be making this essential feature of Ruby easy to use, then we might as well try reprogramming the Ruby community as well to make this feature clearer and easier to use and understand, too. --Austin Ziegler


Hi --

I don't think the singleton/Singleton thing will go away that easily -- unless Singleton gets renamed. self_class doesn't really express what the method is doing; it's so similar to self.class, and any explanation of why would probably include discussion of [Ss]ingleton anyway. It gives you an object's singleton class, just as instance_methods gives you its instance methods, etc., and I think in the long run that's the clearest way.

-- David Black


Well, it was a thought. Maybe an RCR for Singleton to be renamed to SingletonPattern? Yes, it will break things, but it's a matter of implementing a Pattern. --Austin Ziegler


I would suggest a more rubyesque implementation.


module Kernel
  def singleton_class(&block)
    s = class << self
      self
    end
    s.class_eval(&block) if block
    s
  end
end

-- Matthias Georgi


I'd rather keep it strictly parallel to, and congruent with, #class. One of the main goals of this RCR is to put an object's class and singleton class in as close to the same conceptual and semantic category as possible, so any special casing is at cross purposes with the goal. (I also would not agree that the block version is more rubyesque, but that's like debating readability or intuitiveness -- i.e., more or less pointless :-)

-- David Black


Oh, I overlooked this parallelism, so I agree with you. I'm just not clear about metaclasses, they are the singleton classes of classes. Are they different in an import way from singleton classes of objects ? (I think they are created at class definition time, but maybe that makes no different in usage, I guess.)

-- Matthias Georgi


IMHO there is another reasonable implementation of singleton_class:

module Kernel
  def singleton_class
    if has_any_singleton_method?
      class << self; self; end
    else
      nil
    end
  end
end

That way one can easily recognize whether this instance has any per instance methods defined.

Btw: Why did you suggest to put it into Kernel, IMHO Object is the more appropriate place to put it.

-- Robert Klemme


The object can have a singleton class without having any singleton methods. If you want to return nil if the object has no singleton class, then you need:

  module Kernel
    def singleton_class
      if has_singleton?
        class << self; self; end
      else
        nil
      end
    end
  end

Where singleton? cannot be written in Ruby, but must be written in C as:

  VALUE has_singleton(VALUE self)
  {
    return FL_TEST(RBASIC(self)->klass, FL_SINGLETON) ? Qtrue : Qfalse;
  }

Also, note that returning nil breaks David's original use of singleton_class, since you can now no longer write:

  obj.singleton_class.class_eval

if the object has no singleton class. A parameter could be added to create the singleton class if it doesn't exist, or the two could be split into bang and non-bang versions, but I don't like either solution. Better IMO would be to have singleton_class always return a singleton class (creating it if it doesn't exist) and add a method to determine if an object currently has a singleton class.

-- Paul Brannan


I'd personally prefer #instance_class over #singleton_class, but either addition would be a boon to the language, IMO.


Yes, this should definitely go in.

However, I too am a little concerned about the name. While singleton classes are technically singletons (since they only ever have one instance), the term ``singleton class'' might be a little bit more confusing than necessary. It _is_ the current Ruby term, so the method should definitely go in under the name #singleton_class, but maybe it should have an alias? (Certainly, trying to make room by renaming the Singleton class would turn out to be even more confusing, since the term ``singleton'' is well-established throughout the object-oriented community.)

The suggested name #instance_class avoids the singleton mess, but creates a sizable mess of its own. You run into problems like this:

  class Object
    def instance_class
      class << self ; self end
    end
    def define_instance_method name, &body
      instance_class.class_eval { define_method name, &body }
    end
  end

Here you have a situation where defining an ``instance method'' (the natural choice of term for a method of the ``instance class'') on a class is not reflected in the Class#instance_methods collection. The problem, of course, is the unavoidable confusion that arises because classes both _produce_ instances and _are_ themselves instances.

The name #instance_class thus ends up being just as confusing as #singleton_class. Another obvious candidate is #private_class, which is, again, just as confusing (I don't think I have to be any more specific here).

The ideal solution would be to choose a name that avoids overloading already-established terms like ``singleton,'' ``instance,'' and ``private''. We could just call it Object#own_class --- after all, singleton classes are really a just way to give any particular object its very _own_ class. As it happens, I think this name is rather evocative and pedagogical. And it does indeed give you more freedom of expression with respect to ambiguity:

  class Object
    def own_class
      class << self ; self end
    end
    def define_own_method name, &body
      own_class.class_eval { define_method name, &body }
    end
  end

No confusion in sight there.

I'm slowly starting to realize that I lack a definitive point to make here, so I'll end this.

In any case, I do insist that the alias #singleton_class be put in. I personally prefer that name over both #instance_class and #own_class, and it seems unlikely that anyone should really be confused by the terminology overlap, once they fully understand both concepts.

Voting strongly in favor.


I wrote that, for the record.

-- Daniel Brockman


I think it might even make sense to add two methods instead of one. One that returns nil if the object has no singleton class yet and one that creates a new singleton class for it and returns that.

The first is neccessary so that no useless singleton classes get created, and that you can test whether a singleton class exists. The second is needed so that you can easily create a singleton class (and singleton methods) of an object without the class << syntax.


-- Zsban Ambrus


Hi Zsban --

Under what circumstances would you care whether a singleton class exists if you're not going to use it?

David Black


Strongly opposed 1
Opposed 0
Neutral 0
In favor 8
Strongly advocate 26
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .