24 Jul 2009, 23:54
Generic-user-small

Ken Ballou (4 posts)

It might just be me, but I think more explanation in the “Class Variables” sidebar would help. In the example following “class variables have a nasty habit of surprising you,” I just don’t see why the last line says the value of @@v is 2. (I did try the example in ruby 1.9, and @@v really is 2.) I’m confused because inside the class definition of MyClass, both self and the current class are MyClass. I’m missing something (obviously!), since I thought the third line of the example would be an assignment to the class variable v in MyClass.

Then, I tried this example and confused myself even more:

class Object
  @@v = 1
end
class MyClass
  @@v = 2
end
@@v # => 2

(I admit that I also didn’t completely understand the example on pages 337-338 of the Ruby 1.9 “pickaxe” book.)

Help, please – I feel the waters rushing in over my head! Thanks.

25 Jul 2009, 06:30
Photo_17_pragsmall

Paolo Perrotta (42 posts)

I can feel your pain, Ken. I’m not a fan of class variables, so I tried to swipe them under the rug by confining them to that sidebar. If that turns out to be confusing, then I’ll have to explain them a little bit more in beta 1.2. (I’m busy finishing the new chapter for beta 1.1 now, so you’ll have to be patient…).

The reason why your example works the way it works is that class variables don’t belong to classes (or objects in general) - they belong to class hierarchies. When you define @@v in Object, all the subclasses of Object (including MyClass) can see and modify @@v. All the instances of the subclasses of Object (including main) can also see and modify @@v, which is why you can also access @@v from the top level of your program.

I understand this is not what you expected, because it’s not what I expected, either. After getting confused by class variables one time too much (and after taking heed of other rubyists’ opinions) I decided that I’ll just steer clear of class variables, and use the more elegant Class Instance Variable spell instead. If Matz decides to drop class variables altogether, I won’t shed a tear for them. ;)

25 Jul 2009, 20:17
Generic-user-small

Ken Ballou (4 posts)

Paolo,

THANK YOU so very much! This sentence from your reply is pure gold: “Class variables don’t belong to classes (or objects in general) - they belong to class hierarchies.” The scales have fallen from my eyes, and I now understand both the sidebar in Metaprogramming Ruby and the examples in the pickaxe book.

I went back to the pickaxe book to see if I had overlooked this explanation. I found this on page 338 of the latest version: “Class variables are inherited across children but are unique across children.” I didn’t understand that sentence then, but now I do.

In fact, the way class variables are handled is even stranger than your sidebar example shows. To make sure I understood, I tried the following in irb version 1.9:

irb(main):001:0> class Foo
irb(main):002:1> @@var = "Foo"
irb(main):003:1> def self.get_var
irb(main):004:2> @@var
irb(main):005:2> end
irb(main):006:1> end
=> nil
irb(main):007:0> class Bar
irb(main):008:1> @@var = "Bar"
irb(main):009:1> def self.get_var
irb(main):010:2> @@var
irb(main):011:2> end
irb(main):012:1> end
=> nil
irb(main):013:0> Foo.get_var
=> "Foo"
irb(main):014:0> Bar.get_var
=> "Bar"
irb(main):015:0> @@var
NameError: uninitialized class variable @@var in Object
	from (irb):15
	from /usr/bin/irb1.9:12:in `<main>'
irb(main):016:0> Foo.class_variables
=> [:@@var]
irb(main):017:0> Bar.class_variables
=> [:@@var]
irb(main):018:0> Object.class_variables
=> []
irb(main):019:0> @@var = "top level"
=> "top level"
irb(main):020:0> @@var
=> "top level"

Well, that’s consistent with my understanding. But what comes next completely surprises me:

irb(main):021:0> Foo.get_var
=> "top level"
irb(main):022:0> Bar.get_var
=> "top level"

Say what? No, it can’t be … a wild hunch:

irb(main):023:0> Foo.class_variables
=> []
irb(main):024:0> Bar.class_variables
=> []
irb(main):025:0> Object.class_variables
=> [:@@var]

Now that officially qualifies as the land of the wacky. Creating a class variable in a superclass actually reaches into subclasses and removes the class variable from the subclass!

01 Aug 2009, 03:05
S-img_1828_pragsmall

Satoshi Asakawa (2 posts)

Hi Ken and Paolo,

Thank you for the good discussion. I understood the behavior of Ruby 1.9. I like it. Because it’s clear that class variables belong to class hierarchies. :)

However, I’m still using Ruby 1.8.6 on Windows XP. So, it’s more cofusing. I tried the same steps on my IRB, but I got the different outputs. Look at the following:

irb(main):013:0> Foo.get_var
=> "Foo"
irb(main):014:0> Bar.get_var
=> "Bar"
irb(main):015:0> @@var
NameError: uninitialized class variable @@var in Object
        from (irb):15
irb(main):016:0> Foo.class_variables
=> ["@@var"]
irb(main):017:0> Bar.class_variables
=> ["@@var"]
irb(main):018:0> Object.class_variables
=> []
irb(main):019:0> @@var = "top level"
=> "top level"
irb(main):020:0> @@var
=> "top level"
irb(main):021:0> Foo.get_var
=> "Foo"
irb(main):022:0> Bar.get_var
=> "Bar"
irb(main):023:0> Foo.class_variables
=> ["@@var"]
irb(main):024:0> Bar.class_variables
=> ["@@var"]
irb(main):025:0> Object.class_variables
=> ["@@var"]
01 Aug 2009, 03:16
S-img_1828_pragsmall

Satoshi Asakawa (2 posts)

Hi again, I tried a bit more with ruby 1.8.6.

C:\>ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]

C:\>irb
irb(main):001:0> class A
irb(main):002:1>   @@var = "A"
irb(main):003:1>   def self.get_var
irb(main):004:2>     @@var
irb(main):005:2>   end
irb(main):006:1> end
=> nil
irb(main):007:0> A.get_var
=> "A"
irb(main):008:0> @@var
NameError: uninitialized class variable @@var in Object
        from (irb):8
irb(main):009:0> @@var = 'top level'
=> "top level"
irb(main):010:0> @@var
=> "top level"
irb(main):011:0> A.get_var
=> "A"
irb(main):012:0> A.class_variables
=> ["@@var"]
irb(main):013:0> Object.class_variables
=> ["@@var"]
irb(main):014:0> class A
irb(main):015:1>   @@var = "AA"
irb(main):016:1> end
=> "AA"
irb(main):017:0> A.get_var
=> "AA"
irb(main):018:0> @@var
=> "top level"

The @@@var of A or the @@@@var of Object looks like independent of each other. But, as I assign the @@@var of Object at the first line:

C:\>irb
irb(main):001:0> @@var = 'top level'
=> "top level"
irb(main):002:0> class A
irb(main):003:1>   @@var = "A"
irb(main):004:1>   def self.get_var
irb(main):005:2>     @@var
irb(main):006:2>   end
irb(main):007:1> end
=> nil
irb(main):008:0> A.get_var
=> "A"
irb(main):009:0> @@var
=> "A"
irb(main):010:0> @@var = 'top level again'
=> "top level again"
irb(main):011:0> @@var
=> "top level again"
irb(main):012:0> A.get_var
=> "top level again"
irb(main):013:0> class A
irb(main):014:1>   @@var ="AA"
irb(main):015:1> end
=> "AA"
irb(main):016:0> A.get_var
=> "AA"
irb(main):017:0> @@var
=> "AA"

The @@@var of A and the @@@var of Object look like the same. This behavior made me crazy. I have to update Ruby asap. :-P

01 Aug 2009, 23:29
Photo_17_pragsmall

Paolo Perrotta (42 posts)

Confusing, isn’t it? :)

I was expecting the behavior in your last example (you added a class variable to main’s class Object, so all the subclasses of Object get it, including A), but your next-to-last experiment surprised me as well. ;)

I had similar (but not identical) problems with class variables, and that’s why I encourage people not to use them. I would go as far as to say that they’re broken. OK, maybe technically they’re not - but if experienced programmers can be tripped by them so easily, I’m inclined to put the blame on the language, not the programmers.

02 Aug 2009, 03:54
Generic-user-small

Ken Ballou (4 posts)

Satoshi,

What you saw in Ruby 1.8.6 is actually how I thought class variables should work. That is, when you first create the “var” class variable in the Foo and Bar classes and then create it at top level, I expect Foo and Bar to keep their class variables. On the other hand, when you create the top level class variable first and then reference it in the Foo and Bar classes, I expect to use the top level variable. In that case, the subclasses use the class variable from the superclass.

It’s still counterintuitive to me that in Ruby 1.9, referencing a class variable in a superclass affects subclasses. It’s as though we’re looking down the hierarchy instead of just up.

  You must be logged in to comment