Smalltalk systems provide an integrated programming environment, which is quite different to almost any other programming system. The difference is that in Smalltalk, both your program and the IDE live in and are part of the same object-world. In Smalltalk, you are not editing a file or code from which an application is built. Instead, you are adding code to the IDE to add your application(s) to it. This does not mean, that it is not possible or common to create a stand alone application for deployment eventually. But during the development, your code and the IDE are one program. Even more striking is the fact, that often Smalltalk programmers have ALL of their code they ever developed in their IDE. This makes reuse much more straight forward.
The Smalltalk IDE is a really integrated environment, not just a bunch of editing tools wrapped into as single frame.
This programming environment allows up-to date access to an object's instance variables,
to a classes definition,
the set of messages which are implemented there (i.e. the protocol),
the source code of those methods, the execution state and much more.
In addition, cross references can be searched (who sends which message, who implements which message, who references a variable etc.), breakpoints can be set and modifications can be made to a class.
Modifications to a class can be made at any time - even while there are active instances of it around; any modification is immediately effective (that does make a difference in programmers productivity !).
Every Smalltalk system also includes a (usually) large collection of useful classes, which provide building blocks and cover a wide area of typical objects/operations. Therefore, a big part of learning how to write Smalltalk programs consists of learning how to use the class library.
Although it is possible to define new classes programatically (i.e. by
sending definition-messages to some object in a workspace or by a program),
classes are normally defined using a browser. This tool provides functions
for most programming tasks and will be your most-used tool when
working with Smalltalk.
Therefore, take some time and play around in a System browser to get a feeling for it.
Also, have a look at the browser's documentation and the new improved browser's documentation.
You can open a System browser either via the launcher's menu, via an inspector's menu, or by sending a "browse"-message to any object.
All classes are organized by various aspects: inheritance hierarchy, package-ownership
and class category.
When opened, the default organization shown in the browser is "by class category".
Class Categories exist only for organization - to allow grouping of classes with similar behavior. For example, all container classes are organized under categories starting with "collection". This makes it easy to find a class.
To define a new class, first add a new class category using the class-category-lists popup menu:
enter the name of the new category:
and click on the 'create' button of the entry box.
The browser will add this new category to its list and select (i.e. highlight) it:
then, select the class-lists new class menu item,
to get a definition template into the codeView:
Fill out the template
and accept it using codeViews popup menu.
This creates your new class and the browser will update its class-list view, showing your new class:
In this example, we defined a
and defined two instance variables named name and
I.e. instances of this class will be able to hold references
to two other objects, and those will be referred to as name and
newmessage to the class:
You can type in the above expression into any code view or workspace, select it and use one of the "DoIt" / "PrintIt" / "InspectIt" menu functions.Person new
You don't have to compile, build or otherwise care for the compilation details. The class is ready to be used at the moment you defined and accepted its definition.
You may wonder, how our new class 'knows' how to respond to
"new"-message - the answer is that all classes
are themself instances of a so called metaclass, which
itself inherits from the
Behavior defines quite a bunch of useful protocol,
one being the
If you are curious, have a look at the
class in your browser and see what other useful protocol is found there.
Personclass do not provide any special protocol - however, a lot is inherited from its superclass:
Therefore, we can already ask instances about their class or class-name,
we can compare instances, ask an instance for a copy of itself
and ask it for its printed string representation.
Try a few expressions (using printIt) in a workspace:
Its printed representation (called printString):
Display it on the Transcript:
Person new printString
another method to display it on the Transcript:
Transcript show:(Person new).
Comparing for identity:
Person new printOn:Transcript.
Asking for a copy:
p1 := Person new.
p2 := Person new.
p1 == p2
Also, have a look at the implementation of some of the above
protocol - especially, have a look into the
p1 := Person new.
p2 := p1 copy.
implementation and understand that there is another (more basic) functionality
involved (to give you a hint: figure out what the role of the
method is ...)
In order to do someting useful with persons, we must tell them how to respond to more specific messages - i.e. we have to add some more protocol. Also, we might want to redefine some of the inherited protocol, for example, we may want to change the way instances represent themself in their printString.
The task of adding protocol (i.e. methods) is much like what we did to define the class - first we have to define a method category by selecting the new method category item from the method-category-list:
enter some descriptive name:
and click the 'create' button.
Then, either ask for a methods template code or simply type in the code and accept it via the codeViews popup menu.
The above method defines how instances of our class will react
setName: keyword message - a message with one
"setName:" method simply
binds the instance variable called name to whatever was
given as the messages argument.
We can check this in a workspace, by evaluating:
Of course, this is pretty useless, since we should have a look into
our person instance, in order to validate that it really did what
it is supposed to ...
Person new setName:'Miller'
... for this, Smalltalk provides a tool called inspector, which allows us to peek into an object.
Try the above expression again, this time using the inspectIt menu function (instead of doIt). This evaluates the expression and opens an inspector on the result:
click on the name item in the list, to see its value:
Also, open an inspector on a fresh person - and see what the instance variables are initialized with (automatically).
In addition to setting a persons name, we would also like to
ask a person for its name.
Similar to the above setter-method, we need a corresponding getter-method, which returns some information about the person object.
i.e. a little up-arrow (^) followed by an expression.^ expression
this evaluates the expression and returns the expressions value from the method - effectively returning that value as the value of the message send (remember: all methods are invoked due to some message-expression evaluation).
Now, that we know that every message-send evaluates to something,
and all messages are actually implemented in some method, you may ask
what value is returned if no explicit return expression is evaluated
in a method (as in our first example method).
The answer is:
if no explicit return value is given in a method, the value returned is the original receiver of the message
This makes sense, because if the returned value is the original object,
it may be the target of another message send, as in:
or, if we know the preceedence rules:
(Person new setName:'Miller') setFirstName:'Fred'
Many methods (and therefore: many messages) return the original
receiver - however, there are some noticable exceptions, which
form a trap to beginners. If you are in doubt, better do not depend
on a methods returnValue and write things down explicit:
Person new setName:'Miller' setFirstName:'Fred'
Although it takes more to type this, it definitely makes things
clearer and less error prone.
newPerson := Person new.
Now, we are ready to add the getter-method for the persons
add the following method to the
As an excercise, add corresponding set- and get- methods for
the firstname instance variable.
"return the persons name"
Whenever an object is to be displayed somewhere (for example: in the Transcript),
the object in question is asked for its printString (using the
Therefore, it would make sense, to redefine this, to get a nicer printout of persons.
However, if you had a look at the implementation of this message, you might have noticed, that the
printStirng implementation simply forwards the
real work to some other method, by sending the
This one is supposed to append a printed representation to some stream, which gets passed as argument.
The reason for this is to avoid the construction of many temporary strings, when objects are printed on the printer or a printed representation is stored into a file; in those cases, the
printOn: message is sent directly, passing
the destination stream as argument.
So, we better redefine the
printOn: method, and a possible implementation
An interesting detail of the above code is that the
firstname instance variables are themself asked to print on the stream,
instead of passing them as argument of a
"send a printed representation of myself
aStream nextPutAll:' firstname='.
This has the advantage that anything can be used as name/firstname; as opposed to
"aStream nextPutAll:name" - which implies
that its argument is some string-like object.
With that addition, our person object prints much nicer;
try it with 'Fred Miller':
p := Person new.
Lets change the
printOn: method, to display persons in another format:
Notice, that when you accept something in the browser,
the code is compiled and installed as specified in the text -
regardless of which method the text originally came from.
"send a printed representation of myself
aStream nextPutAll:'a person named '.
This allows you to take any other methods code as a template to start with.
I.e. you don't have to type in all of the methods text if you find some similar method.
For the curious:
You should notice, that since you changed the definition of person, any existing person object cannot be an instance of this 'new' class, because they do neither know about, nor hold on any of the additional instance variables.
Therefore, any existing instances are now made instances of a so called obsolete person class - the old person class will stay around in the system until no more obsolete person objects are in the system.
As an exersize, add an instance variable called telno and also
add corresponding access protocol.
By now, you may wonder if such mechanic things as defining access methods could not be done automatically - you are right !
The browser's method category menu provides a function called "generate access methods". This defines get- and set-methods for either a single or all instance variables - the methods will be named according to the instance variables name(s).
To try it, select a name from the instance variable list and invoke that menu item:
#new) are defined in its metaclass.
As an example, let us redefine our persons
to automatically initalize the name fields of the newly created person
The interesting thing here is thenew |newPerson| newPerson := super new. newPerson setName:'*** no name yet ***'. ^ newPerson.
"super new". This invokes the original
#newmethod, as inherited from some superclass (i.e. the one we have overwritten).
#newmethod, a simple
"self new"would lead to an endless recursion.
Copyright © 1996 Claus Gittinger Development & Consulting
Copyright © eXept Software AG
<cg at exept.de>