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.