You've decided to expand your horizons. You've been programming exclusively in Java (or C++, or Perl, or Ruby) for a while now. You're happy and productive, but you have this nagging feeling that you're solving problems by rote rather than thinking as creatively as you once did. Learning a new language, especially one that forces you to re-examine some of your notions about software development, may be just the ticket.
Smalltalk, an influential language with deep roots in software development practice, offers an outstanding opportunity for stretching your mind and exercising your development muscles. The only drawback is that once you try it, you may never go back. This article will help you get started.
Researchers at Xerox's legendary Palo Alto Research Center developed Smalltalk more than 30 years ago. It took the shape that it largely still holds today with the release of Smalltalk-80 in 1980. Xerox provided the ST-80 release to a small number of companies at the time, including Apple Computer. Years later, when Alan Kay and Dan Ingalls (the architect and lead developer of Smalltalk at PARC) were at Apple, they wanted a system with which they could develop multimedia educational software. They took the original Smalltalk-80 implementation at Apple and developed a modern implementation called Squeak. The paper "Back to the Future: The Story of Squeak, A Practical Smalltalk Written in Itself" details the whole story of Squeak's development.
Despite (or perhaps, because of) its long history, Smalltalk remains extremely influential and relevant today. The authors of the seminal Design Patterns were steeped in Smalltalk development practice. The originators of eXtreme Programming developed their teachings during large-scale Smalltalk development projects. Seaside, a continuation-based web development framework for Smalltalk, has received some positive attention, as has DabbleDB a Web 2.0 application written in it. On the desktop, the OpenCroquet project provides an immersive, peer-to-peer collaboration architecture/application framework.
You can obtain Squeak for a variety of platforms at the Squeak website. Linux users can often find Squeak in the repositories for their distributions. For example, Ubuntu users can simply
apt-get install squeak as long as the Ubuntu "Multiverse" is in /etc/apt/sources.lst.
Before I talk about at the mechanics of using Squeak, I need to explain the Smalltalk language itself. In Smalltalk, everything is an object. Nearly every action in Smalltalk involves sending a message to an object. As may make sense for a language that is heavily message-oriented, there are plenty of ways to send a message.
Unary messages have no arguments. For example, if
a is an array:
returns an array arranged in the reverse order of the receiver,
Arithmetic operators are really binary messages.
+ is a typical example:
5 + 4.
This sends the
+ message to the
5 object (which is an instance of the
SmallInteger class). The implementation of the
+ message receives the
4 as its only argument and returns the sum of the two numbers.
Finally there are keyword messages. These accept arguments that are separated and identified by keywords. For example, you can call the
at:put: method of a
Dictionary object like this:
d at: 'Fred' put: 7.
This inserts the value 7 in the
d at key
Fred. Careful selection of the keywords used to identify a particular method can greatly enhance code readability.
Precedence is straightforward: evaluation occurs left to right. Unary messages get sent before binary messages, and binary messages come before keyword messages. You can use parentheses to alter precedence.
There's a subtle implication of these rules: addition is simply a message send represented by sending the
+ binary message to objects like
SmallInteger instances that understand it. Given these precedence rules, the Smalltalk statement
2 + 3 * 3.
evaluates to 15, not 11, because of the left-to-right evaluation of message sends.
The assignment message is
:=. The statement terminator is a period. You can send successive messages to the same object by separating them with a semicolon. The pipe character (
|) marks the declaration of local variables in a method. The caret (
^) returns a value from a method, and double quotes enclose comments.
Here's an example that ties some of this together:
myInitDayMap "Class side method to initialize a dayname->ordinal day number mapping singleton" | map | map := Dictionary new. map at: 'Sunday' put 0; at: 'Monday' put 1; at: 'Tuesday' put 2; at: 'Wednesday' put 3; at: 'Thursday' put 4; at: 'Friday' put 5; at: 'Saturday' put 6. ^map.
Naming this method with a
my prefix follows the convention that such methods are for the private use of an object, though the language does not enforce this. The initial comment describes this method as a "class-side" method, which is the Smalltalk way of describing what might be more familiar to you as a static method. This code declares
map, a variable local to the scope of the method. It initializes the variable as a
Dictionary object and sends it a series of
at:put: messages. Finally, the method returns the new, initialized dictionary.
That's about it for Smalltalk syntax. You may have noticed that there has been no discussion of control keywords such as
while. In Smalltalk, there are none. Rather, the class library implements classes that understand messages that serve roughly the same purpose. For example, to assign the value 7 to the variable
a is less than the current value of
(a < b) ifTrue: [ a := 7 ].
< operator is a unary message sent to the
a object; it returns an instance of
True (subclasses of
Boolean) based on evaluating
Boolean instance understands the
ifTrue: message, which takes a block closure as its argument (the code surrounded by
). A block closure is a fragment of Smalltalk that you can store and execute at a later time. An instance of
False ignores the block sent with
ifTrue:, an instance of
True executes it.
While other languages lose some utility when divorced from their associated libraries, portions of Smalltalk simply evaporate. Traditionally, however, Smalltalk marries its class library, the language processors, and the development tools into a single entity called the image. The Squeak virtual machine is a platform-independent byte-code interpreter not unlike the VMs that come with more recently developed languages such as Java. The image, however, doesn't have an analogue with other languages--it is a platform-independent snapshot of the current state of the VM's object memory. The VM along with an image becomes, in effect, a "virtual workstation." Each time you restart a Squeak VM with a given image, your entire context reloads from exactly where you left off. This means that the stack trace you were looking at when you exited the image will still be there when you get back--even if you restart the image on another VM on another architecture.
Typically, you start with the base image as it comes from squeak.org and customize it to suit your goals and objectives. As distributed, the image contains objects implementing fundamental building blocks of the language (
Dictionary, etc.), GUI frameworks, and development tools (which are really just objects that provide UIs for editing, debugging, variable inspections and so on). Ultimately, a Squeak program is a VM and an image customized to provide a particular user experience when started.
Starting Squeak with a particular image differs from platform to platform. After installing Squeak on Ubuntu, simply typing
squeak at a shell prompt (or starting Squeak from the
Development menu) will, if necessary, create a ~/squeak directory and copy in the base image. You'll then be able select any image in that directory to start. When you've done so, you will have the chance to update your image. Saying "yes" will query squeak.org for updates and apply them. Typically with the stable images supplied with various distributions, there are no updates to apply.
Click the background of the main Squeak window. In the resulting menu that appears, select
Save As... and save the image as experiment.image. Now, should you desire, you'll always be able to get back to a fresh image. It's not unusual for a Squeak developer to have numerous images saved. Some will be configured for different purposes (web deployment versus development, for example). Others might be snapshots saved before undertaking a particularly risky bit of refactoring.
Figure 1. The Workspace in Squeak
Now, click the Squeak desktop again. In the world menu, select
Open... and, from the resulting menu, select
Workspace. In a Squeak image, the Workspace (Figure 1) serves roughly the same purpose as a command prompt in other development environments. It is not, however, a Read-Eval-Print-Loop such as found with Lisp or Python. Rather, it is simply an editable scratch space that does some variable management for you. By highlighting text in a work space you can execute it by pressing
Alt-P (for Print It, which evaluates the code and prints the result) or
D (for Do It, which just evaluates the result). Type
1 + 5.
into the workspace, highlight it, and print it. The number 6 will print.
To get a feel for development in an image, create a new class. Imagine that you're writing a program that, among other things, generates
cron entries. For now, your class will concentrate on converting day names into the ordinal numbers expected by
cron and will store the associated command. Type this code into the workspace and do it:
Object subclass: #CronGen instanceVariableNames: 'command' classVariableNames: 'DayMap' poolDictionaries: '' category: 'Example-Category'.
This creates a new class,
CronGen, with a member variable,
command, and a class variable,
DayMap. In Smalltalk, classes are instances of the type
Class. Note in this example how creating a class in a Smalltalk image differs from other languages. In Java or Ruby, you'd enter source code into a text editor, save it, and then let the language system process it. The class would be created in the current runtime instance. In contrast, the Smalltalk example sends a message to an already existing instance of the type
Object. That message returns another instance of the type
Class, called CronGen in this case. Unless it is removed, this instance,
CronGen, will continue to exist in the image it was created in. Smalltalkers refer to this as working with "live" objects.
This may seem like a distinction without a difference at first, but it has a rather profound effect on the language. Enter this code in the workspace and Do it:
Figure 2. The Smalltalk browser
This will open a classic Smalltalk browser (Figure 2). The panels along the top represent class categories, classes, message categories, and messages, respectively. Class categories are a way of grouping related classes into applications or packages. Highlighting a category causes the class panel to only show classes in that category. Highlighting a class lists only the message categories of that class in the message category panel, and so on.
You've probably seen similar tools in other language IDEs. There is an important difference, however. Browsers for other languages must work by parsing the source code for the language in some way, generally manipulating text files. Often, the parser used by the development tools is not the same as the language compiler, with all of the perils of redundancy that implies. Smalltalk development tools, on the other hand, can work by introspection. The browser simply iterates over the image's collection of classes and then introspects the resulting class objects to create the browser's view. In a Smalltalk image, code really is data.
Now use the browser to add a few methods to your
CronGen class. First, middle-click (or, on Windows with a default configuration, right-click) in the class category pane of the browser. Select
find class... and enter "CronGen" in the resulting dialog. Don't worry about method categories for now. Instead, select
-- all -- as the category. The code pane at the bottom of the browser will display a template reminding you of the syntax of the method. Now add accessors for your
command instance variable:
command ^command command: cmdString command := cmdString
Note that the two new messages both have
command as a selector. One is a unary message that simply returns the
command instance variable, while the other is a keyword message that accepts a
cmdString, which it assigns to the
command instance variable. This is a fairly typical syntactical pattern when adding setter/getter methods to a Smalltalk object.
Now create some class-side methods. To do so, select
class at the bottom of the class pane. Add the
myDayMapInit from earlier in this article. Also add an accessor for the map that treats it as a singleton, initializing it if necessary and returning it:
DayMap DayMap ifNil: [ DayMap := self myDayMapInit]. ^DayMap.
The browser isn't the only development tool in the Squeak image. Evaluate this code in a workspace:
CronGen DayMap inspect.
Figure 3. Using the Smalltalk inspector
This will open an inspector on the DayMap singleton (Figure 3). In this case, because the map is a dictionary, a
DictionaryInspector is opened. Inspectors allow you to examine the values associated with a given instance. The dictionary inspector allows you to look at the member variables of the dictionary DayMap and to look at the elements that it contains.
You can see that the tight integration of the language, the class library, and the development tools facilitate a unique development style. To manipulate and inspect the objects of your application, simply send messages to your object instances. Perhaps the most important of these messages is the
halt message. Kent Beck, the father of eXtreme Programming, has reportedly advised "Don't think, just set the halt," allowing you to inspect the live system and fix defects.
Figure 4. The walkback from a
Imagine that your application that uses the
CronGen class is having a problem where some of the
CronGen instances have their
command set to a number rather than a string. Edit the
command: method to add a line to the beginning of the method:
(cmdString isKindOf: Integer) ifTrue: [ self halt. ].
This will trigger a walkback at the point where the
halt is sent (Figure 4). From the walkback, it's possible to drop into a debugger, from which you can inspect the state of your program (Figure 5).
Figure 5. The Smalltalk debugger in action
To see this in action, evaluate this code in the Workspace, one line at a time, after having made the previous change to your image:
cg := CronGen new. cg command 1.
Squeak also provides extensive facilities for navigating pre-existing code. This helps both with understanding the services available in a base image and to come to an understanding of locally developed legacy code. In a running Squeak image, click on the tab on the right border of the main Squeak window. Drag the icon for Method Finder to the desktop.
Figure 6. The Smalltalk method browser
The Method Finder does just that. Type a method-name fragment in the upper right window, and you'll receive a list of candidate method names (Figure 6). Selecting one of those lists the classes that implement the selected method, which you can then browse. The System Browser provides similar services with context menus that allow you to browse senders and implementers of any selected method.
Smalltalk's long history ensures that there is a great quantity of information available to you for further study. The Squeak.org website provides a central point of information about the Squeak implementation of Smalltalk, while Smalltalk.org serves a similar purpose for the language in general. Stéphane Ducasse, a prominent Squeak community member, has provided a great community service by petitioning the rights holders of several out-of-print Smalltalk books to allow him to post online versions of Smalltalk books. The Squeak mailing lists are populated with an extremely friendly, knowledgeable, and helpful community that resemble the language they work with: broad and deep and repaying manyfold the effort you put in.
Keith Fieldhouse is a software developer and writer living in upstate New York with his wife and two young daughters.
Return to ONLamp.com.
Copyright © 2009 O'Reilly Media, Inc.