15 Mar, 2010, flumpy wrote in the 21st comment:
Votes: 0
But although the class object is an instance, it isn't an instance of the class it's a metaclass of.. It's a shared 'instance'


To me this makes sense and I can see why they've done it like this
15 Mar, 2010, Runter wrote in the 22nd comment:
Votes: 0
flumpy said:
But although the class object is an instance, it isn't an instance of the class it's a metaclass of.. It's a shared 'instance'


To me this makes sense and I can see why they've done it like this


I'm unclear what you mean.
15 Mar, 2010, Runter wrote in the 23rd comment:
Votes: 0
Syntax macro for…
Val = Class.new
Val.instance_variable_set(:var, "easily confused")

Val.send(:define_method,:initailize) do
self.instance_variable_set(:val, "With this.")
end



Here's something else that isn't immediately obvious once you know the aforementioned.

class Meh
@@meh = "Is this"
def initialize
@@meh = "different?"
end
end

m = Meh.new

p Meh.class_variables
p Class.class_variables


[:@@meh]
[]


So yes, they both are the same thing. The area where you access the classes instance variables, i.e. the instanced Class variable's instance variables, does not let you access the instance's class variables. To be clear, the object being the Meh object. (Instanced from class.) So that area of the class definition is behaving as two different scopes for very similar opertions. (instance_variable_set and class_variable_set)
15 Mar, 2010, flumpy wrote in the 24th comment:
Votes: 0
Meh's is not a Class object, it's it's own subclass of Class.

For your example to show the same value would break encapsulation.

In your previous example, Val = Class.new seems to be creating a subclass of the object Class, thus making it an entirely separate class instance from Class (with it's own data).

I am guessing what .new does on Class here of course, but it makes sense to me…
15 Mar, 2010, Runter wrote in the 25th comment:
Votes: 0
To follow up on my last post. Here's the real reason that works the way it does. Instances of the Class object work differently than any other object.

Meh = Class.new
Meh.class_variable_set(:@@one, "blah")
Meh.send(:define_method, :initialize) do
@@two = "blah"
#self.class_variable_set(:@@two, "blah")

# Second line was no good.
end

p "Meh #{Meh.class_variables}"
p "Class #{Class.class_variables}"

# created using Meh
m = Meh.new

p "Meh #{Meh.class_variables}"
p "Class #{Class.class_variables}"


"Meh [:@@one]"
"Class []"
"Meh [:@@one]"
"Class []"


The reason this is confusing is because Meh doesn't inherit anything from Class. It's just an instance—but it's a special instance. The truth is Class is the only real class. Class is an instance of Class in fact. But it appears that there is a difference between the Class.new method and the Meh.new method. Even though they appear the same, Class.new constructs differently. Without using inheritance, it constructs a class. There's no other class that does this.

So class variables only exist for non-class instantiated objects and they're stored on the instance of the Class object that created them. That is to say, objects that are of Class type store class variables on themselves which may conflict with objects created with their new. This may indeed be desirable, but this wasn't immediately apparent to me. An instance of the Class class, according to the rules of instancing in Ruby, created with new from an instance of Class (which class is), should be no different from something created from any other instance of Class. This is a subtle difference.

So for completeness sake, as far as the class syntax macro goes, any time the @@ sigil is used either in the object created by the factory or the factory itself it refers to the factory itself. This isn't true if you're trying to use the class_variable_set method. Indeed, the method doesn't exist for objects created with the factory. And this is why the functionality appears strange at first glance. @ can refer to the Class object or the object created using the factory depending on where you use it. @@ in the scope of the factory or the object created with the factory always refers to the same class variable stored on the factory itself.
15 Mar, 2010, Runter wrote in the 26th comment:
Votes: 0
flumpy said:
Meh's is not a Class object, it's it's own subclass of Class.

For your example to show the same value would break encapsulation.

In your previous example, Val = Class.new seems to be creating a subclass of the object Class, thus making it an entirely separate class instance from Class (with it's own data).

I am guessing what .new does on Class here of course, but it makes sense to me…


Nah, you're wrong. Val = Class.new is not a subclass. It's an instance of the object Class. Class itself happens to be an instance of the object Class as well.

Metaclasses are singleton-classes stored on these instances. Which, incidentally, any object anywhere may have as well. (A singleton-class.) I.e., the singleton-class of Val, an otherwise normal object which acts as a factory. The only thing I've found that behaves differently so far from a normal object is class variables. Which is what started this confusion for me specifically. :)

This is what Ruby's creators coined first-class-objects.

Ruby Documentation said:
Classes in Ruby are first-class objectseach is an instance of class Class.

When a new class is created (typically using class Name end), an object of type Class is created and assigned to a global constant


In other words if I did this..
Factory = Class.new

obj = Factory.new

which is the same as
class Factory
end

obj = Factory.new


obj is an instance of Factory perhaps, but it's constructed using Class.new just like factory was. It's confusing chicken-before-the-egg logic hidden in the C engine. Since Class is an instance of Class and was constructed by Class.

I think subclassing is what I was expecting, but I don't necessarily hate this approach. I just wish it had been more obvious upon reading about first-class objects when I first started. :)

Anyways, I apologize dragging this thread on as long as I did, but if I helped even one person better understand this while I explored it myself then all wasn't lost. :p
15 Mar, 2010, flumpy wrote in the 27th comment:
Votes: 0
OIC, that makes sense.

It made sense syntactically before to me, but now at least I undestand whats going on inside ruby too :D
15 Mar, 2010, Tyche wrote in the 28th comment:
Votes: 0
I guess the best illustration is code.

#include <iostream>
using namespace std;
class A {
private:
static int a;
public:
static void set(int _a) {
a = _a;
};
static int get() {
return a;
};
};

class B : public A {
private:
static int a;
public:
static void set(int _a) {
a = _a;
};
static int get() {
return a;
};
};

int A::a = 0;
int B::a = 0;

int main() {
A::set(1);
cout << A::get() << endl;
B::set(2);
cout << B::get() << endl;
cout << A::get() << endl;
}


$ g++ classvar.cpp ; ./a
1
2
1

class A
def A.set a
@@a = a
end
def A.get
@@a
end
end
class B < A
def B.set a
@@a = a
end
def B.get
@@a
end
end

A.set 1
p A.get
B.set 2
p B.get
p A.get


$ ruby classvar.rb
1
2
2

They are like class global variables. While they are defined as instance variables on each class, no matter where you are in the heirarchy from the top class where the variable is define to the bottommost sub class, there is to all appearances only one variable. It's like they exist in the class instance variable but actually point to the same storage address.

While it's somewhat useful, it's not at all what you'd expect coming from C++.
15 Mar, 2010, Runter wrote in the 29th comment:
Votes: 0
Tyche: I'm curious if you use Rails?
15 Mar, 2010, Tyche wrote in the 30th comment:
Votes: 0
Runter said:
Tyche: I'm curious if you use Rails?


I did. The TeensyMud site is a rails app. The code for the site is in the repository here under 'web related'.
But it's old won't run on anything higher than Rails 1.1.6. But later I discovered Nitro and liked that better.
But Nitro is a dead project.
15 Mar, 2010, David Haley wrote in the 31st comment:
Votes: 0
The Ruby behavior as Tyche posted makes perfect sense to me, for the reason I stated earlier. FWIW I wasn't coming from a C++ background (nor Python nor Lua nor …), I was making that statement out of principle.

Anyhow the C++ code that Tyche gave is doing something completely different from the Ruby code, namely it redefines 'a' on B… so of course a different 'a' is being set! (it's just that C++ static variables aren't like class variables) Its output is also perfectly sensible.

The reason it makes sense is that when you look up some variable 'x' on an instance, you would expect the following to happen:
- Go to the instance, see if it has an 'x'.
- If not, go to the instance's class, see if it has an 'x'.
- If not, keep going up the inheritance tree to see if it has an 'x'.

This is exactly what happens for looking up a method on an instance, so I'm not sure why it should be any different for values.
15 Mar, 2010, Runter wrote in the 32nd comment:
Votes: 0
Quote
This is exactly what happens for looking up a method on an instance, so I'm not sure why it should be any different for values.


Well, the confusing thing for me really was the special case for class variables inside of certain objects. Namely inside of instances of the Class object.

Factory = Class.new
# Roar is an instance of Class.

o = Factory.new
#o is an instance.


If we access a class variable by any name inside of o it would be stored on Factory.
If we access a class variable inside of Factory it would also be stored on Factory.

The behavior I would have expected here is that a class variables accessed from Factory was in fact stored on Class since it is an object instantiated from Class just like o is instantiated from Factory.
15 Mar, 2010, David Haley wrote in the 33rd comment:
Votes: 0
Well, a class variable shouldn't propagate upwards unless it already existed up there. It'll get stored at the appropriate place based on where it was created. Does that make sense? Kinda pressed for time now so being brief. (or maybe I'm misunderstanding what you're saying)
15 Mar, 2010, Runter wrote in the 34th comment:
Votes: 0
David Haley said:
Well, a class variable shouldn't propagate upwards unless it already existed up there. It'll get stored at the appropriate place based on where it was created. Does that make sense? Kinda pressed for time now so being brief. (or maybe I'm misunderstanding what you're saying)


The way it actually behaves doesn't bother me. And practically it probably doesn't matter but I'll attack this problem from a different perspective.

o = Class.new


We would expect this to be an object just like any other. Unfortunately, this is not true. There's a very special case that isn't very well documented for objects of this type. And that behavior is that class variables associated with this object are stored on the object itself. Effectively not making it a class variable at all.
o = Class.new
o2 = Class.new


We would expect that a class variable by the name of @@anything would share values between o and o2 since they are instanced from the same class. This is not the case. They'd actually have unique values.

In any other case where we instance an object the instances do share class variables. Again, this may be the correct way to go but it needs to be more clearly explained. This actually could effect someone writing code assuming they can have class variables for these instances of Class.

Oh, and the reason I say it probably doesn't matter is because typically any object you're actually instantiating and dealing with isn't from Class.new. It's just when you hear that it's a regular instanced object you may assume it can behave just like other instanced objects. There's at least 2 things where this isn't true. I showed once of those two things. The second one involves how these objects use singleton-classes.
15 Mar, 2010, David Haley wrote in the 35th comment:
Votes: 0
Oh, I see now what you mean. Yes that is a little strange, from a certain point of view. My guess is that class variables work by going up the instance/class pointers until you find something that is of type class. In this case, 'o' itself is of type class so it just says "ok, I'm done, I'll stick it here". From this point of view, it seems normal enough.

This is a complaint I have with both Ruby and Python: it's difficult to find documentation on the exact behavior for things like this. The language that shan't be named is perhaps simple enough that the exact mechanisms can be very well documented, which makes for less confusion on issues like this. We were talking about something similar the other day w.r.t. method hooks, IIRC.
15 Mar, 2010, flumpy wrote in the 36th comment:
Votes: 0
Would you prefer Class.new to be called something else? Class.clone? Class.instanciate?

I think it's reasonable, in a language of convention, to mention a break with that convention and have done with it.
15 Mar, 2010, Runter wrote in the 37th comment:
Votes: 0
flumpy said:
Would you prefer Class.new to be called something else? Class.clone? Class.instanciate?

I think it's reasonable, in a language of convention, to mention a break with that convention and have done with it.


I don't know what your point is.

If it has anything to do with a technical difference between Class.new and AnyObject.new you'd be wrong. In fact, calling AnyObject.new calls Class.new.


Ruby touts the "Everything is an object" mantra and then sets rules you should be able to count on from behavior of all objects. It's not unreasonable to want to know when there are real differences between one type of instanced object and every other type when they've went to extraordinary lengths to bridge differences.

It also touts itself as following the principle of least surprises. I find this surprising since it isn't actually mentioned anywhere when outlining how objects behave.

If I tell you that A is an object. Then I go on to tell you that all objects do B then you'd expect A to do B. That's my only point.
15 Mar, 2010, flumpy wrote in the 38th comment:
Votes: 0
Ah, but if I'd overridden new in any object, you wouldn't expect different behaviour either, no? The difference would be if I mentioned the behaviour in the docs, which then becomes reasonable and fair.

No other point really, either :D
15 Mar, 2010, Runter wrote in the 39th comment:
Votes: 0
flumpy said:
Ah, but if I'd overridden new in any object, you wouldn't expect different behaviour either, no? The difference would be if I mentioned the behaviour in the docs, which then becomes reasonable and fair.

No other point really, either :D


Well, yeah, but I wouldn't expect you to be able to override the way class variables behave on principle unless you changed the C core.
15 Mar, 2010, David Haley wrote in the 40th comment:
Votes: 0
It's very frustrating to search for material on this. Lots of people have identified the problem (in fact the first page of Google results for "ruby class variable" is full of people complaining about or otherwise noting the problem). But it's hard to find Ruby documentation on what exactly class variables mean. This is another problem with a "buzzy" language (in that it generates a lot of buzz): search results end up going to random blog posts rather than official websites. :rolleyes:

But… I finally found something interesting. Here's a Ruby specification.

In the case of assignment… go to logical page 53 (actual page 69 in the PDF).

Quote
If the variable is a class-variable-identifier :
1) Let C be the first class or module in the list at the top of [class-module-list] which
is not an eigenclass.
Let CS be the set of classes which consists of C and all the superclasses of C. Let
MS be the set of modules which consists of all the modules in the included module
lists of all classes in CS. Let CM be the union of CS and MS.
Let N be the class-variable-identifier.

2) 1 If one of the classes or modules in CM has a binding with name N in the set of
bindings of class variables, let B be that binding.
If more than one class or module in CM has bindings with name N in the set
of bindings of class variables, let B be one of those bindings. Which binding is
selected is implementation defined.
Replace the value of B with V.
3) If none of the classes or modules in CM has a binding with name N in the set of
bindings of class variables, create a variable binding with name N and value V in
the set of bindings of class variables of C.


You get similar cases for 'defined?' (logical page 64, actual 80) and for evaluation of a class variable (logical page 82, actual 98). In particular:

Quote
11.4.3.4 Class variables
A class-variable-identifier is evaluated as follows:

a) Let N 1 be the class-variable-identifier. Let C be the first class or module in the list at the
top of [class-module-list] which is not an eigenclass.
b) Let CS be the set of classes which consists of C and all the superclasses of C. Let MS be
the set of modules which consists of all the modules in the included module list of all classes
in CS. Let CM be the union of CS and MS.
c) If a binding with name N exists in the set of bindings of class variables of only one of the
classes or modules in CM, let V be the value of the binding.
d) If more than two classes or modules in CM have a binding with name N in the set of
bindings of class variables, let V be the value of one of these bindings. Which binding is
selected is implementation dependent.
e) If none of the classes or modules in CM has a binding with name N in the set of bindings
of class variables, let S be a direct instance of the class Symbol with name N and raise a
direct instance of the class NameError which has S as its name property.
f) The value of the class-variable-identifier is V.



The interesting piece is how "class-module-list" is constructed. See logical page 7, actual 23:

Quote
class-module-list: A logical stack of lists of classes or modules. The class or module at
the head of the list which is on the top of the stack is called the current class or module.


Therefore, when the "self" in question (see right above that quotation) is a class, then it is itself the class at the top of the class module list.

So I think that the specification does indeed define what we have discussed here as the proper behavior.


The closest I could find to this in "normal English" in the Ruby docs was this:
Ruby docs said:
Class variables belong to the innermost enclosing class or module. Class variables used at the top level are defined in Object, and behave like global variables. Class variables defined within singleton methods belong to the receiver if the receiver is a class or a module; otherwise, they belong to the class of the receiver.

which IMHO doesn't make clear at all what is happening.
20.0/48