Class-tree variables


#1

There’s been a lot of blog traffic lately about class variables vs.
class instance variables.
I’d like to give an example of something in-between: class-tree
variables. These are sort
of like class variables, but they only point downward in the
inheritance tree rather than
up. That is, each class in an inheritance tree can get a variable
which refers to itself and
all its descendents. Class-tree variables are implemented by class
variables, but the
implementation occurs only in the top (root) class and is inherited by
the rest of the tree.
It is probably unclear what the heck I am talking about, but the
following example should
make things clear.

class_tree_var.rb: demo program of class-tree variables

#-- Hey Emacs! Use -- mode: ruby; coding: utf-8-unix; --
class Person
attr_accessor :name

class << self
attr_writer :instances
def instances
@instances ||= []
end
end

def initialize( name )
klass = self.class
until klass == Person
klass.instances << self
klass = klass.superclass
end
Person.instances << self
@name = name
end

end

class Scholar < Person
end
class Student < Scholar
end
class Professor < Scholar
end
%w(Fred Barney).each{|caveman| Person.new(caveman)}
%w(Wilma Betty).each{|cavewoman| Scholar.new(cavewoman)}
%w(Twerply Bob Sheila).each{|brat| Student.new(brat)}
%w(Whiplash Grindemdown).each{|sadist| Professor.new(sadist)}
puts "Persons are " + Person.instances.collect{|person|
person.name}.join(’, ‘)
puts "Scholars are " + Scholar.instances.collect{|scholar|
scholar.name}.join(’, ‘)
puts "Students are " + Student.instances.collect{|student|
student.name}.join(’, ‘)
puts "Professors are " + Professor.instances.collect{|professor|
professor.name}.join(’, ')

end of file

$ ruby class_tree_var.rb
Persons are Fred, Barney, Wilma, Betty, Twerply, Bob, Sheila,
Whiplash, Grindemdown
Scholars are Wilma, Betty, Twerply, Bob, Sheila, Whiplash, Grindemdown
Students are Twerply, Bob, Sheila
Professors are Whiplash, Grindemdown

I don’t actually recommend this. I think that in Ruby it’s better not
to use inheritance for
such things; you have an abundance of alternatives that are more
flexible. Functionality
built into inheritance tends to break upon refactoring. But I think
this example does shed
some light on what class instance variables are capable of. It did
for me, anyway.

Regards, Bret
Bret Jolly


#2

Hi –

On Fri, 9 Feb 2007, oinkoink wrote:

There’s been a lot of blog traffic lately about class variables vs.
class instance variables. I’d like to give an example of something
in-between: class-tree variables. These are sort of like class
variables, but they only point downward in the inheritance tree
rather than up. That is, each class in an inheritance tree can get
a variable which refers to itself and all its descendents.
Class-tree variables are implemented by class variables, but the
implementation occurs only in the top (root) class and is inherited
by the rest of the tree.

I think that’s a typo; I don’t see any class variables in your code.

def instances
Person.instances << self
end
puts "Professors are " + Professor.instances.collect{|professor|
I don’t actually recommend this. I think that in Ruby it’s better
not to use inheritance for such things; you have an abundance of
alternatives that are more flexible. Functionality built into
inheritance tends to break upon refactoring. But I think this
example does shed some light on what class instance variables are
capable of. It did for me, anyway.

It’s a good illustration of the fact that instance variables are the
way to maintain state per object, and that that’s the same for class
objects as for other objects.

David


#3

On Feb 8, 12:41 pm, removed_email_address@domain.invalid wrote:

by the rest of the tree.
I think that’s a typo; I don’t see any class variables in your code.

Yes, sorry. I meant “class instance variables” (and also
“descendants”) but the
word “instance” got lost in careless editing.


#4

On Feb 8, 2007, at 3:25 PM, oinkoink wrote:

That is, each class in an inheritance tree can get a variable
which refers to itself and
all its descendents. Class-tree variables are implemented by class
variables, but the
implementation occurs only in the top (root) class and is inherited by
the rest of the tree.

Ruby’s class variables already have this behavior, sort of.
The gotcha is that order matters. More info after the example…

class A; end
class B < A; end

class A; @@alpha = 42; end # establishes @@alpha for A and
descendants
class B; @@alpha = 52; end # reuses @@alpha from A
class A; @@alpha; end # 52
class B; @@alpha; end # 52

#################

class C; end
class D < C; end
class E < C; end
class D; @@beta = 42; end # establishes @@beta for B and
descendants
class C; @@beta = 52; end # establishes @@beta for A and
descendents
# excluding B since that branch
already
# has @@beta defined!
class C; @@beta; end # 52
class D; @@beta; end # 42
class E; @@beta; end # 52

There are three issues that make Ruby’s class variables obtuse:

  1. The inheritance tree lookup procedure described above.
  2. The lexical scoping used to initiate the lookup (see below).
  3. The name ‘class variable’, which has a much different meaning
    in Ruby than in other languages because of 1) and 2)

Lexical Scoping

The starting class for class variable name resolution (step 1) is
determined lexically (e.g., by the parser) not dynamically:

$ cat lexical.rb
class A
@@alpha = 42 # lexically within A, @@alpha anchored to A
end

@@alpha = 52 # lexically in top-level, @@alpha anchored to Object

p (class Object; @@alpha; end) # 52
p (class A; @@alpha; end) # 42

p A.class_eval { @@alpha } # 52! lexically at top-level, not A!

def A.show_alpha
@@alpha # lexically at top-level!!!
end

p A.show_alpha #52!
$ ruby lexical.rb
52
42
52
52

########################################

$ cat lexical2.rb

@@alpha = 52 # lexically in top-level, @@alpha anchored to Object

class A
@@alpha = 42 # lexically within A, @@alpha anchored to A
# but @@alpha already defined in ancestor (Object)
# so @@alpha from Object is referenced
end

p (class Object; @@alpha; end) # 42
p (class A; @@alpha; end) # 42
$ ruby lexical2.rb
42
42

Gary W.


#5

Hi –

On Fri, 9 Feb 2007, Gary W. wrote:

The gotcha is that order matters. More info after the example…
Also you wouldn’t want to use class variables to maintain state for a
class, since they’re considered the property equally of all the
instances.

class C; end
class D < C; end
class E < C; end
class D; @@beta = 42; end # establishes @@beta for B and descendants
class C; @@beta = 52; end # establishes @@beta for A and descendents
# excluding B since that branch already
# has @@beta defined!

I think there’s an error here; C and D aren’t related to A and B.

in Ruby than in other languages because of 1) and 2)

  1. The @@ prefix, which leads people to think there must be
    some similarity or kinship with instance variables. Class
    variables would be better off using $$, because they’re more
    closely related to globals than to instance variables.

@@alpha = 52 # lexically in top-level, @@alpha anchored to Object

p (class Object; @@alpha; end) # 52
p (class A; @@alpha; end) # 42

p A.class_eval { @@alpha } # 52! lexically at top-level, not A!

I think the scope of class_eval is pretty much flat with the scope
it’s created in, even for constants and class variables.

def A.show_alpha
@@alpha # lexically at top-level!!!
end
p A.show_alpha #52!

I’m not sure whether it’s lexical scoping or just the fact that A’s
singleton class is sharing @@alpha with Object. If you do:

class String
p @@alpha
end

you’ll also get 52. Also, if you do:

class << A
@@alpha = 1
end

p @@alpha

Object’s @@alpha will be 1.

David


#6

On Feb 8, 2007, at 4:33 PM, removed_email_address@domain.invalid wrote:

I think there’s an error here; C and D aren’t related to A and B.

My bad, pay attention to the code, not the comments. Comments should
have had D for B and C for A.

p @@alpha

end
you’ll also get 52.

@@alpha has already been established in Object, which
takes precedence over anything in String (since it was
defined first). Consider:

irb(main):001:0> class A; end
=> nil
irb(main):002:0> class B
irb(main):003:1> def A.foo
irb(main):004:2> @@alpha
irb(main):005:2> end
irb(main):006:1> @@alpha = ‘B scope’
irb(main):007:1> end
=> “B scope”
irb(main):008:0> A.foo
=> “B scope”

In this example the definition of A::foo is lexically within the
(class B;end) block and so @@alpha is resolved relative to B. Not to
the top-level or relative to B’s singleton class.

Also, if you do:

class << A
@@alpha = 1
end

p @@alpha

Object’s @@alpha will be 1.

Well it turns out that the scope created by “class <<A; end” is not
at all like the scope created by “class A; end”, at least with respect
to class variables. Throw away all the stuff above and consider fresh:

class A
@@alpha = 1
end

p (class <<A; @@alpha; end) # warning, top-level scope for @@alpha

In fact, I couldn’t figure out how to even create a class variable
that was anchored to the singleton class of a class. I didn’t try too
hard though…

Gary W.


#7

oinkoink wrote:

implementation occurs only in the top (root) class and is inherited by
the rest of the tree.

This is a solution to a slightly different problem (inheritance of
hashes of attributes), but it might be interesting anyway:

http://redshift.sourceforge.net/superhash/

A little example:

require ‘superhash’

class A
class_superhash :options

options[:foo] = “A foo”
options[:bar] = “A bar”

def options; self.class.options; end
end

class B < A
options[:foo] = “B foo”
end

p A.options
p B.options.to_hash
p B.new.options.to_hash

END

output:

{:foo=>“A foo”, :bar=>“A bar”}
{:foo=>“B foo”, :bar=>“A bar”}
{:foo=>“B foo”, :bar=>“A bar”}