Wednesday, March 28, 2007

Java Is Plato's Republic

Plato's Republic is heavily influenced by geometry, the big intellectual innovation of the time. Of course the major weirdness of geometry is that you can design a real object like a building, and base that design on an unreal thing, like a square or a circle or a triangle, and it'll work. So the big disconnect is that the logic of squares and circles and triangles is powerful and effective and works in a pragmatic sense, and yet no true perfect squares, circles, or triangles exist in real life.

So the solution to this is to posit that outside the universe there dwells a pure realm of math, where the real squares and triangles et cetera exist, and to say that our own world is just a shadowy reflection of that pure greatness.

And this philosophy has all kinds of consequences, some of them markedly non-beneficial, but that's another subject. The reason I bring all that up is that's Java.

Anybody who comes to Ruby from Java will sooner or later encounter confusion about the assigning of instance variables to classes. You would think those instance variables would then become available to instances of the class, as class variables; in fact, from the point of view of an instance of a given class, an instance variable assigned to a class just disappears into a cloud of mystery.

The solution is that an instance variable assigned to a class isn't actually assigned to a class at all; it's assigned to a Class. Ruby's object model is very closely based on Smalltalk's, where classes themselves are objects in the system, available for real-time modification by the programmer. This means that if you assign an instance variable to a class, it's available to methods within the class itself, but of course not to instances of the class, because in Ruby, a class isn't an abstract Platonic definition that sits outside the world itself. It's right there in the world with everything else, and an instance of it is just something which implements the definition it provides.

6 comments:

You could say this about almost any early-bound language. C++, C#, VB.Net, etc. are the same way.

Smalltalk was the first OOP language I programmed in years ago (briefly). We used class and instance variables. I remember asking a C++ programmer the question, "What's the equivalent of a class variable in C++?" The answer is a static variable with class scope. The same goes for class methods (as opposed to instance methods). The thing that's a bit weird is in Smalltalk a class variable's value exists for eternity, for as long as the class exists in the image, or until somebody changes it. That's a foreign concept to most programmers, because to them programs have a lifetime. You run them. They run for a while (or indefinitely if they're designed that way), and then they stop. All variable values are gone.

Another difference, from my experience, is that class methods have a sense of "self" (ie. a "this"), whereas static methods in C++, Java, etc. do not. In C++, for example, you can't take a "this" and cast to a base class's version of the "overloaded" method you're in (a method of the same name as the base class's method), when in a static method. You have to use scoping syntax. I haven't tried it, but it looks to me like in Smalltalk you could do this (no casting, but using the "super" keyword).

I don't know exactly what you mean. The big frustration in my life right now is that my Seaside/Smalltalk learning got waylaid by other stuff going on in my life. I don't think there's such a thing as a static method in Smalltalk, in the Java sense, is there?

Smalltalk doesn't have static methods. Static methods are weird, they don't belong to anything, they are really just scoped free floating functions.

Smalltalk is simple. Everything is an object. The term "object" means an instance of a class. All objects have instance methods, and instance variables. All objects have a superclass, and can refer to them via the keyword #self. All classes are a running instance of some other class somewhere in the system. Since classes are also themselves objects, then it follows that they can have instance variables and methods.

Classes also have what are called class variables, this is like what'd you think of as static, ie both the class and its instances can see these. These are mainly used to declare constants I think.

When you declare a class...

Object subclass #Customer

another class is created for you automatically that parallels that using your classes name plus the word class.

#Customer superclass is #Object, #'Customer class' superclass is #'Object class'. These are called meta classes. The meta class is a subclass of the meta class of the class you were subclassing.

So when you edit the class side, you're actually editing the hidden meta class definition that defines your class, which is its sole instance.

Therefore, class methods are actually just ordinary methods, unlike static methods. Class methods have access to #self, which refers to the class itself, and class methods are inheritable and overridable, unlike static methods. Since constructors are just class methods, this means you can inherit and override and extend constructors, without being forced to redeclare them in all subclasses like you would in Java or C#.

Since classes are just ordinary objects, you can put them into variables and pass them around, and call methods on them, like any ordinary object.

Consider an array of classes...

class := {Customer. DateTime. String} atRandom.instance := class new.

The variable instance could have one of three possible instances in it. #new is not a static method, or a freaky special nameless method like in Java and C#, it's just an ordinary instance method that all three of those classes inherit from some superclass they have in common.

I haven't tried it, but it looks to me like in Smalltalk you could do this (no casting, but using the "super" keyword).

Correction: Actually, I have done this. I didn't realize it (probably because it's so easy), but it's difficult to get too much done without doing this.

You see a lot of this in Smalltalk class methods:

SomeClass>>createInstance

^super new initialize.

which passes a "new" message to the base class, and sends an initialization message to the resulting object instance.

@Giles:

Re: static methods

As Ramon said there's no such thing as static methods in Smalltalk. I was just talking about what's analogous to a class method in Smalltalk, in C++, Java, C#, etc., which is a static method. For example, if you have, as a class method:

SomeClass>>getInstance

^super new

you can call it like (say, from within a Workspace):

instance := SomeClass getInstance

Like you were saying with Ruby class variables, I didn't have to say "SomeClass new" beforehand. The class object just exists, and I can call a method on it at any time.

This is roughly equivalent to, in C++:

SomeClass.h:

class SomeClass { static SomeClass *GetInstance();};

SomeClass.cpp:

#include "SomeClass.h"

static SomeClass *SomeClass::GetInstance(){ return new SomeClass;}

and is called like:

void main(void){ SomeClass *ptr = SomeClass::GetInstance();}

Obviously with such a simple example, I could've just had a constructor instead, but this is how class factories and singletons (from patterns) are often done in C++, Java, C#, etc.

The reason it's roughly analogous is that static methods are available all the time (while the program is running). You don't need an object instance to call them. As I and Ramon said, the idioms are inconsistent in languages like C++, et al. For static methods you have to use scoping syntax, as I illustrate above, to call them. For instance methods, you call "ptr->SomeMethod()". Instance methods have an implicit "this" pointer/reference, so you can refer to variables and methods inside the object, like "someVariable" and "someMethod()" without saying "this->someVariable" and "this->someMethod()" all the time ("this" is analogous to "self" in Smalltalk).

Static methods don't have an implicit "this" pointer, so you have no access within the method to any of the objects that belong to the same class it belongs to. It only has access to static member variables, or global variables. This is rather like Smalltalk class objects as well. They only have access to their own class variables, and class variables/methods of other classes. They have no access to instance variables/methods, unless an object instance is given to them.

Classes in C++, et al. exist only as "object templates" within the compiler's symbol table. When they're instantiated into objects, it's like they're "stamped" into the executable image, as well as the memory, using the "template". There's no access to the classes themselves in C++.

Within Java and .Net it is possible to get access to some metadata for classes. It's even possible to dynamically construct classes, and generate object instances from them, but it's pretty complicated. It's not nearly as easy as it is in Smalltalk, and I assume, Ruby.

I took a look at it briefly last summer. It came highly recommended from an old friend of mine. He had been working with Ruby for about 3 years (not Rails, though). He swore by it. I found a couple of Ruby demos/tutorials which were impressive. The syntax reminded me a bit of Smalltalk. I had heard from this same friend that the modern Smalltalk was called "Squeak" and was open source. So after puttering with Ruby for a bit I took a look at Squeak and was blown away by some of the things it could do. I've been just kind of stuck here ever since (pleasantly :) ), but the idea of returning to Ruby for the purposes of actually making some money (the first duty of a programmer is to eat) has crossed my mind. We'll see.