From: Christoph Date: 2002-08-16T14:56:15+09:00 Subject: Re: Thought question: Where does "new" come from? "MikkelFJ" wrote in .... > I didn't track this thread, but today I independently tried to write up the > scoping rules for Ruby. I think they are very complex and difficult to > explain. Any given expression is working in a context of about 5 different > kinds of scope at the same time. I am not sure if I would call the rest of Ruby simple (undoubtedly a reflection of my own limitations) but I agree with your point that Ruby's scoping rules are ``somewhat'' complex. Learning and understanding these different scoping rules is probably a big part in becoming proficient in writing, and even more so, reading Ruby code. Anyway, I sort of counted seven types of variables or constants in Ruby. local variables dynamic local variables (whatever those may be ...?) thread local variables instance variables class variables global variables constants On top of this, there is a subtle interaction and slightly different scoping rules between ``normal scope'' and ``eval scope'' (for local variables). In addition, we have method names which can interfere with local variables or constant names - the most prominent example being the infamous attr_writer, versus local variable confusion. --- class Object attr_writer :surprise def surprise_test surprise = "surprise" p @surprise self.surprise = "okay" p @surprise end end surprise_test # nil # "okay" --- > > This is perhaps a weakness of Ruby - it makes an otherwise very easy > language very hairy in some aspects. Or is it because of this that most > things are easy - such as being able to have temporary variables in a class > definition? Yes these ``extra local'' variables add another level of complexity - they are great too play around;-) and even better to p..s my time away;-( ... --- t = "changed" s = "changed" A = Class.new { define_method(:outer_t=) {|s,| t =s } define_method(:outer_t) { puts t } } class A t = "unchanged" define_method(:inner1_t) { puts t } end class A t = "changed" define_method(:inner2_t= ) {|s,| s,t = t,s; t } define_method(:inner2_t) { puts t } class << A; self end. \ send(:define_method,:inner2_t) { puts t } end a = A.new a.outer_t = "outer" a.inner2_t = "inner" a.outer_t # outer a.inner1_t # unchanged a.inner2_t # inner A.inner2_t # inner p [s,t] # ["outer", "outer"] --- > > Below is what I wrote - which ended up being just examples because other > attempts of description got rather complex. Disclaimer - it may not be > correct and the terms are certainly not official Ruby terms. .... > However: > > @a = 2 > > def double_a > @a + @a > end > > double_a # -> 4 > > Variables starting with @ are visible both inside and outside of functions. Actually this has a fairly simple explanation. The ``double_a'' declaration on the ``main scope'' defines a private Object instance method available for the ``main'' or any other object - e.g. --- p $main = self # main def double_a puts @a + " : " + @a end p Object.private_instance_methods # ["double_a", "initialize"] p $main.singleton_methods # ["public", "include", "private", "to_s"] public :double_a @a = "$main @a doubled is" 1.instance_eval { @a = "1 @a doubled is" } double_a # $main @a doubled is : $main @a doubled is $main.double_a # $main @a doubled is : $main @a doubled is 1.double_a # @a doubled is : 1 @a doubled is ---- Of course this begs the question why Matz didn't simply set $main = Object or possibly $main = Kernel? > > This chapter will explain the non-trivial scoping rules of Ruby. In the end the scoping rules for instance, class variables (at least their recent behavior) and constant are actually fairly logical and consistent. On the hand, the scoping rules for local variable are imo the least satisfying ones. .... > class Foo > > @a = 2 #this is a static object member variable > > def Foo.hello > #this is a static object method > puts @a #same @a as above > end You are mixing many different issues here. Declaring, changing and using instance and/or class variables, and declaring instance and/or singleton methods - e.g. --- class Class @a = "Class @a" def hello @a end end def Object.world puts @a + " : " + @a end class Foo @@a = "Foo a@@" def bar @a = "Foo inst @a" puts [Foo.hello,Bar.hello, Foo.bar].join(", ") end end Foo.instance_eval { @a = "Foo @a" } class Bar @@a = "Bar @@a" @a = "Bar @a" def Foo.bar @@a end end Class.world # Class @a : Class @a Foo.world # Foo @a : Foo @a Bar.world # Bar @a : Bar @a Foo.new.bar # Foo @a, Bar @a, Bar @@a --- /Christoph