I’ve been playing around with Ruby for a while now, and I know that you can create meta-classes, but why this is a feature designed into the programming language hasn’t been very clear to me. Until today, when I found a use-case that really brought it home to me.
A critical part of understanding the reasoning behind having meta-classes was actually when I took some time to understand what the @ and @@ notations really do. A lot of tutorials and descriptions will say that this is the syntax for defining, respectively, “instance variables” and “class variables.” This isn’t entirely true.
One of the problems with explaining Ruby’s behavior – how it conceptualizes the notion of object-oriented programming – by drawing analogies to a lot of traditional concepts, is that Ruby isn’t really designed like most object-oriented (OO) languages. Consider for example that Ruby doesn’t expose anything but methods publicly for a class or object. Consider also that classes themselves are objects, in a much more actionable way than in most OO languages.
So what the ‘@’ and ‘@@’ syntax does is something pretty specific to how Ruby creates bindings. But before we get to that, let’s clear up something about “class variables” vs “instance variables.” In fact, we won’t talk about the @@ notation in the rest of this post; that’ll come in a later post.
A “class variable” is also referred to as a “static variable” – its value is accessible to all instances of a class, and when one instance changes it, all other instances see that update. In Ruby, a “class variable,” in this sense of the word, is defined by writing the variable definition in the “body” of the class definition itself:
class Dog < Animal @number_of_legs = 4 end
Remember that a “class” definition is really just an object – an instance of the class Class. So what we just did in the previous snippet was to add an instance variable in the binding of the class Dog, which is an instance of Class. That is now available to any instance of that class – but first, we define the accessor methods on the parent class (Animal), so that all you have to do for the child class (Dog) is define the value of the “class variable” for that class:
class Animal def self.number_of_legs @number_of_legs end def number_of_legs @number_of_legs end end class Dog < Animal @number_of_legs = 4 end d1=Dog.new puts d1.number_of_legs # 4
So where does this leave us? Why do we use the @@ notation at all? Let’s read on to find out, and to move a step closer to understanding metaclasses (and what they are good for.)