class variable leading a double life

A

Amarison

Can someone please explain why the @var variable leads a double life? One
for instances and one for the class itself?



class Test
def Test.inc
@var ||= 0
@var += 1
end

def inc
@var ||= 0
@var += 1
end
end


puts Test.inc

x = Test.new
puts x.inc

y = Test.new
puts y.inc

#-------------

puts Test.inc
puts x.inc
puts y.inc
 
J

James Edward Gray II

Can someone please explain why the @var variable leads a double
life? One
for instances and one for the class itself?

@var refers to an instance variable of the current "self" object.
class Test
def Test.inc
@var ||= 0
@var += 1
end

Here self is Test.
def inc
@var ||= 0
@var += 1
end
end

And here, self is an instance of Test. Two different objects, two
different variables.
puts Test.inc

x = Test.new
puts x.inc

y = Test.new
puts y.inc

#-------------

puts Test.inc
puts x.inc
puts y.inc

If you want it to be the same variable in both places, you need a
class variable:

class Test
@@var = 0

def Test.inc
@@var += 1
end

def inc
@@var += 1
end
end


puts Test.inc

x = Test.new
puts x.inc

y = Test.new
puts y.inc

#-------------

puts Test.inc
puts x.inc
puts y.inc

Hope that helps.

James Edward Gray II
 
B

Brent W. Hughes

James said:
@var refers to an instance variable of the current "self" object.



Here self is Test.
I thought that Test.inc was a class method that could be called without
ever instantiating an object of class Test, and that therefore there
would be no "self". Are you saying that Test is not just a class, but
is also an instance of some other class? I don't understand, but then
I'm just a newbie to Ruby.
 
A

Austin Ziegler

I thought that Test.inc was a class method that could be called without
ever instantiating an object of class Test, and that therefore there
would be no "self". Are you saying that Test is not just a class, but
is also an instance of some other class? I don't understand, but then
I'm just a newbie to Ruby.

Ruby isn't Java or C++; in Ruby (like in Smalltalk, I think),
essentially everything is an object, including classes. Class objects
are instances of class Class.

-austin
--=20
Austin Ziegler * (e-mail address removed)
* Alternate: (e-mail address removed)
 
A

Adam P. Jenkins

Brent said:
I thought that Test.inc was a class method that could be called without
ever instantiating an object of class Test, and that therefore there
would be no "self". Are you saying that Test is not just a class, but
is also an instance of some other class? I don't understand, but then
I'm just a newbie to Ruby.

That's exactly right. Test is an instance of class Class. There's a
description about Ruby classes and objects in the Pickaxe book at
http://www.rubycentral.com/book/classes.html. The 2nd Edition, which
you have to buy, has a better description, but the free online version
has a good enough intro.
 
D

Devin Mullins

James said:
If you want it to be the same variable in both places, you need a
class variable:

class Test
@@var = 0

...

James Edward Gray II

OK, now I'm confused. I had thought that class variables were just
instance variables of the Test object, but it seems that the case is
much weirder than that. (Why?)

class Test
@fun = "hello"
@@fun = 5
def initialize
p @fun
p @@fun
@fun = "blah"
@@fun = "bloo"
end
def fun
p @fun
p @@fun
end
end
def Test.fun
p @fun
p @@fun
@fun = "bing"
@@fun = "bong"
end

irb(main):021:0> Test.fun
"hello"
NameError: uninitialized class variable @@fun in Object
from (irb):17:in `fun'
from (irb):21

irb(main):022:0> t = Test.new
nil
5
=> #<Test:0x2aba608 @fun="blah">

irb(main):023:0> Test.fun
"hello"
NameError: uninitialized class variable @@fun in Object
from (irb):17:in `fun'
from (irb):23

irb(main):024:0> t.fun
"blah"
"bloo"
=> nil

1. Class variables are these odd things that are shared among every
instance of Test.
2. Class variables of Test are not available to class methods of Test
itself.
3. The singleton class for Test can have instance variables, but not
class variables.*
4. Why didn't the second Test.fun call print "bing"?
5. Is there some way to share data between between a class's instance
methods and its class methods?

I'm going to go study the PickAxe2 now, but I figured maybe I could get
a quicker answer from some of you. :)

Devin
*I'm referring to the anonymous subclass of Class that is the class of
the Test constant itself. She sells sea shells by the sea shore.
 
J

James Edward Gray II

OK, now I'm confused. I had thought that class variables were just
instance variables of the Test object, but it seems that the case
is much weirder than that. (Why?)

We already have a syntax for instance variables, so that's what you
use if you want an instance variable on test. The reason class
variables don't work like that is that they would then by tricky to
reach fro instance objects of the class and that wouldn't be good at
all.

Hope that makes sense.

James Edward Gray II
 
E

Eustáquio Rangel de Oliveira Jr.

Hi.
I thought that Test.inc was a class method that could be called without= =20
ever instantiating an object of class Test, and that therefore there=20
would be no "self". Are you saying that Test is not just a class, but=20
is also an instance of some other class? =20

There is an "internal", if makes it easier to understand, representation
of Test, so @@var is there. Kind of when creating t =3D Test.new, t point=
s
to @@var on the "internal" class.

This is the way you can add or remove methods to a class already
defined, you change the representation on the "internal" class who
shares your change with all other objects of the same class, and
thinking this way, share that variable also.

Kind of when you write

class Test
def method_one
puts "one!"
end
end
t1 =3D Test.new

and then, later

class Test
def method_two
puts "two"
end
end
t2 =3D Test.new

you have BOTH methods on t1 and t2, not matter that t1 was defined
before it was added, you changed that "internal" class where t1 and t2
are using as reference. When you create a class variable, think it is
stored there.

Best regards,
 
R

Robert Klemme

James Edward Gray II said:
We already have a syntax for instance variables, so that's what you
use if you want an instance variable on test. The reason class
variables don't work like that is that they would then by tricky to
reach fro instance objects of the class and that wouldn't be good at
all.

Let me add to that that usually you want to associate a variable with the
class instance or with instances. Class variables (the ones with @@) have
some strange properties that sometimes lead to surprising behavior. I
generally recommend to not use them. Instance variables in classes are much
simpler to handle and much clearer and cleaner IMHO.

Just a simple example where each new instance gets assigned a serial number:

class Foo
@cnt = 0
def self.tick() @cnt += 1 end

attr_reader :serial

def initialize
@serial = self.class.tick
end
end

?> instances = (1..10).map { Foo.new }
=> [#<Foo:0x1017e248 @serial=1>, #<Foo:0x1017e1a0 @serial=2>,
#<Foo:0x1017e0e0 @serial=3>, #<Foo:0x1017dff0 @serial=4>, #<Foo:0x1017
df60 @serial=5>, #<Foo:0x1017deb8 @serial=6>, #<Foo:0x1017de58 @serial=7>,
[QUOTE="# said:
instances.map {|f| f.serial}
[/QUOTE]
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Note that this has to be changed slightly if you want to inherit from Foo.
I just didn't want to make this overly complicated.

Kind regards

robert
 
E

Eustáquio Rangel de Oliveira Jr.

Hey Robert!
Let me add to that that usually you want to associate a variable with=20
the class instance or with instances. Class variables (the ones with=20
@@) have some strange properties that sometimes lead to surprising=20
behavior. I generally recommend to not use them. Instance variables i= n=20
classes are much simpler to handle and much clearer and cleaner IMHO.

Just to make things more clear,

irb(main):001:0> class Test
irb(main):002:1> @@v1 =3D 0 # class variable?
irb(main):003:1> @v2 =3D 1 # ???
irb(main):004:1> def initialize
irb(main):005:2> @v3 =3D 2 # instance variable?
irb(main):006:2> end
irb(main):007:1> def one
irb(main):008:2> @@v1
irb(main):009:2> end
irb(main):010:1> def two
irb(main):011:2> @v2
irb(main):012:2> end
irb(main):013:1> def three
irb(main):014:2> @v3
irb(main):015:2> end
irb(main):016:1> end
=3D> nil
irb(main):017:0> t =3D Test.new
=3D> #<Test:0xb7dc6574 @v3=3D2>
irb(main):018:0> t.one
=3D> 0
irb(main):019:0> t.two
=3D> nil
irb(main):020:0> t.three
=3D> 2

What is the correct names of v1, v2 and v3? v1 is a class variable, but=20
what about v2 and v3? We can say that v3 belongs to the class instance=20
(we saw that after creating the class v3 is on the output=20
<Test:0xb7dc6574 @v3=3D2>)on t, but v2 belongs to the class Test?

The answer is the difference to create the variables just after the=20
class statement and before the initialize method OR create it in the=20
initialize method, right?

And v2 is not reached by the two method, but if I change the class and=20
insert

irb(main):026:0> class Test
irb(main):027:1> def self.four
irb(main):028:2> @v2
irb(main):029:2> end
irb(main):030:1> end

and use

irb(main):032:0> t.class.four
=3D> 1

I can reach it. So where v1 and v2 exactly are named and belongs to? I=20
can see that each t haves it's own v3, but still didn't get the way the=20
or ones are. How v1 and v2 are located on different places.
Kind of

+-------+ +-----+
| Test +-----+ t |
+-------+ +-----+
| @@v1? | | @v3 |
| @v2? | +-----+
+-------+

Maybe v1 and v2 are on the same place (the Test instance - v1 needs to=20
be shared by all Test's, right?) but just can be reached on different=20
ways (v2 using four)?

On the pickaxe we have an example

class SongList
MaxTime =3D 5*60 # 5 minutes
def SongList.isTooLong(aSong)
return aSong.duration > MaxTime
end
end

On this case, MaxTime resides on the same place that v2 or not? Because=20
if I change SongList and add

class SongList
def isTooLong(aSong)
return aSong.duration > MaxTime
end
end

and use

s =3D SongList.new
s.isTooLong(<song here>)

I have a valid result, but something like

irb(main):057:0> class SongList2
irb(main):058:1> @maxtime =3D 5*60
irb(main):059:1> def SongList2.isTooLong(t)
irb(main):060:2> return t > @maxtime
irb(main):061:2> end
irb(main):062:1> def isTooLong(t)
irb(main):063:2> return t > @maxtime
irb(main):064:2> end
irb(main):065:1> end
=3D> nil
irb(main):066:0> s2 =3D SongList2.new
=3D> #<SongList2:0xb7d733d8>
irb(main):067:0> s2.isTooLong(301)
ArgumentError: comparison of Fixnum with nil failed
from (irb):63:in `>'
from (irb):63:in `isTooLong'
from (irb):67
from (null):0
irb(main):068:0> SongList2.isTooLong(301)
=3D> true
irb(main):069:0> SongList2.isTooLong(30)
=3D> false

gives me that error, so there is also a scope difference on a constant=20
like MaxTime and a variable like @maxtime, defined on the class body?

Thanks! :)
 
R

Robert Klemme

Eustáquio Rangel de Oliveira Jr. said:
Hey Robert!


Just to make things more clear,

I'm not sure about "more clear" with your example... :)
irb(main):001:0> class Test
irb(main):002:1> @@v1 = 0 # class variable?
irb(main):003:1> @v2 = 1 # ???
irb(main):004:1> def initialize
irb(main):005:2> @v3 = 2 # instance variable?
irb(main):006:2> end
irb(main):007:1> def one
irb(main):008:2> @@v1
irb(main):009:2> end
irb(main):010:1> def two
irb(main):011:2> @v2
irb(main):012:2> end
irb(main):013:1> def three
irb(main):014:2> @v3
irb(main):015:2> end
irb(main):016:1> end
=> nil
irb(main):017:0> t = Test.new
=> #<Test:0xb7dc6574 @v3=2>
irb(main):018:0> t.one
=> 0
irb(main):019:0> t.two
=> nil
irb(main):020:0> t.three
=> 2

What is the correct names of v1, v2 and v3? v1 is a class variable,
but what about v2 and v3? We can say that v3 belongs to the class
instance (we saw that after creating the class v3 is on the output
<Test:0xb7dc6574 @v3=2>)on t, but v2 belongs to the class Test?

In fact you have v2, v2 and v3 which shows a bit unfortunate naming because
these are three different things. They are all instance variables only that
the first v2 is an instance variable of the class object while the second v2
is an instance variable of each Test instance. These are not the same nor
do they interfere with each other.
The answer is the difference to create the variables just after the
class statement and before the initialize method OR create it in the
initialize method, right?

Yes. And you can create them even later. Instance variables can spring
into existing any time (i.e. during any method execution, see also
#instance_variable_get, ..set etc). Ruby is different than other OO
languages where you usually have to declare variables. In Ruby, you just
use them. Whenever you do @something=x then the instance that is currently
referred to by self will have an instance variable named "something".
And v2 is not reached by the two method, but if I change the class and
insert

irb(main):026:0> class Test
irb(main):027:1> def self.four
irb(main):028:2> @v2
irb(main):029:2> end
irb(main):030:1> end

and use

irb(main):032:0> t.class.four
=> 1

I can reach it. So where v1 and v2 exactly are named and belongs to? I
can see that each t haves it's own v3, but still didn't get the way
the or ones are. How v1 and v2 are located on different places.
Kind of

+-------+ +-----+
+-------+

You're missing @v2 in the right object.
Maybe v1 and v2 are on the same place (the Test instance - v1 needs to
be shared by all Test's, right?) but just can be reached on different
ways (v2 using four)?
Yes.

On the pickaxe we have an example

class SongList
MaxTime = 5*60 # 5 minutes
def SongList.isTooLong(aSong)
return aSong.duration > MaxTime
end
end

On this case, MaxTime resides on the same place that v2 or not?
Because if I change SongList and add

class SongList
def isTooLong(aSong)
return aSong.duration > MaxTime
end
end

and use

s = SongList.new
s.isTooLong(<song here>)

I have a valid result, but something like

irb(main):057:0> class SongList2
irb(main):058:1> @maxtime = 5*60
irb(main):059:1> def SongList2.isTooLong(t)
irb(main):060:2> return t > @maxtime
irb(main):061:2> end
irb(main):062:1> def isTooLong(t)
irb(main):063:2> return t > @maxtime
irb(main):064:2> end
irb(main):065:1> end
=> nil
irb(main):066:0> s2 = SongList2.new
=> #<SongList2:0xb7d733d8>
irb(main):067:0> s2.isTooLong(301)
ArgumentError: comparison of Fixnum with nil failed
from (irb):63:in `>'
from (irb):63:in `isTooLong'
from (irb):67
from (null):0
irb(main):068:0> SongList2.isTooLong(301)
=> true
irb(main):069:0> SongList2.isTooLong(30)
=> false

gives me that error, so there is also a scope difference on a constant
like MaxTime and a variable like @maxtime, defined on the class body?

The trick is in the lookup: @something always refers to the current instance
at the moment of execution. Your instance method isTooLong (which btw. in
Ruby convention would be named too_long?) refers to a @maxtime instance
variable of the SongList2 instance - as these instances don't have a
@maxtime you get nil.

Constants are looked up via in class scope - naturally because you cannot
have constances in an instance.

Kind regards

robert
 
D

Devin Mullins

Robert said:
Class variables (the ones with @@) have some strange properties that
sometimes lead to surprising behavior.

Indeed. I figured out why my code sample didn't work. Ruby considers it
a "toplevel singleton method." If I define a class method inside the
class definition, it works, but if I define it outside (as I did in my
email), it's looks to the closest class definition outward -- in this
case, the thing irb wraps my code in. This is one of those odd cases
where irb acts differently from Ruby-raw, too. Also, Ruby didn't warn me
about toplevel singleton access like they said it should. I noticed it
does, though, if I use class << Test syntax.

This make sense, now. def Test.blah is a singleton method, and Ruby
doesn't know anything about class scope when it's looking at this
definition, because it's treating Test like any old object on which to
define a singleton method. It needs to be stuck inside a class
Blah...end block to know where the class variables lie.

Still icky, though. (See below for my RCR-lite.)
I generally recommend to not use them. Instance variables in classes
are much simpler to handle and much clearer and cleaner IMHO.

Just a simple example where each new instance gets assigned a serial
number:

Thanks for the example. I'm just wondering why

class Foo
def blah
@@cnt += 1
end
end

couldn't have been a shortcut for [an optimization of]:

class Foo
class << self
attr_accessor :cnt
end
def blah
self.class.cnt += 1
end
end

And why instead class variables were done the way they were.

And if I don't get a decent answer, I'll go complain about it in my
imaginary blog! So take that, matz!

Devin
 
E

Eric Mahurin

--- Devin Mullins said:
Thanks for the example. I'm just wondering why
=20
class Foo
def blah
@@cnt +=3D 1
end
end
=20
couldn't have been a shortcut for [an optimization of]:
=20
class Foo
class << self
attr_accessor :cnt
end
def blah
self.class.cnt +=3D 1
end
end

I agree. Class variables should just be a shortcut for
instance variables of an object's class. Having an independent
and slightly different class variable concept seems redundant,
confusing, and inconsistent.

You could just say that these should be equivalent:

@@x # self.class.instance_eval{@x}
@@x =3D y # self.class.instance_eval{@x=3Dy}




=09
__________________________________=20
Discover Yahoo!=20
Stay in touch with email, IM, photo sharing and more. Check it out!=20
http://discover.yahoo.com/stayintouch.html
 
D

Devin Mullins

Eric said:
Having an independent
and slightly different class variable concept seems redundant,
confusing, and inconsistent.
And *weird*.

irb(main):001:0> class A
irb(main):002:1> @@n = 0
irb(main):003:1> def initialize
irb(main):004:2> @@n += 1
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> class B
irb(main):008:1> @@n = 0
irb(main):009:1> def initialize
irb(main):010:2> @@n += 1
irb(main):011:2> end
irb(main):012:1> def A.num
irb(main):013:2> @@n
irb(main):014:2> end
irb(main):015:1> end
=> nil
irb(main):016:0> A.new; B.new; B.new
=> #<B:0x2ac1190>
irb(main):017:0> A.num
=> 2

For extra fun, replace def A.num with def $stdout.num.
You could just say that these should be equivalent:

@@x # self.class.instance_eval{@x}
@@x = y # self.class.instance_eval{@x=y}
Oh, tryin' ta one-up me, eh? Well, that shouldn't be too hard. I'm still
learning this stuff. :)

Devin
 
R

Robert Klemme

Devin Mullins said:
Robert said:
Class variables (the ones with @@) have some strange properties that
sometimes lead to surprising behavior.

Indeed. I figured out why my code sample didn't work. Ruby considers
it a "toplevel singleton method." If I define a class method inside
the class definition, it works, but if I define it outside (as I did
in my email), it's looks to the closest class definition outward --
in this case, the thing irb wraps my code in. This is one of those
odd cases where irb acts differently from Ruby-raw, too. Also, Ruby
didn't warn me about toplevel singleton access like they said it
should. I noticed it does, though, if I use class << Test syntax.

This make sense, now. def Test.blah is a singleton method, and Ruby
doesn't know anything about class scope when it's looking at this
definition, because it's treating Test like any old object on which to
define a singleton method. It needs to be stuck inside a class
Blah...end block to know where the class variables lie.

Still icky, though. (See below for my RCR-lite.)
I generally recommend to not use them. Instance variables in classes
are much simpler to handle and much clearer and cleaner IMHO.

Just a simple example where each new instance gets assigned a serial
number:

Thanks for the example. I'm just wondering why

class Foo
def blah
@@cnt += 1
end
end

couldn't have been a shortcut for [an optimization of]:

class Foo
class << self
attr_accessor :cnt
end
def blah
self.class.cnt += 1
end
end

And why instead class variables were done the way they were.

And if I don't get a decent answer, I'll go complain about it in my
imaginary blog! So take that, matz!

Devin

As far as I remember even Matz agrees that class variables are awkward. I
think they are on their way out - at least in Ruby 2.

Btw, for extra fun mix inheritance with class variables and change the order
in which they are defined between super and sub classes! :)

Regards

robert
 
E

Eustáquio Rangel de Oliveira Jr.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Eric Mahurin wrote:

| I agree. Class variables should just be a shortcut for
| instance variables of an object's class. Having an independent
| and slightly different class variable concept seems redundant,
| confusing, and inconsistent.

That was confusing me. The way it should be that shortcut, because they
looks like the other a lot.

- ----------------------------
Eust=E1quio "TaQ" Rangel
(e-mail address removed)
http://beam.to/taq
Usu=E1rio GNU/Linux no. 224050
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (GNU/Linux)

iD8DBQFCyI8Fb6UiZnhJiLsRAlqRAKCZATsytU0RNnv6rH2djXa+LgDQugCghQxd
WmhIidaZMFnlbqjVnHiZ8SI=3D
=3DpdqY
-----END PGP SIGNATURE-----
 
E

Eustáquio Rangel de Oliveira Jr.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

| As far as I remember even Matz agrees that class variables are awkward.
| I think they are on their way out - at least in Ruby 2.

That will lead to a really more clean thing. :)
Because @@var and @var (defined on the class body) really looks the same
when you see it at first time.

- ----------------------------
Eust=E1quio "TaQ" Rangel
(e-mail address removed)
http://beam.to/taq
Usu=E1rio GNU/Linux no. 224050
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.0 (GNU/Linux)

iD8DBQFCyI/Ob6UiZnhJiLsRAtSqAJ9XvIw7xGvLEmtAfwLG0HyH73LDQwCgmpGr
HjIGp46PhlZDwwH7HJVvmgk=3D
=3Dz8YL
-----END PGP SIGNATURE-----
 
D

David A. Black

Hi --

OK, now I'm confused. I had thought that class variables were just instance
variables of the Test object, but it seems that the case is much weirder than
that. (Why?)

Actually the case is *less* weird than that :) It comes down to:
every object can have instance variables (including Class objects),
and class variables are a completely separate matter.

The confusion, I think, arises from the visual similarity (@ and @@),
which suggests that there's some relation between the two. Then
discussion proceeds as to how to reconcile them, or which should
become more like the other, etc. -- which are really solutions in
search of a problem.

This confusion arises a lot. It's perhaps the main reason I would
like to see class variables disappear in 2.0. They make it much
harder for people to understand that classes can have instance
variables, and that in turn interferes with understanding the whole
instance variable concept as well as the "a class is itself an object"
concept.


David
 
D

David A. Black

Hi --

--- Devin Mullins said:
Thanks for the example. I'm just wondering why

class Foo
def blah
@@cnt += 1
end
end

couldn't have been a shortcut for [an optimization of]:

class Foo
class << self
attr_accessor :cnt
end
def blah
self.class.cnt += 1
end
end

I agree. Class variables should just be a shortcut for
instance variables of an object's class. Having an independent
and slightly different class variable concept seems redundant,
confusing, and inconsistent.

You could just say that these should be equivalent:

@@x # self.class.instance_eval{@x}
@@x = y # self.class.instance_eval{@x=y}

That's a breakage of encapsulation, though. In its capacity as "just
an object", a class should have the same "rights" as any other object
-- specifically, the right to keep its instance variables to itself.

(I understand that no object's instance variables are safe from
instance_eval, but I still wouldn't want to see it hidden behind a
special syntax to the detriment of Class objects.)

I think I like Devin's idea better -- essentially:

@@x # self.class.x
@@x = y # self.class.x = y

though I would want the writing of the accessor methods to be explicit
(rather than having the @@x reference cause a singleton method to be
written on the class). This perhaps favors the "a class is just an
object" state of things more than the "classes and their instances
should have a way to share variables" concept, and that in turn
probably reflects my own priorities.

I'm not a big fan of the @@x syntax at all, but to have it work more
smoothly with the whole instance variable system would be much better,
in my view, than having it be a whole separate system which looks
connected to it but isn't.


David
 

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

No members online now.

Forum statistics

Threads
474,175
Messages
2,570,942
Members
47,490
Latest member
Finplus

Latest Threads

Top