Class Variables in EigenClass

I am using Ruby 1.8.7 and I bumped into a weird behavior regarding
eigenclass of Class object.

Below, I am declaring a class variable inside the eigenclass of Class A.
However for some reasons when I print out the class variables of A and
B, it seems the class variable @@test is accessible for both.

===

def eigen
class << self
self
end
end

class A
end

class B
end

egA = A.send :eigen
puts egA.object_id

egB = B.send :eigen
puts egB.object_id

egA.class_eval do
@@test = 5
end

puts “A Class Variables”
puts A.class_variables

puts “B Class Variables”
puts B.class_variables

===

-607970628
-607970668
A Class Variables
@@test
B Class Variables
@@test

Any idea?

Thanks,

Actually it seems that for some reasons the @@test class variables ended
up in the Object class:

puts Object.class_variables

@@test

Anyway I am still confused about this… Help would very appreciated.

Thanks,

On Mar 6, 2011, at 7:55 PM, JP Billaud wrote:

Actually it seems that for some reasons the @@test class variables ended
up in the Object class:

puts Object.class_variables

@@test

Anyway I am still confused about this… Help would very appreciated.

The snarky answer is: “Don’t use class variables”. They really are hard
to understand and generally don’t have the semantics you expect or need.

The non-snarky answer is that when you do:

egA.class_eval do
@@test = 5
end

Ruby does not evaluate ‘@@test’ relative to self, which would be egA,
but instead evaluates it relative to the lexical scope that is in
effect, which in your code is the top level scope. Class variables
evaluated at the top level scope are instantiated within Object.

Most people expect class variables to only be visible to the class they
are instantiated in but they are actually visible to their ‘home’ class
as well as all subclasses. Since every class is a subclass of Object,
in your example, @@test becomes visible in every class and in every
instance.

Gary W.

Thanks Gary for the explanation. I was hoping the self logic would have
applied in this case since I feel this is the intuitive way to think
about it…

Another question about class variables actually. My understanding of
class variables lookup is that it is not different from the methods
lookup. Indeed, an instance object look at its class’s class variables
and then go through superclass and module all the way up to BasicObject.

Now why does not it apply to Classes object:

===
class Class
@@test = “test”
end

class B
def self.print
puts @@test
end
end

B.print

In this case B.print fails with:

module.rb:7:in `print’: uninitialized class variable @@test in B
(NameError)
from module.rb:11

Since B’s class is Class that should be fine. Obviously I must be
missing something here.

Thanks,
Jean-Pascal Billaud

Alright that explains the situation even though I feel that the dynamic
context should apply in this case since class_eval changes self and the
current class. IMO lexical scope should only be useful for local
variables…

On Mar 6, 2011, at 11:04 PM, JP Billaud wrote:

Another question about class variables actually. My understanding of
class variables lookup is that it is not different from the methods
lookup.

They are not the same and are closer to the way constants are looked up
but not quite the same as that either…

Indeed, an instance object look at its class’s class variables
and then go through superclass and module all the way up to BasicObject.

Not really, the lookup is more accurately associated with the lexical
context of the reference than the dynamic context. In your B.print
method, the lexical scope is actually the class B and so that is the
starting point for the class variable resolution. This is distinctly
different than the way instance variables are resolved.

end

Since B’s class is Class that should be fine. Obviously I must be
missing something here.

Consider the following:

class A
@@foo = “hello”
def foo
@@foo
end
def foo_ieval
42.instance_eval { @@foo }
end
def foo_ceval
Array.class_eval { @@foo }
end
end

puts A.new.foo # “hello”
puts A.new.foo_ieval # “hello”
puts A.new.foo_ceval # “hello”

Array.class_eval { @@foo } # undefined

The lexical scope for foo, foo_ieval, and foo_ceval is the enclosing
class/end block for A while the lexical scope for the final
Array.class_eval is the top level. For the three methods, they all
resolve to the same class variable owned by the class A while the
class_eval outside of A’s definition block resolves to the top level
class, Object.

Gary W.

Hey,

While what you’ve said is true for class_eval contexts (that they
behave like constants) it doesn’t explain the following behaviour, which
the OP may or may not have been getting at:

class C; end

c = C.new

class << c
@@var = 20
end

#=> Warning: class variable access from toplevel singleton method

It appears that the class var is defined on Object. In fact it’s working
lexically here too:

$c = C.new

module J
class << $c; @@var = 30; end
end

J.instance_variables #=> [:@@var]

Pretty weird

Gary W. wrote in post #985860:

On Mar 6, 2011, at 7:55 PM, JP Billaud wrote:

Actually it seems that for some reasons the @@test class variables ended
up in the Object class:

puts Object.class_variables

@@test

Anyway I am still confused about this… Help would very appreciated.

The snarky answer is: “Don’t use class variables”. They really are hard
to understand and generally don’t have the semantics you expect or need.

The non-snarky answer is that when you do:

egA.class_eval do
@@test = 5
end

Ruby does not evaluate ‘@@test’ relative to self, which would be egA,
but instead evaluates it relative to the lexical scope that is in
effect, which in your code is the top level scope. Class variables
evaluated at the top level scope are instantiated within Object.

Most people expect class variables to only be visible to the class they
are instantiated in but they are actually visible to their ‘home’ class
as well as all subclasses. Since every class is a subclass of Object,
in your example, @@test becomes visible in every class and in every
instance.

Gary W.

On Mar 9, 2011, at 7:55 PM, John M. wrote:

While what you’ve said is true for class_eval contexts (that they
behave like constants) it doesn’t explain the following behaviour, which
the OP may or may not have been getting at:

I think you just restated the same thing I said.

One thing that you emphasized and which is confusing is that the
singleton class notation:

class <<object
end

does not create a new lexical scope - nor does class_eval or
instance_eval.

Because there is no new lexical scope from these constructs the
interpretation of class variables doesn’t change when these constructs
are used, and that is generally not what is expected.

Gary W.

On Mar 22, 2011, at 1:23 AM, John M. wrote:

No, it does create a new lexical scope – did you test it ? :slight_smile:

o = Object.new
j = 10

class << o
puts j
end

I think it was my sloppy terminology that was confusing. My intent was
that the phrase ‘lexical scope’ would refer to the scope that was
relevant to constant and class variable resolution. As you’ve pointed
out, that phrase just is not specific enough as there is a lexical scope
created for local variables by the singleton class notation (and
blocks). So there are at least two different types of lexical scopes
that affect name resolution of variables and constants. I’m not sure if
there is any standard terminology used to differentiate them. Perhaps
that is part of the confusion.

Gary W.

One thing that you emphasized and which is confusing is that the
singleton class notation:

class <<object
end

does not create a new lexical scope - nor does class_eval or
instance_eval.

No, it does create a new lexical scope – did you test it ? :slight_smile:

o = Object.new
j = 10

class << o
puts j
end

NameError: undefined local variable or method `j’

This was the point of my post – that you can’t simply use the ‘lexical
scope’ argument (as you can with class_eval); that this is a genuine
quirk of class variables in ruby :slight_smile:

Jean-Pascal Billaud wrote in post #985843:

Any idea?

Thanks,

You can use class_variable_set:

def eigen
class << self
self
end
end

class A
end

class B
end

egA = A.send :eigen
puts egA.object_id

class A
@@in_A = ‘A’
end

egA.send(:class_variable_set, :@@in_egA, ‘egA’) # The secret!

puts “A Class Variables”
puts A.class_variables #=> @@in_A

puts “egA Class Variables”
puts egA.class_variables #=> @@in_egA

puts “B Class Variables”
puts B.class_variables #=> empty

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs