Class variables, instance variables, singleton; Ruby v. C++

D

David A. Black

Hi --

DAB> And of course this is one of the (many) problems with class variables:
DAB> they make obscure the otherwise rather simple notion that classes can
DAB> have instance variables because classes are objects and any object can
DAB> have instance variables.

David ... this may be the higher-level gestalt that I was missing. It
sure sounds like it.

I have often made the observation that the answer to 75% of all
questions about Ruby is: "Because classes are also objects." :)

Rick has explained most or all of what you were asking about. With
regard to class variables, I would encourage you to keep them somewhat
separate in your mind from the rest of the Ruby object and
variable/identifier model. Class variables are kind of layered on top,
as an expedient for getting class objects and their instances to share
data. Since classes are objects and their instances are *different*
objects, having them share data via variables is just a variation on
the theme of global variables; indeed, class variables are essentially
hierarchy-scoped globals.

Instance variables are a completely unrelated and totally different
matter: they are the mechanism for per-object state. Every object has
its own supply of instance variables, and by definition, no object can
see the instance variables of any other object -- even if object 1 is
a class and object 2 is an instance of that class. This is enforced
via the "self" mechanism: whenever you see an instance variable, you
are seeing an instance variable that belongs to self (whatever self is
at that moment).

I've always felt that it's unfortunate that class variables start with
"@@", because that conveys the impression that they're somehow akin to
instance variables. They're not; they're actually kind of the opposite
of instance variables, because they exist in order to blur the borders
between objects rather than to clarify them. They really should have
been "$$var" :) Many of us would not mourn them if they disappeared
from Ruby.


David
 
D

David A. Black

Hi --

As others have pointed out class variables are a pretty wonky area of
Ruby. In Ruby 1.8 if G is a actually a subclass of F, then whether or
not @@x is the same instance variable for both depends in some cases
on whether or not F got its instance variable before G did or not.

When a class variable is initialized Ruby 1.8 looks through the chain
of superclasses to see if a class variable with that name already
exists, and if so uses that definition. If not it defines it for the
current class. Ruby 1.9 changed that so that now class variables are
no longer shared with subclasses.

I think that was true briefly but not more recently:

$ irb191
irb(main):001:0> class C
irb(main):002:1> @@x = 1
irb(main):003:1> end
=> 1
irb(main):004:0> class D < C
irb(main):005:1> @@x = 2
irb(main):006:1> end
=> 2
irb(main):007:0> class C
irb(main):008:1> @@x
irb(main):009:1> end
=> 2

(unless it's been re-reverted yet more recently and I haven't picked
up on it).


David
 
R

Ralph Shnelvar

DAB> Many of us would not mourn them [@@ variables?] if they disappeared
DAB> from Ruby.

Do you mean to say there is a way around using class variables in
Ruby?
 
M

Marnen Laibow-Koser

Ralph said:
DAB> Many of us would not mourn them [@@ variables?] if they disappeared
DAB> from Ruby.

Do you mean to say there is a way around using class variables in
Ruby?

Yes, and it's been discussed in this thread. Go reread.

Best,
-- 
Marnen Laibow-Koser
http://www.marnen.org
(e-mail address removed)
 
D

David A. Black

Hi --

DAB> Many of us would not mourn them [@@ variables?] if they disappeared
DAB> from Ruby.

Do you mean to say there is a way around using class variables in
Ruby?

What people usually want to do when they reach for class variables is
maintain state on a class object. Class variables don't actually do
that. The way to maintain per-object state is with an instance
variable, optionally wrapped in accessor methods (mainly if you want
other objects to be able to read or write the object's state).

This is the so-called "class instance variable", which is really just
"an instance variable that happens to belong to a Class object". It's
common to use the phrase "class instance variable", but it's just a
way of clarifying what you mean. There's no separate "class instance
variable" language-level construct.

I would strongly recommend learning all about instance variables,
including the fact that, as objects, classes can have them; learn
about self, and how instance variables are resolved; and don't worry
about class variables for the moment. It's important to know they're
there, and how they work, but they don't shed much light on the
language overall.


David
 
R

Ralph Shnelvar

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.

class X
# "equivalent" to C++'s
# class X {
# protected:
# static sometype at_at_x = 3;
# };
@@x = 3

# "equivalent" to C++'s
# class X {
# private:
# sometype at_x;
# X::X() {at_x =3;}
# };
# at_x is not "inheritable" since it is private.
@x = 3


# No equivalent notion in C++. It's just a temporary variable
x = 3
end



- - - -



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.

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.



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".
 
D

David A. Black

Hi --

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.

class X
# "equivalent" to C++'s
# class X {
# protected:
# static sometype at_at_x = 3;
# };
@@x = 3

# "equivalent" to C++'s
# class X {
# private:
# sometype at_x;
# X::X() {at_x =3;}
# };
# at_x is not "inheritable" since it is private.
@x = 3

@x belongs to self. Inside a class definition, self is the class:

class X
p self # Output: X
end

Therefore, if you create an instance variable, it belongs to X (the
class object):

class X
@var = 1 # this @var belongs to the class object X (i.e., self)
end

p X.instance_variables # Output: ["@var"]

The same thing happens with EVERY object in Ruby. If self is object x,
then @var is an instance variable of object x:

class C
def greet
p self
@var = 1
p instance_variables
end
end

C.new.greet

Output:

#<C:0x484554> (i.e., an instance of class C)
["@var"] (the i.vars of that instance)

str = "I'm a string"
str.instance_eval do # instance_eval sets "self" for the
@var = 1 # duration of a code block.
p self
p self.instance_variables
end

Output:

"I'm a string"
["@var"]

I repeat: instance variables are always pegged to "self". They are not
usefully understood as private or public. You can say, informally,
that instance variables are "private", in the sense that they only
appear when self is the object that owns them. But it's kind of
pointless since there's no other state they can be in; there's no such
thing as a "public" instance variable.
# No equivalent notion in C++. It's just a temporary variable
x = 3
end



- - - -



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.

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.

I have to admit that in nine years of using, teaching, and writing
books about Ruby, I don't think I've ever used the word "static" to
describe anything in Ruby. Ruby and "static" don't mix :)
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".

I'm a little baffled by why you're trying to understand Ruby by
transliterating concepts and even syntax from C++, and then concluding
that Ruby is a "jumble" because there's no one-to-one correspondence.
I'd really, really urge you to forget about C++ when learning Ruby.
(Easy for me because I don't know C++ in the first place :)

In Ruby terms: a class is an object. It has a few special behaviors
that set it apart from other objects, mainly:

1. it can spawn other objects
2. it has a keyword dedicated to switching into its "self" scope:
class

The Class class is a subclass of the Module class. Modules and classes
are storage units for methods (which objects can cause to be executed,
in response to the appropriate methods) and constants.

I would suggest forgetting C++, forgetting class variables, and
playing around with class definitions, the concept of "self", and
instance variables. Try to understand it in Ruby terms. It's actually
much more straightforward that way.


David
 
D

David Masover

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.
 
P

Paul Smith

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.

Does it? I thought this would only give @count the value 0 if it
previously had the value nil or false, and that if the count was -7
that would remain. Did you mean "@count has a true value" instead of
"@count has a positive value"?

irb(main):001:0> @count = -7
=> -7
irb(main):002:0> @count ||= 0
=> -7
irb(main):003:0> @count = nil
=> nil
irb(main):004:0> @count ||= 0
=> 0

C:\Documents and Settings\paul.smith>ruby -v
ruby 1.9.1p243 (2009-07-16 revision 24175) [i386-mingw32]
 
D

David Masover

Does it? I thought this would only give @count the value 0 if it
previously had the value nil or false, and that if the count was -7
that would remain. Did you mean "@count has a true value" instead of
"@count has a positive value"?

Whoops, yes, I did. You're right.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top