On Monday 30 November 2009 01:43:38 am Ralph S. wrote:
DAB> This is the so-called “class instance variable”, which is really just
DAB> “an instance variable that happens to belong to a Class object”. It’s
DAB> common to use the phrase “class instance variable”, but it’s just a
DAB> way of clarifying what you mean. There’s no separate “class instance
DAB> variable” language-level construct.
So … let’s see if I get this.
Sorry, I don’t remember enough C++ to help you here.
It appears to me that “class” is a jumble of two kinds of very
different semantics.
There is the notion of a class as a template for instances of the
class.
Correct.
Then there is a very different notion of a sorta static class that
has it’s own variables that do not propagate to instances; and may
(@@x) or may not (@x) propagate and be accessile to static subclasses
inherited from the static superclass.
Well, my advice is to forget that @@x exists at all – I’ve never
properly
understood how it propagates or how to properly manage it, and it seems
to be
an invitation for disaster.
But you’re half-right…
A lot to wrap one’s head around. I assume that there are good design
reasons to jumble these two concepts together in the semantics of
“class”.
It’s very simple, really:
In Ruby, EVERYTHING is an object.
You may have seen this cute example before:
class Fixnum
def + other
5
end
end
That’s right, ordinary integers are just objects, and arithmetic
operators are
just methods, which can be overridden. And if you type the above into
IRB, you
can prove that 2+2==5, for very strange definitions of +.
But this is the crucial concept: Numbers are objects. True and false are
objects. Even null is an object – the object is called nil, and it is
an
instance of NilClass. Files are objects. Sockets are objects. Constants
are
objects. Symbols are objects.
EVERYTHING is an object. So, EVERYTHING has the semantics of an object.
In
addition, everything that’s an object is an instance of a class.
Or, to put it another way: The number 5 has the semantics of an ordinary
integer, but also the semantics of an object. You can prove it to
yourself:
class Fixnum
attr_accessor :message
end
That’s right, numbers can have methods, even instance variables! Type
that
into IRB and try this:
5.message = ‘Hello, world!’
4.message
5.message
If you can wrap your head around this concept, it shouldn’t surprise you
too
much that classes are objects, and modules are objects, too. Classes are
instance of the class “Class”, while modules are instances of the class
“Module”.
So the idea of “static” is really unnecessary baggage in this
discussion. If
you’re looking for something similar, I’ve already presented one option,
but
it’s not really “static”, just like an each loop isn’t really a foreach
loop.
Here’s a complete answer to the question “How many objects of type Foo
are
there?” in the traditional C++ style:
class Foo
class << self
# These are about the Foo class itself, as an object.
# Any methods here are methods on the Foo object itself,
# not instances of Foo:
def count
@count ||= 0
end
attr_writer :count
def finalizer
@finalizer ||= lambda do
self.count -= 1
end
end
end
These are instance methods on the Foo class:
def initialize
self.class.count += 1
ObjectSpace.define_finalizer(self, self.class.finalizer)
end
end
As you can see, finalizers are kind of cumbersome to use (so don’t use
them,
you almost certainly don’t need them), but they should work. Whenever
the
garbage collector finally eats those objects, the count will go down.
(You can
force the matter by calling GC.start, but that’s for debugging purposes
only!)
Now, what’s going on here? Probably the most confusing part is this
statement:
class << self
What is that doing? That’s opening what’s called the metaclass of an
object.
Every object has a metaclass, which is easiest to think of like this:
When you
call Foo.new, you get an object which inherits from a class unique to
that
object, which in tern inherits from Foo.
It’s actually not quite like that, but that gives you an idea of what’s
going
on. For example, try this:
a = ‘foo’
def a.bar
5
end
If you understand what that does, then this does the exact same thing:
a = ‘foo’
class << a
def bar
5
end
end
Now, what is self inside the Foo class? It’s the Foo object itself.
The simplest way to explain what all that code is doing is to remember
that I
was just using Foo as an object because it seemed a convenient place to
store
values common to all Foos. I could just as easily have done this (for
the non-
finalized version):
class Foo
Count = 0
def initialize
Count += 1
end
end
It’ll whine because I’m reassigning constants, but you see the point. I
mean,
I could also do this:
FooCount = 0
class Foo
def initialize
FooCount += 1
end
end
It’s just that the Foo object is already there, and makes sense for this
purpose.
Also, if you haven’t seen this before, don’t let it frighten you:
@count ||= 0
That’s providing a default value for @count. When you execute that code,
it
checks whether @count has a positive value – if it doesn’t, it assigns
0 to
@count. Either way, the (possibly new) value of @count will be returned.
So regarding your musings as to whether there’s a good design reason,
I’d
argue yes and no. No, it really doesn’t seem good design, at first
glance,
that numbers can have instance variables, because why would that ever,
ever,
be a good idea?
But yes, it actually gives a nice uniform model. Again, EVERYTHING is an
object, so EVERYTHING has methods, instance variables, a metaclass, and
so on.