<<Up     Contents

Self programming language

Self is an object-oriented programming language based on the concept of prototypes. It was used primarily as an experimental test system for language design in the 1990s, and is no longer being actively worked on.

Table of contents

History

Self was designed primarily by David Ungar and Randall Smith in 1986 while working at Xerox PARC. They moved to Stanford University and continued work on the language, building the first working compiler in 1987. At that point focus changed to attempting to bring up an entire system for Self, as opposed to just the language.

The first public release was in 1990, and the next year the team moved to Sun Microsystems where they continued work on the language. Several new releases followed until falling largely dormant in 1995 with the 4.0 version. The latest 4.1 version was released in 2002 and runs on Mac OS X and Solaris.

Self also inspired a number of languages based on its concepts. Most notable, perhaps, was the NewtonScript language for the Apple Newton. Other examples include Io[?], Cel[?] and Agora[?].

The problem

Traditional object languages are based on a deep-rooted duality. Classes define the basic qualities and behaviours of objects, and instances are a particular object based on a class.

For instance you might have a "Vehicle" class that has a "name" and the ability to perform "drive to work" and "deliver construction materials". "Porsche 911" is a particular instance of the class Vehicle with the name set to Porsche 911. In theory you can then send a message to Porche 911, telling it to "deliver construction materials".

This example shows one of the problems with this approach. A Porsche is not capable of delivering construction materials (in any general sense anyway!) but this is something that a vehicle can do. In order to avoid this problem we have to add additional specialization to Vehicle via the creation of subclasses. In this case one could imagine "sports car" and "flatbed truck".

This is a contrived example but it illustrates a very real problem. Unless you can predict with certainty what qualities the objects will have in the distant future, you cannot design your class hierarchy properly. All too often the program will evolve to require additional behaviours, and suddenly the entire system has to be re-designed (or refactored) to break out the objects in a different way.

Experience with early OO languages like Smalltalk showed that this sort of issue came up time and time again. Systems would tend to grow to a point and then become very rigid, as the basic classes deep below the programmers code were simply "wrong". Without some way of easily changing the original class, you may have a serious problem.

Dynamic languages such as Smalltalk allowed for this sort of change via well-known methods in the classes, by changing the class the objects based on it would change their behaviour. But in other languages like C++ there is no such ability, and making such a change can actually break other code, a problem known as the fragile base class problem[?]. In general such changes had to be done very carefully, as other objects based on the same class might be expecting this "wrong" behavior - "wrong" is often dependent on the context.

The solution

The problem here is that there is a duality, classes and instances. Self simply eliminated this duality.

Instead of having an "instance" of an object that is based on some "class", in Self you make a copy of an existing object, and change it. So "Porche 911" would be created by making a copy of an existing "Vehicle" object, and then adding the "drive to work" method. Basic objects that were used primarily to make copies of were known as prototypes.

This may not sound earth shattering, but in fact it greatly simplifies dynamism. If you have to fix a problem in some "base class" because your program has a problem, simply change it and make copies of that new object instead. No other program will see this change. If at some point in the future Porches can "deliver construction materials", big deal, add it.

Better yet, this actually dramatically simplifies the entire OO concept as well. Everything might be an object in traditional system, but there is a very fundamental difference between classes and instances. In Self, there isn't.

The language

Self objects are a collection of "slots". Slots are accessors methods that return values, and placing a colon after the name of a slot sets the value. For instance if you have a slot called "name", "myPerson name" returns the value in name, and "myPerson name:'gizifa'" sets it.

Self, like Smalltalk, uses blocks for flow control and other duties. Blocks are objects containing code, and can be placed in a Self slot just like any other object - a number for instance. The syntax remains the same in either case, and likewise, one can replace either via assignment.

Note that there is no distinction in Self between fields and methods, everything is a slot. Since accessing slots via messages forms the majority of the syntax in Self, many messages are sent to "self", and the "self" can be left off. Thus the name.

Basic syntax

The syntax for talking to slots is Smalltalk-like, with "object_name slot_name" pairs being listed from left to right with the rightmost expression being returned. Lines end on an optional period. For instance:

'Hello, World!' print.

Is the Self version of the hello world program. The ' syntax is a shortcut that makes a copy of the known string object and puts "Hello, World!" into its only slot. Grouping is as in math, using parens. For instance:

'Hello, World!' uppercase print.

Is equivalent to:

('Hello, World!' uppercase) print.

Whereas:

'Hello, World!' (uppercase print).

would attempt to uppercase and print nothing, and thus cause an error.

Making new objects

Consider a slightly more complex example:

labelWidget copy label: 'Hello, World!'.

makes a copy of the "labelWidget" object with the copy message (no shortcut this time), then sends it a message to put "Hello, World" into the slot called "label". Now let's do something with it:

(desktop activeWindow) draw: (labelWidget copy label: 'Hello, World!').

In this case the (desktop activeWindow) is performed first, returning the active window from the list of windows that the desktop object knows about. Next (read inner to outer, left to right) the code we examined earlier returns the labelWidget. Finally the widget is sent into the draw slot of the active window.

Inheritance

One of the side effects of using the clone method is that the new object is created with a slot inside it labeled as the parent. Any slot can be made a parent pointer by adding an asterix as a suffix. As the name implies, this slot is set with a pointer to the object that it was cloned from. In this way Self handles duties that would use inheritance in more traditional languages.

For instance, you might have an object defined called "bank account" that is used in a simple book keeping application. Typically this object would be created with only the methods inside, perhaps "deposit" and "withdraw". This is a prototype.

Making a clone of this object for "Bob's account" will create a new object with a parent slot pointing to this prototype. In this case we have copied the slots including the methods and any data. However a more common solution is to first make a more simple object called a traits object which contains the items that one would normally associate with a class.

In this example the "bank account" object would not have the deposit and withdraw method, but would itself be cloned from an object that did. In this way many copies of the bank account object can be made, but we can still change the behaviour of them all by changing the slots in that root object.

How is this any different than a traditional class? Well consider the meaning of:

myObject parent*: someOtherObject.

This is quite interesting, it changes the "class" of myObject at runtime.

Adding slots

Copying an existing object gets you the same object, but how does Self modify that new object to include new slots? This is very easy, using the <- operator.

In an earlier example we talked about having to refactor a simple class called Vehicle in order to be able to differentiate the behaviour between cars and trucks. In Self one would accomplish this something like this:

Vehicle: (Object copy)
(name <- ' ') Vehicle.
SportsCar: (Vehicle copy).
(driveToWork <- |some code, this is a block|) SportsCar.
Porche911: (SportsCar copy).
Porche911 name:'Bobs Porche'.

The environment

Perhaps one of Self's few problems is that it is based on the same sort of virtual machine system that earlier Smalltalk systems used. That is, programs are not stand-alone entities as they are in languages such as C, but need their entire memory environment in order to run. This requires applications to be shipped in chunks of saved memory known as snapshots which tend to be large and annoying to use.

On the upside the Self environment is very powerful. You can stop programs at any point, change values and code, and continue running where you left off. This sort of "on the fly" development delivers a huge increase in productivity, and its a shame that more systems don't support it.

In addition the environment is tailored to the rapid and continual change of the objects in the system. Refactoring you "class" design is as easy as dragging methods out of the existing ancestors and into new ones. Simple tasks like test methods can be handled by making a copy, dragging the method into the copy, then changing it. Unlike traditional systems, only that object has the new code, and nothing has to be rebuilt in order to test it. If the method works, simply drag it back into the ancestor.

See also:

External Links

wikipedia.org dumped 2003-03-17 with terodump