[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Class-tree variables

oinkoink

2/8/2007 8:21:00 PM

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

6 Answers

dblack

2/8/2007 8:41:00 PM

0

oinkoink

2/8/2007 8:47:00 PM

0

On Feb 8, 12:41 pm, dbl...@wobblini.net wrote:
> 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.

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

Gary Wright

2/8/2007 9:14:00 PM

0


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 Wright




dblack

2/8/2007 9:33:00 PM

0

Joel VanderWerf

2/8/2007 9:51:00 PM

0

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

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

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

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"}

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Gary Wright

2/8/2007 10:33:00 PM

0


On Feb 8, 2007, at 4:33 PM, dblack@wobblini.net wrote:
>> 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.

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

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

@@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 Wright