Why Isn't the Eigenclass Equivalent to Self.Class, When It Looks So Similar

Why isn't the eigenclass equivalent to self.class, when it looks so similar?

class << self is more than just a way of declaring class methods (though it can be used that way). Probably you've seen some usage like:

class Foo
class << self
def a
print "I could also have been defined as def Foo.a."
end
end
end

This works, and is equivalent to def Foo.a, but the way it works is a little subtle. The secret is that self, in that context, refers to the object Foo, whose class is a unique, anonymous subclass of Class. This subclass is called Foo's eigenclass. So def a creates a new method called a in Foo's eigenclass, accessible by the normal method call syntax: Foo.a.

Now let's look at a different example:

str = "abc"
other_str = "def"

class << str
def frob
return self + "d"
end
end

print str.frob # => "abcd"
print other_str.frob # => raises an exception, 'frob' is not defined on other_str

This example is the same as the last one, though it may be hard to tell at first. frob is defined, not on the String class, but on the eigenclass of str, a unique anonymous subclass of String. So str has a frob method, but instances of String in general do not. We could also have overridden methods of String (very useful in certain tricky testing scenarios).

Now we're equipped to understand your original example. Inside Foo's initialize method, self refers not to the class Foo, but to some particular instance of Foo. Its eigenclass is a subclass of Foo, but it is not Foo; it couldn't be, or else the trick we saw in the second example couldn't work. So to continue your example:

f1 = Foo.new(:weasels)
f2 = Foo.new(:monkeys)

f1.weasels = 4 # Fine
f2.monkeys = 5 # Also ok
print(f1.monkeys) # Doesn't work, f1 doesn't have a 'monkeys' method.

Hope this helps.

What is self in class self and why is this different from the Class instance that is defining this block of code?

Why is self not the same as MyClass inside of the class << self scope.

Because the class keyword always changes the scope:

class MyClass
puts self #=> MyClass

class <<self
puts self #=>MyClass’s singleton class
end
end

More specifically, why is the method delegate not available to self
inside of class << self?

class MyClass

def self.delegate
puts "executing MyClass.delegate()"
end

class <<self
delegate
end

end

--output:--
1.rb:8:in `singleton class': undefined local variable or method `delegate' for #<Class:MyClass> (NameError)
from 1.rb:7:in `<class:MyClass>'
from 1.rb:1:in `<main>'

Note that the following constructs are equivalent:

class MyClass

def self.delegate
puts "executing MyClass.delegate()"
end

end

MyClass.delegate

--output:--
executing MyClass.delegate()

and:

class MyClass

class <<self
def delegate
puts "executing MyClass.delegate()"
end
end

end

MyClass.delegate

--output:--
executing MyClass.delegate()

Therefore, your code is equivalent to:

class MyClass

class <<self
def delegate
puts "executing MyClass.delegate()"
end

delegate
end

end

If you ignore the outer MyClass for a moment, then you defined a class like this:

class <<self
def delegate
puts "executing MyClass.delegate()"
end

delegate
end

That same structure can be replicated like this:

class Dog
def bark
puts “woof”
end

bark
end

which will produce the same type of error:

1.rb:7:in `<class:Dog>': undefined local variable or method `bark' for Dog:Class (NameError)
from 1.rb:1:in `<main>'
  1. When you call a method and you don't specify a receiver, ruby uses whatever object is currently assigned to the self variable as the receiver.

  2. Inside a method, ruby assigns the object that called the method to the self variable. The object that called the method is not the same thing as the class (object) in which the method is defined.

  3. Inside a class, but outside of any method definitions, ruby assigns the class (object) to self.

Note that it is the instances of the Dog class that can call the def's inside the Dog class, e.g. bark(). Similarly, it is the instances of the singleton class that can call the def's inside the singleton class, e.g. delegate()--the singleton class itself cannot call the def's inside the singleton class. The whole reason they are called singleton classes is because singleton classes have only one instance--in your case the one instance of the singleton class is MyClass. As a result, MyClass can call delegate(), but the singleton class cannot call delegate().

I don't understand really what a class method on an eignclass is
however.

Personally, I don't use the term eigenclass. In my opinion, ruby has made a decision that the term is singleton class. If you look through the docs for the Object class, there are no method names that have eigenclass in them, yet there are method names with singleton class in them.

All objects have a singleton class. A singleton class is an object. Therefore, every singleton class also has a singleton class--which means that the chain of singleton classes is infinite:

class Dog
end

s1 = Dog.singleton_class
puts s1

s2 = s1.singleton_class
puts s2

s3 = s2.singleton_class
puts s3

--output:--
#<Class:Dog>
#<Class:#<Class:Dog>>
#<Class:#<Class:#<Class:Dog>>>

Which means you can do stuff like this:

class Dog
class <<self #s1
class <<self #s2
def greet #Instances of s2 can call greet, and the only instance of s2 is s1.
puts "hello"
end
end
end
end


class Dog
class <<self
#Inside here self = Dog's singleton class = s1
greet #equivalent to Dogs_singleton_class.greet
end
end

--output:--
hello

However, I've never seen anyone use a singleton class of a singleton class (s2) before in their code. I did it once to answer a question a long, long time ago, and nobody had any idea what I was talking about.

There are some method lookup diagrams here, which might prove useful.

Knowing if a class is an eigenclass

(Prior to Ruby 2.0) The following expression evaluates to true if an only if the object x is an eigenclass:

Class === x && x.ancestors.first != x

The === equality check asserts that x is an instance of the Class class, the != inequality check uses the fact that the ancestors introspection method "skips" eigenclasses.
For objects x that are instances of the Object class (i.e. x is not a blank slate object), the Class === x check is equivalent to x.is_a? Class or, in this particular case, to x.instance_of? Class.

Starting with Ruby 2.0, the above expression is not sufficient to detect eigenclasses since it evaluates to true also for classes that have prepended modules. This can be solved by an additional check that x.ancestors.first is not such a prepended module e.g. by Class === x.ancestors.first. Another solution is to modify the whole expression as follows:

Class === x && !x.ancestors.include?(x)

class methods and different styles of programming in ruby

I think the difference between those is just style. They both add a method to the singleton class of the class. Here is what I did with your code to investigate it:

class Jt
class << self
def say_hello
puts "I want to say hello"
end
end
end

class Jt2
def self.say_hello
puts "2 want to say hello"
end
end

p Jt.singleton_class.instance_method(:say_hello) # => #<UnboundMethod: #<Class:Jt>#say_hello>
p Jt2.singleton_class.instance_method(:say_hello) # => #<UnboundMethod: #<Class:Jt2>#say_hello>

In case it matters, I used JRuby.

What's the difference between a class and the singleton of that class in Ruby?

To answer your question directly: Module#define_method creates an instance method. A "class method" is an instance method on the singleton class (or eigenclass) of a Class object. I'm sure that sounds very confusing. Let me explain why Ruby includes the concept of "singleton classes" in the first place:

First, let me say that the basic "framework" of different object-oriented languages are quite varied. Ruby's design as regards objects, classes, metaclasses, etc. is by no means the only possible one, and the language could have been designed in a different way. Having said that, there are logical reasons why Ruby works the way it does. I'll try to explain as concisely as possible...

Think of a simple method call, like:

[1,2,3].first

Here we are calling a method called first, with an Array object as receiver. To process this method call, Ruby needs to search for a matching method definition, and execute it. Where does it start looking? Naturally, in the instance methods of Array. If it doesn't find it there, it will look in Array's superclass, then the superclass of the superclass, as well as Modules which are mixed into Array or its superclasses, etc.

"Class-based" (as opposed to prototype-based) object-oriented languages all work this way, more or less. If you've ever programmed in Java, or C++, or Python, this behavior should be familiar to you.

Now, the creator of Ruby wanted to also make it possible to add methods to just one object. In a prototype-based OO language, that would be easy, but how could it work in a class-based language? He made it possible by introducing the idea of "singleton classes" or "eigenclasses".

A "singleton class" is, simply, a class which has only one instance. I believe that rather than trying to keep track of a different singleton class for every single object, Ruby waits until the first time you try to access an object's singleton class, and then creates the class and inserts it into the object's inheritance chain dynamically.

As I just said, when a method is called, Ruby looks first in the object's class to find a matching definition, then the superclass, etc. Since singleton classes are inserted as the first link in an object's inheritance chain, they are the first place which Ruby will look for a method definition.

Bringing in the concept of "singleton classes" also solved another problem at the same time. In Java (for example), you can define static methods which are called on a class. In Ruby, people often want to do something similar. With "singleton" classes and methods, you can do just that: all you have to do is define a singleton method on a Class object.

(Remember that classes are also objects in Ruby. That's why the concept of "singleton" classes and methods can "kill 2 birds with 1 stone", as I explain above!)

EXTRA INFORMATION:

At the beginning, I mentioned "instance methods". I don't know if that might be confusing, or if you already know what "instance methods" are. When you define a Ruby class, like this...

class MyClass
def my_method
# do something
end
end

...then my_method will be added as an instance method of MyClass. When Ruby searches an object's class, superclass, etc. for a method definition, what it actually looks at are their instance methods. So an object's "methods" are the instance methods of its class, plus the instance methods of the superclass, the superclass of the superclass, etc.

About how singleton classes interact with method lookup in Ruby, there is a slight inconsistency which I didn't mention above. If you want to understand in detail:

Singleton classes of class objects are treated a little differently from singleton classes of other objects in general. If you have a class A which inherits from another class B, and B has singleton methods, A will inherit not just the instance methods, but also the singleton methods of B. In other words, B's singleton class is treated as a superclass of A's singleton class. This is not true of the singleton classes of any other objects.

About class definition in Ruby

class A
# self here is A
def aa
# self here is the instance of A who called this method
end
class << self
# self is the eigenclass of A, a pseudo-class to store methods to a object.
# Any object can have an eigenclass.
def aa
# self is the pseudo-instance of the eigenclass, self is A.
end
end
end

This is the same:

class A
def self.aa
end
end

And this:

class << A
def aa
end
end

And also this:

def A.aa
end

You can open the eigenclass of anything:

hello = "hello"
class << hello
def world
self << " world"
end
end
hello.world #=> "hello world"
"my".world #=> NoMethodError

An eigenclass is actually an instance of Class, it has it own eigenclass too.

"If it appears to be too confusing, just remember that Class is an object and that Object is a class."

Ruby eigenclass (sigleton class) created? For which?

  1. Look at cat.singleton_class and cat.singleton_methods.
  2. No. Define a couple singleton methods on cat and then look at cat.singleton_class.instance_methods and you'll see them both there. You can get a hint about the hierarchy with cat.singleton_class.ancestors.
  3. You've defined cat to be some entirely new object now. Why would it have the same methods? If you say cat = "string" then you can call cat.upcase, but if you later reassign cat = 5, of course you can't still call cat.upcase.

Ruby class question

What's happening here is that we're reopening an object's class from within itself and defining a new instance method on it. This is one of the ways you can do so-called "monkey-patching" in Ruby. This method adds the method only to the current object rather than all objects of the class.

It's equivalent to doing this:

my_obj = MyClass.new

class << my_obj
def some_method
end
end

# or...

def my_obj.some_method
end

Here's a good article that covers it: Learning Ruby: class << self.



Related Topics



Leave a reply



Submit