From: Austin Ziegler Date: 2006-05-06T12:20:59+09:00 Subject: Re: Constant in Ruby. I'm going to start from a few basic principles, because Ruby worksdifferently than you think it does, and that's kinda obvious from howyou're not getting this. Before I get started, I think that the article you read has done you agreat disservice because the authors raised a point that isn't actuallya problem in practice in Ruby. (And even in C++, it is pretty easy tosubvert the const-ness of a variable. It's a little harder to subvertthe const-ness of a reference, but not impossible.) Most of what I'm going to say here is similar to what Jim Weirich saidin his "Shoeboxes and Bindings"[1] essay. You probably came from theclass of languages descending from C, where each variable is a place inmemory. For non-pointer operations, simple assignment is duplication: std::string a, b; a = b = "Hello"; In this C++ example, you have two shoeboxes that each contain a copy ofa string, "Hello": a [ "Hello" ] b [ "Hello" ] In C and C++ you can use pointers and references to alias theseshoeboxes. std::string& c; std::string* d; c = a; d = &b; c += ", world!"; (*d) += ", program!"; In this C++ example, you have two more shoeboxes. These are specialshoeboxes in that they don't contain strings; they contain the addressesof strings. c [ address-of a ] -> a [ "Hello, world!" ] d [ address-of b ] -> b [ "Hello, program!" ] In C++, the variable d has its own storage. Because it's a pointer tostd::string, it's going to be sizeof(void*). It's even possible to havea pointer to d: std::string** e = &d; Which is: e [ address-of d ] -> d [ address-of b ] -> b [ "Hello, program!" ] It's turtles all the way down. The only time that it gets a little diceyis when you allocate memory on the heap: std::string* f = new std::string("Greetings, program!"); std::string** g = &f; But even that is pretty easy to deal with, conceptually. In this case,we're given a handle to the memory -- the address. It might look like: f [ address-of g ] -> g [ address ] -> [ "Greetings, program!" ] Like I said, pretty basic stuff. ... And completely *not* the way that Ruby works. Ruby variables are closer to the behaviour of C/C++ pointers andreferences, except that they are not shoeboxes themselves. This is animportant thing to remember. Jim calls them "bindings." I call them"labels." Either way, they're essentially local names for values. The fundamental in Ruby is not a variable, but an object: "Hello" This is an object. It can have things done to it and it can be passedaround. However, this object is anonymous. We don't know its name, or itdoesn't have one. Because it is anonymous and isn't bound anywhere, itmay end up going out of scope quickly and be eligible for garbagecollection. So, we give an object a name: x = "Hello" Now, we can manipulate the object without fear that it's going to go outof scope and be garbage collected when we need to use it. Therepresentation is something like: x -> [ "Hello" ] Note the difference in the way that I showed this from the way that Ishowed the C++ example: x -> [ "Hello" ] c [ address-of a ] -> a [ "Hello, world!" ] g -> [address of f ] -> f [ address ] -> [ "Greetings, program!" ] The form with 'f' is a little closer to the Ruby view of variables, butit's still a piece of storage that can have its address taken as well,as the 'g' shows. Well, that won't work with Ruby. x = y = "Hello, muddah." That would look something like: x ------v [ "Hello, muddah." ] y ------^ We now have the object "Hello" referred to by two different names. Butif I assign to y again: y = "Hello, faddah." I the two separate names pointing to two objects. x -> [ "Hello, muddah." ] y -> [ "Hello, faddah." ] Pretty simple so far, and clear. Ruby throws one loop at us, but it'snot nearly as bad as the object/primitive distinction that Java gives.Some Ruby objects exist exactly once and are never garbage collected.This is partially for performance reasons, but it also makes sense. Thevalues that do this are Fixnums (31-bit signed integer values), Symbols,and true, false, and nil. These values are called "immediate" values.These objects are immutable. It is possible for a class to simulateimmediacy without the object itself being a full-on immediate value. Ibelieve that Bignums are implemented this way. 99% of the time, the distinction between immediate and reference valueswith the binding approach that Ruby takes to variables. You simply binda variable to an object; you don't make a storage space that you thenput an object or a reference to that object to. There are no shoeboxes.Ruby only has to worry about memory management of objects; you don'thave to worry about any of it. Now, how does this apply to constants? Well, in Ruby, constants are justspecial variables. The bindings *can* be changed, but Ruby will complainon binding them to different objects. Unless a mutable object is frozen,though, the contents of the object referred to by object can bemodified. Note the important words on that last sentence: mutable object.Immediate values -- Fixnums -- are immutable. That's why 10.freeze(which is what x = 10; x.freeze does) is a no-op. Hopefully, that helps. -austin[1] http://onestepback.org/index.cgi/2003/12/15--Austin Ziegler * halostatue@gmail.com * Alternate: austin@halostatue.ca