[self-interest] Object Names (was: Object identity)
Jecel Assumpcao Jr
jecel at lsi.usp.br
Tue Jun 8 18:13:40 UTC 1999
"Why doesn't every object get a name when it is created?"
This is a very good question, and Douglas has already given a nice
answer to it. I would like to go a little deeper into the philosophy
behind Self: "The Power of Simplicity".
Why do we need names at all? I'll come back to this later, but for
now we can say that while object IDs are sufficient for the system
to access an object, a name makes it easy for us to "mention" an
object in the program text.
Here are the ways Smalltalk uses object names:
1) instance methods - stored in a dictionary in the object's class
2) class methods - stored in a dictionary in the object's metaclass
3) inherited methods - stored in some dictionary in a superclass
(not necessarily direct) of the object's class. Same thing for
4) instance variables - the values are stored in the object itself,
but the names are stored inside the object's class
5) class variables - can be stored as a dictionary in the object's
6) class instance variables - since classes are themselves objects,
they can have instance variables as well
7) pool variables - information that is shared between several classes
(a table of special character names, for example) can be stored in
a dictionary which is then listed in the object's class
8) global variables - a special dictionary called Smalltalk has
variables that are "seen" everywhere in the whole system
9) temporary variables - these have their values stored in the
context objects at runtime, while their names are only stored
in the method's source
10) arguments - just like temporaries, but are read-only and their
values are loaded from the top of the calling method's stack
Some Smalltalks introduce even more complications, but this is
enough for the general idea. The spirit of Self is to reduce this
to just two ideas:
A) slots - associate a name with a value in the object itself, so
that each object is like a Smalltalk dictionary
B) inheritance - some slots are tagged as special, the parent
slots. Objects referenced through these slots become logically
a part of the referring object, so that their slots are inherited
So we have the "simplicity" part. Is the "power" part true? That is,
can this do everything that the Smalltalk system did? Yes:
1) instance methods - methods in the object's slots or one of its
parents can handle this function quite well.
2) class methods - methods in a parent object (called traits) can
more or less replace these.
3) inherited methods - methods in slots of more remote ancestors
of an object can neatly replace these
4) instance variables - the object's slots have the same function. The
only detail is that a Smalltalk object's instance variables include
all those defined in its superclasses automatically. The Self
programming environment can simulate this by tagging some slots
as being "copy down" and automatically propagating any programming
changes to them to objects marked as "copy down children" of that
5) class variables - data slots in the traits object
6) class instance variables - these too can be emulated with data
slots in the traits object
7) pool variables - data slots in an object that is used as the
parent of several traits objects. This object normally doesn't
have any parents itself, and is used in a "mixins" style
8) global variables - nearly all objects inherit from the lobby
which has a parent called "globals". Anything available there
is effectively a global variable
9) temporary variables - the data slots in the context objects
(Self calls them activations - they are clones of the method
objects) can be accessed by sending messages to "self" is the
implicit receiver syntax
10) arguments - just like temporaries
So slots and parents are indeed as powerful as the Smalltalk scheme.
Not only can they emulate the ideas in Smalltalk, but they can also
be combined in novel ways. Look at this object:
( | parent* = traits clonable.
clear = (mySet: set copyRemoveAll).
remember: obj = (mySet add: obj).
check: obj = (mySet includes: obj)
Why can we execute 'set copyRemoveAll' inside the 'clear' method?
By sending the 'set' message to itself, the object looks at its
own slots (which are 'parent', 'mySet', 'clear', 'remember:' and
'check') and doesn't find it, so it looks in the 'traits clonable'
object and doesn't find it there either, then at the 'lobby' and
finally in the 'globals' object which does have a 'set' slot. So
it seems that 'set' is the global name of a certain object and that
we can use it anywhere we want inside Self, right? WRONG!!
If we eliminate the 'parent' slot from the above object, the 'clear'
method will no longer work as it will cause a 'message not found'
error for 'set'. This doesn't seem very usefull, but consider this
( | mySet <- set copyRemoveAll.
clear = (mySet: mySet copyRemoveAll).
remember: obj (mySet add: obj).
check: obj = (mySet includes: obj)
This works again, even though this new object is entirely cut off
from the 'lobby' and 'globals' name space. We are making use of the
fact that "slot initializer expressions" are evaluated in the
context of the lobby, and not of the object that is being created.
This allows us to use an expression such as 'set copyRemoveAll' to
give the 'mySet' slot an initial value that is different than 'nil'.
So the parser can use 'set', even though it would be an error for
the object itself to try to do so. This object has no idea of any
global objects, but does know a copy of the 'set' object. And it
can use that to create more copies.
The whole point of this is that each Self object is a unique and
separate name space. We can choose to include a whole bunch of
other objects in this name space, which we will call by convention
"global object", if this is convenient. Or we can choose not to
do so. We have total control over this, unlike in Smalltalk in most
Now I'll go back to the question of why we need names at all. It
is interesting that you have to give classes names in Smalltalk, but
not instances. Compare the typical introductory example in
box new named "joe"!
joe turn 30!
joe grow -15!
box new named "jill"!
jill turn -10!
1 to 10 do (joe turn 20)!
forever do (joe turn 11. jill turn -13)
with the same thing in Smalltalk-80:
Smalltalk at: #Joe put: box new.
Joe turn: 30.
Joe grow: -15.
Smalltalk at: #Jill put: box new.
Jill turn: -10.
(1 to: 10) do: [Joe turn: 20].
[true] whileTrue: [Joe turn: 11. Jill turn: -13].
In both versions of this example we have two boxes, so it is obvious
we need different names for them so we don't get messages intended
for one mixed up with those inteded for the other. What I wanted
to show is how in the interest of making languages "simpler" we have
made naming objects more complex, and so harder for beginners to
grasp. We need to work on this.
On the other hand, a big use for names was saving objects in a textual
form so they could be moved to another Snapshot. As things become
more graphical, you can refer to an object by its position (click
on the topmost object, and in its evaluator send the following
I hope this didn't make thing more confusing than they already were :-)
eGroups.com home: http://www.egroups.com/group/self-interest
http://www.egroups.com - Simplifying group communications
More information about the Self-interest