DM> On Saturday 28 November 2009 08:43:30 am Ralph Shnelvar wrote: >> >> But see (3), below. >> >> >> >> >> >> >> >> (3) >> >> class F >> >> def sub1 >> >> @@x = 1 >> >> end >> >> end >> >> >> >> class G >> >> self.sub1 >> >> @@x=2 >> >> end >> >> end >> >> >> >> # Why? Didn't the interpreter "see" @@x in class F? >> >> F.class_variables # [] >> >> MLK> Probably not, since you haven't called the function yet! >> >> Didn't the interpreter parse it? >> >> If I do >> class X >> def y >> xyzzy = 3++ >> end >> end >> >> then the interpreter/parser will complain immediately that there was a >> syntax error even though the function method y was not executed. DM> Yes, syntax errors are caught immediately by parsing. But think of instance DM> variables as a hash associated with the object and it might be more clear. For DM> example, if you did this: DM> SomeHash = {} DM> def foo DM> SomeHash[:x] = :y DM> end DM> Would you expect SomeHash[:x] to exist just because you defined that method? DM> Now, I don't know whether instance variables are _actually_ implemented as a DM> hash. It might well be something much more efficient, but it behaves like a DM> hash, so that makes sense. DM> Also, consider: The method in question may _never_ be called. Why should Ruby DM> bother to create a variable that may never be used? That would be just as DM> foolish as implying that SomeHash[:x] exists before foo is called in my DM> example above. Sure, you could create it and set it to nil, but that'd be DM> pointless. >> In C++, instances have access to the static variables and functions of >> the class. >> >> They don't inherit it ... but merely have access to it as if they >> inherited it. DM> Instances indeed get access to class variables associated with that instance, DM> but again, class variables behave weirdly. But here's a quick example to help DM> clarify things: DM> SomeClass.new DM> Would you expect an object created that way to have a 'new' method of its own? DM> That is, would this make any sense: DM> SomeClass.new.new DM> Similarly, do the methods available at class creation time make any sense in DM> an object? For example, when creating a class: DM> class Foo DM> attr_accessor :bar DM> end DM> You might think attr_accessor is a keyword. It isn't, it's just a method on DM> Class, so it's a class method on Foo. DM> There is no proper analog to "static functions" in C++, by the way -- they're DM> just methods on the class. But again, they aren't included into the instance DM> -- you access them on the class, just like you would with any other object. DM> So let me return to some simple examples that I hope make sense. Let's create DM> a counter for the number of instances that have been created. DM> class Foo DM> def self.count DM> @count DM> end DM> def self.increment_count DM> @count ||= 0 DM> @count += 1 DM> end DM> end DM> This should be easy to understand. (If it's not, pretend I defined them DM> without self, and see if they make sense.) DM> Now, go define them, and play with them in irb. You wouldn't create any DM> instances of Foo yet, but you can do things like this: DM> Foo.count DM> Foo.increment_count DM> Foo.count DM> Foo.increment_count DM> Foo.increment_count DM> Foo.count DM> Go try that in irb, and see if the result makes sense. DM> Now let's move on. Keep the same class above, but add this -- if you're in the DM> same irb session, you can just re-open the class: DM> class Foo DM> def initialize DM> Foo.increment_count DM> end DM> end DM> Now our counter should work as expected: DM> Foo.count DM> f = Foo.new DM> Foo.count DM> It won't tell you how many Foo objects actually exist. It's more a count of DM> how many have ever been created. Cool. Is there a way to tell how many instances actually exist? Is there anything like a destructor for class instances? Just curious. DM> Also, if you understand this so far, go back to my earlier example that was DM> "way over your head" -- see if it makes sense. I'll give you an example -- if DM> you have a variable f, which is an instance of class Foo, what is f.class? DM> And if you're inside the initialize method of f, what is self? And what is DM> self.class? >> MLK> (There is a way to call private methods from outside, but I will leave >> MLK> you to find it out on your own. It's not generally a good thing, and >> MLK> I'm not going to hand you a dangerous tool until you understand when >> not MLK> to use it.) >> >> So ... when _can_ I use class_variable_get ??? DM> You can use it whenever you want. When _should_ you use it? DM> Like instance_variable_get, it's designed for metaprogramming -- that is, when DM> you're trying to access a variable, but its name is dynamic. DM> I'll give you an example of when instance_variable_get might be used. Remember DM> attr_reader? (If not, look it up...) Now, for speed, attr_reader is defined DM> in C, but it can be defined in Ruby. Here's the obvious 'eval' solution: DM> class Module DM> def attr_reader *names DM> names.each do |name| DM> eval "def #{name}; @#{name}; end" DM> end DM> end DM> end DM> But there are many reasons I dislike eval. Here's the solution I'd prefer: DM> class Module DM> def attr_reader *names DM> names.each do |name| DM> var_name = :"@#{name}" DM> define_method name do DM> instance_variable_get var_name DM> end DM> end DM> end DM> end So, basically, the reason that instance_variable_get is private to Module is that one does not wish make breaking encapsulation too easy? I mean, it seems easy enough to break encapsulation by adding an accessor, right? DM> I don't expect you to follow every detail here. The use of define_method is an DM> advanced topic already. Hopefully, though, the fact that you already know how DM> to use attr_reader should give you an idea of how that works. DM> Also, I don't really expect you to need any of this yet -- attr_reader, DM> attr_writer, and attr_accessor should already do everything you need. This is, actually, very lucid. I had few problems following any of it. Also ... I _did_ try (successfully, I hope) to follow every detail. Many many thanks.