Hi --

On Thu, 14 Dec 2006, Giles Bowkett wrote:

>> >> Most of what people need to grasp and embrace, in order to understand
>> >> and use the vast bulk of these techniques, and others, is:
>> >>
>> >>    * classes are objects
>> >>    * each object (or almost each object) has both a "birth class" and a
>> >>      singleton class
>> >>    * objects don't have methods; they look for them along a
>> >>      class/module search path, with well-defined rules of order and
>> >>      inclusion
>> >>    * there's one and only one 'self' at every point in the program
>> >>    * instance variables belong to 'self', always
>> >>    * class variables are completely unrelated to instance variables,
>> >>      in spite of the use of @ in one and @@ in the other
>> >>    * the def and class keywords start new local scopes
>> >>    * the class keyword can take either a constant (for a named class)
>> >>      or a "<< object" expression (for a singleton class)
>> >
>> > I have to admit, I don't understand what this means in practical
>> > terms. In fact I have a question I need to find an answer to, and I
>> > want to post it in this thread, rather than as its own thing, in the
>> > possibly idealistic hope that somebody can frame an answer in terms of
>> > these principles, and help me understand them.
>> 
>> If they're not of practical value, then they're not of value.  So I'll
>> give it a try :-)
>
> Now I actually have the inverse problem. The good news is the code
> works perfectly -- thank you, by the way -- but the bad news is I
> don't totally know why.
>
>> > Basically, I need to change a class method. It's the Ferret method
>> > full_text_search, in a Rails app.
>> >
>> > e.g.:
>> >
>> > Thing.full_text_search("asdf")
>> >
>> > I need to modify this class method. I tried alias, but did it wrong.
>> >
>> > alias original_full_text_search full_text_search
>> >
>> > but of course it doesn't work, because it's not an instance method but
>> > a class method. Is this a good place for class_eval? I could be way
>> > off.
>> >
>> > Actually now that I re-read it, I think I do understand the basic
>> > concepts, I think I'm just weak on the syntax.
>> 
>> In general, if you want to do:
>>
>>    alias old_x x
>> 
>> you want to do so inside a class definition body for the class where x
>> is defined (or a subclass of that class).
>> 
>> A "class method" is basically a singleton method of a Class object.
>> That means that the class where it's defined is the singleton method
>> of that class object (because that's what singleton classes are: the
>> places where objects' singleton methods are defined).
>
> Ah, ok. The class object creates a singleton, which is where all the
> method definitions live?

All the singleton method definitions of that class object, yes.  It's
really just a not-very-special case of the general principle that an
object's singleton methods live in the object's singleton class.

It's perhaps easier to see the workings of it when the object isn't a
class:

   str = "I am a string
   class << str
     def just_for_me
       puts "This method is just for me!"
     end
   end

I've defined a singleton method on the one String object, str.  No
other string will have that method.  If I'd defined it in String, they
would.  But I've defined it in str's singleton class (class << str),
so only str will have it:

   str.just_for_me       # This method is just for me!
   "other".just_for_me   # NoMethodError

Now, watch as I take that example -- and the explanation -- and
substitute a Class object for the String object:

   class Thing; end
   class << Thing
     def just_for_me
       puts "This method is just for me!
     end
   end

I've defined a singleton method on the one Class object, Thing.  No
other class will have that method.  If I'd defined it in Class, they
would.  But I've defined it in Thing's singleton class (class <<
Thing), so only Thing will have it:

   Thing.just_for_me   # This method is just for me!
   Array.just_for_me   # NoMethodError

Note that in the Thing example, just_for_me is what we would call a
"class method".  A class method, then, is just a singleton method
defined for a class object.

>> In your case, the object in question is Thing.  To get into a class
>> definition body for a singleton class, you use the class keyword plus
>> the "<< object" construct:
>>
>>    class << Thing
>
> And this is because the class, having been defined, now has a
> singleton containing all its methods, and we don't want to create a
> new patch, we just want to append new methods to the existing
> singleton?

Yes, we want to define methods inside the singleton class of Thing.
The singleton class of Thing is not actually created until we ask to
get inside it, but that's really an implementation detail.

> I think I do get it, although I admit I'm still puzzled on the
> singleton class / birth class thing.

It's nature vs. nurture.  Every object comes into existence with
certain capabilities -- that is, when you send it messages, it
searches through specific classes and modules, in a specific order,
looking for corresponding methods.  From that perspective, all
instances of a given class are the same as each other.

But you can also add capabilities to an object.  An object that is
"born" ignorant of a certain message need not remain that way.  You
can do this by adding to the object's class -- if you want *all*
instances of that class to have the new capability.  Or, if you just
want one object to have it, you can add a method to that object's
singleton class.

So by its nature, a String instance can do certain things:

   str = "hello there"
   str.split(/ll/)
   str.upcase
   str.reverse

etc.  By way of nurture, we can add to this string by adding to all
strings:

   class String
     def new_method
       ...
     end
   end

or by adding a method to this one string's singleton class, as in the
examples above.

(You can, by the way, also insert a method into an object's singleton
class with the "def" keyword, like this:

   def str.new_method
     ...
   end

or

   def Thing.some_class_method
     ...
   end

.)

The main thing going on in all this is that Ruby objects can change
over the course of their lives.  That's the principle being enforced;
and singleton classes are just the way Matz has chosen to bring that
principle to life.

I get a lot of mileage out of thinking of the method look-up path.
When you send a message to an object, it walks the path -- a
succession of classes and modules, like rooms strung along a corridor
-- and looks for the method.  First, it checks in its own singleton
class (if you've created one), and in any modules mixed in to the
singleton class.  If no method with the right name is found, it
proceeds to its "birth" class, and then to the modules mixed in there.
And so on -- until it gets to Object, and the module that's mixed in
*there* (Kernel).  Then, if it still hasn't found the method, it gives
up.

This post could probably do with some more editing but I don't have it
in me right now :-)  I hope it's helpful.  You might also have a look
at a little essay that covers similar material:
http://www.wobblini.net/singletons.html.  (And my book, of course :-)


David

-- 
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
    aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)