From: "Chr. Rippel" Date: 2002-02-14T05:14:54+09:00 Subject: Re: Name resolution in Ruby "Alan Stern" wrote in, .... > This is currently under discussion in a separate thread. I think a > consensus is growing that class variables really ought to behave more > like instance variables of the class itself, with appropriate accessor > methods. I wrote something to this affect but this was not quite what I mend. The current class variable behavior is really very usefull but it is possible to simulate this behavior with instance and/or local variables and accessor methods. This has the added advantage of a more fine tuned control of visibility (and no interpretation headaches). Here is an example of a public ``class attribute" with a protected writer attribute that is invisible on the meta level ---- class A var = "a" define_method(:var) { var } protected define_method(:var=) {|x| var = x } end p A.new.var # => "a" class B < A def initialize(x) self.var = x end end B.new(3) p A.new.var # => 3 class << A var # raises a NameError exception end ---- Since we already have an implementation of class variables there is no point to get rid(t?)e of them (they are definitely faster;-). However it is at least a theoretical possibility to for go their implementation in a rewrite like Rite. > .... > > a class is just an object like any other objects. > > While largely true, that's not entirely correct. Ordinary objects and > classes differ in the way their metaclasses are created. For > instance, suppose that x is an ordinary object (not a class) and that > C is a class. Then C always has a metaclass -- it is created at the > same time as C (I think) -- but x only has a metaclass if you create > one explicitly. Also, if C is a subclass of B, then Meta-C is a > subclass of Meta-B. This means that, although Meta-x's superclass is > x's original class, Meta-C's superclass is not C's original class but > instead is C's superclass's metaclass. This is true but this is not relevant for scoping rules, in fact it is possbile to define constants on the meta class level or more generally singleton classes. I appended a script illustrating the Meta-class system (note the use of class variables;-) .... > > A> The overall effect is that constants behave as though they really do > > A> have a static scope. > > > > This is the intented use, I think. Local scope is an illusion created by coding convention on top of Ruby's static scope - but you can always should yourself in the foot;-) ---- A = Class.new x = Class.new A Sub = Class.new x Sub.const_set :Super ,x p Sub.ancestors # => [Sub, Sub::Super, A, Object, Kernel] class Sub class Super p Module.nesting # => [Sub::Super, Sub] end end Normal = x class Normal # prevent creation of Sub::Super::Sub Sub = ::Sub class Sub p Module.nesting # => [Sub, Sub::Super] end end class A L = "A" end class Sub L = "Sub" class Super K = 1 p L # => "Sub" end remove_const :L p K # => 1 class Super p L # => "B" end end class Binding def search_path nest = eval("Module.nesting",self) __self =eval("self",self) if __self.is_a? Module nest.concat __self.ancestors.select {|m| not nest.include? m} else nest.concat __self.type.ancestors.select {|m| not nest.include? m} end end end class Sub class Super p binding.search_path # => [Sub::Super, Sub, A, Object, Kernel] end end # Search path is not transitive! class A; F = "A" end class B < A; end class C < B; end class C G = "visible in C::Z" p Module.constants - Object.constants # => ["F", "L"] class X; end class Y < X;end class Z < Y p Module.constants - Object.constants # => ["G", "Z", "X", "Y"] p binding.search_path # => [C::Z, C, C::Y, C::X, Object, Kernel] end end ---- /Christoph ---- # A meta class illustrations script class Class @@to_s = nil @@level = nil attr_reader :inst def superclasses res = [self] tmp = self res.push tmp while tmp = tmp.superclass return res end def each_superclass klass = self yield klass yield klass while klass = klass.superclass self end def level @level || 0 end def singleton?; true end alias :old_to_s :to_s private :old_to_s def to_s if singleton? if level.zero? (old_to_s.sub(/^(#)$/){"_Sing(#{inst})"} else @to_s end else old_to_s end end alias :inspect :to_s protected def __meta super @@to_s = to_s.concat '_Meta' @@level = level + 1 @meta.instance_eval do @to_s = @@to_s @level = @@level end superclass.__meta if superclass if level.nonzero? def self.__meta; @meta end end return @meta end end class Object @@inst = nil class << self def singleton?; false end undef :inst end def meta(level = 1) return self if level.zero? return __meta.meta(level-1) end def singleton_type class << self; self end end protected def __meta @meta = singleton_type @@inst = self @meta.instance_eval do @inst = @@inst end @meta end end class A; end class B < A; end class C < B def initialize num @num = num end def inspect; @num.inspect end alias :to_s :inspect end def class_chain klass unless klass.level.zero? && klass.singleton? puts klass.superclasses.join(" < ") else res = klass.superclasses puts res.shift.to_s.concat(" << ").concat(res.join(" < ")) end end # Examples class_chain C.meta class_chain Class.meta class_chain C.new({}).meta class_chain Math::E.meta puts "\n\"Higher\" order\n\n" class_chain Object.meta(2) class_chain C.meta(2) class_chain Math::PI.meta(2) class_chain Module.meta(3) class_chain C.meta(3) n = 0 ObjectSpace.each_object(Class) do |klass| klass n+=1 if klass.singleton? end puts "\nNumber of Singletons controlled by GC == #{n}" ---- C_Meta < B_Meta < A_Meta < Object_Meta < Class < Module < Object Class_Meta < Module_Meta < Object_Meta < Class < Module < Object C_Sing({}) << C < B < A < Object Float_Sing(2.718281828) << Float < Numeric < Object "Higher" order Object_Meta_Meta < Class < Module < Object C_Meta_Meta < Class < Module < Object Float_Sing(3.141592654)_Meta < Class < Module < Object Module_Meta_Meta_Meta < Class < Module < Object C_Meta_Meta_Meta < Class < Module < Object Number of Singletons controlled by GC == 0 ----